XACK 是消费者对 Redis 说「这条我处理完了」。它做的事比看上去少:只是把 ID 从组的 PEL 里拿掉,并不删消息。这篇验证它的返回值语义、按组隔离的规则,以及 Redis 8.x 新增的 XACKDEL 如何把「确认」和「删除」合成一步。
> XPENDING s g
1) (integer) 6
2) "1-0"
3) "6-0"
4) 1) 1) "a"
2) "6"
> XACK s g 1-0 2-0
(integer) 2
> XACK s g 1-0
(integer) 0
> XACK s g 99-0
(integer) 0
> XACK s g 3-0 3-0 4-0
(integer) 2
> XPENDING s g
1) (integer) 2
2) "5-0"
3) "6-0"
4) 1) 1) "a"
2) "2"返回的是「这次从 PEL 里真的移除了几条」:第一次 ack 1-0、2-0 返回 2;重复 ack 1-0 返回 0;ack 一个从未投递的 99-0 也是 0;3-0 3-0 4-0 里重复的 3-0 只算一次,返回 2。所以返回 0 不代表出错,只代表「没有东西可移除」。
> XLEN s
(integer) 6
> XRANGE s 1-0 1-0
1) 1) "1-0"
2) 1) "job"
2) "j1"ack 了 4 条之后,XLEN 仍是 6,XRANGE 还能读到 1-0。确认只影响 PEL;消息本身要用 XDEL / XTRIM 处理(见第 46 篇的内存实测)。
> XGROUP CREATE s g2 0
"OK"
> XPENDING s g2
1) (integer) 2
2) "1-0"
3) "2-0"
4) 1) 1) "a"
2) "2"
> XACK s g 1-0
(integer) 0
> XACK s g2 1-0
(integer) 1
> XPENDING s g2
1) (integer) 1
2) "2-0"
3) "2-0"
4) 1) 1) "a"
2) "1"
(XACK 的参数是 key、组、ID,没有消费者名:谁 ack 都行,只要组对)1-0 同时在组 g 和 g2 的 PEL 里各有一份记录:在 g 里 ack(g 里其实已 ack 过,返回 0)不影响 g2;在 g2 里 ack 才让 g2 的 PEL 少一条。XACK 的参数只有 key、组和 ID,没有消费者名——组内任何连接只要知道 ID 都能 ack,Redis 不校验是谁读的。
> XREADGROUP GROUP g a STREAMS s 0
1) 1) "s"
2) 1) 1) "5-0"
2) 1) "job"
2) "j5"
2) 1) "6-0"
2) 1) "job"
2) "j6"XREADGROUP … 0 只返回还在 PEL 里的消息:已 ack 的 1–4 号不再出现,只剩 5-0、6-0。
> XACKDEL t g ACKED IDS 1 1-0
1) (integer) 2
> XACKDEL t g KEEPREF IDS 1 2-0
1) (integer) 1
> XACKDEL t g DELREF IDS 1 3-0
1) (integer) 1
> XLEN t
(integer) 3
> XPENDING t g
1) (integer) 2
2) "4-0"
3) "5-0"
4) 1) 1) "a"
2) "2"
> XPENDING t h - + 10
1) 1) "1-0"
2) "a"
3) (integer) 1
4) (integer) 1
2) 1) "2-0"
2) "a"
3) (integer) 1
4) (integer) 1
3) 1) "4-0"
2) "a"
3) (integer) 1
4) (integer) 1
4) 1) "5-0"
2) "a"
3) (integer) 1
4) (integer) 1两个组 g 和 h 都读了 5 条消息(t 流)。在 g 里对三个 ID 分别用三种策略:
返回值中 2 的含义(「已确认但未删除」)是我根据 XLEN 的变化推断的,官方文档的表述我没有逐字核对。