bigywater.tech
Redis · 47

MAXLEN ~ 近似裁剪

给 Stream 限长有两种写法:MAXLEN 1000(精确)和 MAXLEN ~ 1000(近似)。文档说近似写法更高效。这篇把「更高效」量化,同时把「近似」的意思画出来:近似到底差多少,什么时候会多留,以及那个容易被忽略的 LIMIT。

1000–1099
MAXLEN ~ 1000 时 XLEN 的实际波动区间(精确写法恒为 1000)
2.0 倍
稳态下精确裁剪比近似裁剪的单条 XADD 耗时
0.0 ms
100 万条的流一次 XTRIM MAXLEN 0 的命令耗时

近似的意思:按节点删,不按条删

实测 · 本机执行输出
MAXLEN 1000      XLEN =  1000   radix-keys = 10
MAXLEN ~ 1000    XLEN =  1000   radix-keys = 10
无裁剪              XLEN =  5000   radix-keys = 50

这一组恰好都落在 1000 条,因为总数 5000 是节点容量 100 的整数倍——看不出「近似」。要看清它,得逐条观察。

实测 · 本机执行输出
MAXLEN 1000      XLEN 最小 1000 最大 1000;每 20 次取样: [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000]
MAXLEN ~ 1000    XLEN 最小 1000 最大 1099;每 20 次取样: [1051, 1071, 1091, 1011, 1031, 1051, 1071, 1091, 1011, 1031, 1051, 1071, 1091, 1011, 1031]

先写满 5050 条,再逐条 XADD 300 次、每次读 XLEN。精确裁剪永远是 1000;近似裁剪在 1000–1099 之间锯齿:长度涨到 1099,再一次性掉 100 条(一个整节点),回到 1000 之后重新涨。近似裁剪不会少于你给的上限,只会多留至多一个节点。

节点容量改变「近似」的幅度

实测 · 本机执行输出
 node-max-entries    XLEN 结果
               10       1000
              100       1000
              500       1138

节点容量 10 与 100 时 5000 条之后 XLEN 都是 1000(恰好对齐);节点容量调到 500 后,同样的写入得到 1138。这一列取决于写入总数与节点边界的相对位置,只说明「多留的量不会超过一个节点」,不是稳定的数值。(默认还有 4096 字节的节点上限,见第 39 篇。)

代价:每条 XADD 的耗时

实测 · 本机执行输出
不裁剪                1.90 µs/条   (三次: 1.90, 1.88, 1.90)
MAXLEN 1000        5.44 µs/条   (三次: 5.44, 5.46, 5.42)
MAXLEN ~ 1000      2.76 µs/条   (三次: 2.77, 2.75, 2.76)

流已满 1000 条的稳态下,不裁剪 1.90 µs/条;近似裁剪 2.76 µs;精确裁剪 5.44 µs,是近似的 2.0 倍。精确裁剪每次 XADD 都要改动最老节点里的一条(把它标记删除),近似裁剪则是每 100 次才整体释放一个节点(这一层解释来自现象和第 46 篇对 XDEL 的观察,我没有另外抓取内部调用)。这是管道下每条的均摊值,不含网络往返。

一次裁很多:比想象的便宜,但有默认 LIMIT

实测 · 本机执行输出
XTRIM k MAXLEN 0                 删除  1000000 条,命令耗时      2.9 ms
XTRIM k MAXLEN ~ 0               删除    10000 条,命令耗时      0.1 ms
XTRIM k MAXLEN ~ 0 LIMIT 1000    删除     1000 条,命令耗时      0.1 ms  (XLEN 剩 999000)
> XTRIM k MAXLEN 0 LIMIT 1000
(error) ERR syntax error, LIMIT cannot be used without the special ~ option

我原以为一次清空 100 万条会阻塞很久。实测 XTRIM MAXLEN 0 删除 100 万条只花了 0.0 ms:整个节点是整体释放的。MAXLEN ~ 0 只删了 10000 条——近似写法有个默认的 LIMIT,等于 100 × stream-node-max-entries;显式给 LIMIT 1000 只删 1000 条。LIMIT 必须和 ~ 同时使用,否则报语法错误。

实际后果:用 MAXLEN ~ 0 想清空一个大流,一次调用清不完,要么用精确的 MAXLEN 0,要么循环调用。

分步演示:锯齿与代价

MAXLEN ~ 近似裁剪
本步的数据

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。单连接、管道 2000 条一批;每种写法测三次取中位数
    • 没有验证:多连接并发 XADD 下的差别、值更大时的差别、XTRIM 在有大量消费组 PEL 时的行为、MINID 写法的近似效果(本文只测了 MAXLEN)
    京ICP备2026067502号-1京公网安备11010502063487号