消息被消费、被 ack 之后,Stream 里的内存什么时候还回去?很多人以为 XACK 或 XDEL 之后内存自然就少了。这篇用 10 万条消息量一量:ack、隔一条删一条、连续删除、XTRIM 各自能回收多少,以及为什么「删了一半」不等于「省了一半」。(「回收」在这里指内存回收;PEL 里消息的转移回收见第 44、45 篇。)
阶段 XLEN radix-keys MEMORY USAGE 写入完成 100000 2778 11199430 读取但未 ack 100000 2778 20632009 全部 XACK 100000 2778 11199657
读走不 ack 时,内存从 11.20 MB 涨到 20.63 MB;全部 ack 后回到 11.20 MB(比写入时多 227 字节)。XLEN、radix-tree-keys 都没变:ack 只是清理了 PEL,消息一条也没有被回收。
删除方式 XLEN radix-keys MEMORY USAGE 隔一条删一条 50000 2778 11199353 连续删前一半 50000 1390 5603776 (基线:同样写入 10 万条、什么都没删时 MEMORY USAGE = 11199430)
同样是删掉 5 万条:隔一条删一条,内存 11199353 字节,与基线(11199430)几乎相同,radix 节点数一个没少;连续删前一半,radix 节点数从 2778 降到 1390,内存降到 5603776。原因:一个节点里的消息只要还有一条没被删,这个节点(那段 listpack)就还在;被删的消息只是被打上标记。零星地 XDEL,几乎不省内存。(我只从节点数与内存的变化推断标记机制,没有拆 listpack 验证。)
XTRIM t MINID 1790692921723-0 → 返回 50000;XLEN 50000,radix-keys 1390,MEMORY USAGE 5603073 XTRIM t MAXLEN 0 → 返回 50000;XLEN 0,radix-keys 0,MEMORY USAGE 260
XTRIM MINID 裁掉一半旧数据的结果(5603073 字节,1390 个节点)与「连续删前一半」相同;XTRIM MAXLEN 0 后 XLEN 0、radix 节点 0 个,MEMORY USAGE 只剩 260 字节。日志类的 Stream 通常应该靠 XTRIM 从头部裁剪,而不是零散 XDEL。
length = 50000
entries-added = 100000
max-deleted-entry-id = 1790692920996-736
first-entry = 1790692921002-0删除 5 万条之后:length 是 50000,entries-added 仍是 100000,max-deleted-entry-id 前进到被删的最大 ID。这些计数是 Stream 自己维护的,消费组的 lag 计算依赖它们。