Redis 8.8 新特性实操笔记:原生数组、INCREX 限流、XNACK 流处理


先说结论(省得滑到底)

如果你现在正用 Lua 脚本做限流、用 List 模拟滑动窗口、或对流消息的失败处理有实时性要求,这版能直接少写代码。其余是性能和小增强,按需取。生产环境请用 8.8.1——8.8.0 有多个 RESTORE 相关的远程代码执行漏洞,已在 8.8.1 修掉。


背景:从 2009 到 8.8

Redis 从 2009 年发布起就用五大基础类型:String、List、Hash、Set、Sorted Set。这么多年一直有一类需求没被原生覆盖——按数字下标快速存取一串字符串。想要这个能力,以前只能三选一:用 List 模拟、把数据塞进 JSON、或者自己写 Lua。三种都用着别扭。

2025 年的 8.0 是个节点:开源协议改回 AGPLv3,JSON、时序、向量这些模块能力并进核心。之后从 8.0 到 8.8 迭代明显加快——8.2 简化 Stream 多消费组、8.4 让消费者更易读新增与空闲消息、8.6 加幂等生产。8.8 干的事比较实在:把几个最常用的模式做成了原生命令,不用再靠 Lua 和客户端逻辑拼。

下面按新结构、限流、流消息、周边增强、性能与落地几块讲,命令都给可直接在 redis-cli 里敲的版本,文末附一份上线前 checklist


目录

  1. 原生数组 Array
  2. INCREX 限流
  3. XNACK 流消息
  4. 一批小增强(Hash / TS / JSON / ZSet)
  5. 性能与落地

PART 01 原生数组 Array:终于能按下标存了

8.8 新增了 Array 类型(由 @antirez 提交)。它是一个按数字下标存取的字符串集合,动态扩容、稀疏下标友好,并且支持服务端聚合(SUM/MIN/MAX 直接在 Redis 内算,不用把数据拉回客户端)。

常用命令(可直接抄)

# 从下标 0 连续写入
ARSET seats:A1 0 "free" 1 "sold" 2 "lock"
ARGET seats:A1 1            # 读单个下标 → "sold"
ARLEN seats:A1              # 总长度(最大下标+1)
ARCOUNT seats:A1            # 实际有值的槽位数(稀疏友好)
ARINFO seats:A1 FULL        # 看元数据 / slice 统计

滑动窗口:ARRING 比 RPUSH+LTRIM 快一倍

维护「最近 N 条」这类窗口,经典写法是 RPUSH 追加 + LTRIM 截断。8.8 提供了 ARRING 命令,专门把数组当定长环缓冲用,官方在同机基准下吞吐比 RPUSH+LTRIM 高约 2 倍,且和环大小无关。读窗口用 ARLASTITEMS

# 行情滑窗:新报价写入尾部
ARRING prices:600519 1820.50 1821.10 1822.30
# 取最近 300 条展示
ARLASTITEMS prices:600519 300
# 服务端聚合(不拉数据回客户端)
AROPSUM prices:600519       # 求和
ARGREP prices:600519 "182*" # 类 grep 检索

CASE 01|选座 / 货位 / 充电桩:位置本身就是下标

一排座位、一排充电桩、一排快递柜,哪个位置空闲/占用/维修,按下标读写最贴合业务直觉,不必把每个格子拆成独立 key。稀疏数组直接用 ARCOUNT 就能知道实际占用多少,不用遍历。

⚠️ 什么时候别用:Array 不是 List/Hash/ZSet 的替代品。需要 push/pop 或中间插入用 List;需要字段名访问用 Hash。另外官方实测 Array 比 List 多占用约 18% 内存——初版大数组上线前先用 MEMORY USAGE 看一眼。


PART 02 INCREX:限流不用再写 Lua

限流是 Redis 最高频的用途之一。过去要实现「每分钟 100 次」这种窗口计数,基本人手一段 Lua 脚本,或者用 MULTI/EXEC 包 INCR+EXPIRE。8.8 把这事做成了一条原子命令 INCREX:递增、上下界、过期一次搞定,少一次网络往返。

真实语法(注意不是三个位置参数)

INCREX key [BYFLOAT f | BYINT i]
           [LBOUND lo] [UBOUND hi]
           [OVERFLOW WRAP|SAT|FAIL]
           [EX s|PX ms|...|PERSIST] [ENX]

# 每用户每分钟≤100次:上限100,60s窗口
INCREX api:user:42:req BYINT 1 UBOUND 100 EX 60
# → 返回 [当前计数, 实际增量],如 [3,1]放行 / [100,0]拒绝
# 防爆破:错误上限5,到顶截断不报错
INCREX login:fail:u1001 BYINT 1 UBOUND 5 EX 60 SATURATE

几个关键点:返回值同时给出新计数和实际增量,调用方据此判断放行还是拒绝;UBOUND 是上限,超了直接拒绝;SATURATE 则「部分接受」,计数器截到上限、返回实际接受的增量(适合防爆破锁账号);ENX 只在 key 首次创建时设过期,避免每次请求都把窗口 TTL 重置。

迁移对照:原来怎么写

8.8 之前最常见的固定窗口限流用的是这段 Lua(INCR+边界判断+EXPIRE 包在一起保证原子):

-- ratelimit.lua(经典写法)
local cur = redis.call('incr', KEYS[1])
if cur == 1 then
  redis.call('expire', KEYS[1], ARGV[1])
end
if cur > tonumber(ARGV[2]) then return 0 end
return 1

现在上面这一段可以直接删掉,换成一条 INCREX。少维护一份脚本、少一次往返,边界语义还更全(SATURATE、ENX 都是原来要自己写的)。

CASE 02|中后台 API 限流 + 防爆破登录 + 防短信轰炸

三类都是「计数+限时+可能要上限」的窗口场景:API 每用户每分钟 100 次;同一账号连续输错密码 5 次临时锁 60 秒;同一手机号 60 秒内最多发 1 条验证码。用 INCREX 一把梭,返回的实际增量就是「这次放行没」的判断依据。

⚠️ 上手提示:INCREX 的语法在 RC1→GA 之间改过(修复 #15237)。如果你在 RC 阶段就写过相关逻辑,上线前务必按 8.8.1 文档重测一遍,别直接搬 RC 的参数顺序。


PART 03 XNACK:流消息能主动「甩锅」了

Stream 多消费者协作时,消费者 XREADGROUP 读到消息、还没 XACK 就崩溃或处理失败,消息会卡在 PEL(待处理列表) 里。以前只能等别的消费者靠 XAUTOCLAIM/XCLAIM 超时回收——消息要闲置一阵才能被接手,对实时系统不友好。

8.8 加了 XNACK:处理失败的消费者可以显式把这条消息释放回 PEL,立刻可被其他消费者认领重投,不用干等回收周期。它和 XACK 的区别很直接:XACK 是「成功了,删掉」;XNACK 是「我没处理好,放回给别的人」

三种模式对应三种失败

  • SILENT:失败和消息本身无关(服务重启、瞬时错误),递减送达计数,消息排回 PEL 头部重试。
  • FAIL:本消费者没资源处理(CPU/内存),计数不变,优先分给其他消费者。
  • FATAL:毒消息(格式错、恶意),送达计数设为 LLONG_MAX,等人工介入,不再自动重投。
XREADGROUP GROUP order-g c1 COUNT 1 STREAMS orders >
# 处理失败,主动释放这条消息
XNACK orders order-g FAIL IDS 1 123-0
# 其他消费者立刻能认领(仍需主动来 claim)
XAUTOCLAIM orders order-g c2 0 0-0

CASE 03|订单流水线:消费者崩溃,同伴秒级接手

消费者 C1 处理订单时宕机,C2 不必等默认回收周期(可能数分钟),而是 C1 在退出前主动 XNACK FAIL,C2 马上 XAUTOCLAIM 认领。体验差别就是「卡单」和「秒级恢复」。注意:XNACK 之后消息仍在 PEL,需要别的消费者来 claim 才会真正重投,别指望它自动飞走。


PART 04 一批小增强:单独不大,合起来省往返

Hash 字段级通知

7.4 的 Hash 字段过期(HEXPIRE)很实用,但原来只能收到 key 级事件。8.8 加了 subkey 通知——能订阅字段级的过期/删除,事件里带 key、字段名和类型。缓存失效、审计类场景不用再轮询或全 key 监听。

时序多聚合单命令

蜡烛图这类要同时算 MIN/MAX/FIRST/LAST 的图表,以前要发多条命令。8.8 让 TS.RANGE 等一次返回多种聚合

TS.RANGE sensor:temp - + AGGREGATION avg 60000 \
                         AGGREGATION max 60000

JSON 浮点精度可控

JSON.SET 新增 FPHA 参数,可显式指定 BF16/FP16/FP32/FP64。存向量嵌入或传感器读数时,按源数据精度在内存和精度之间权衡,不用一刀切。

ZSet 新增 COUNT 聚合器

ZUNIONZINTER 等集合运算支持 COUNT 聚合:每个元素的 score 反映它出现在几个输入集合里。标签重合度、去重统计这类需求多一个原生玩法:

ZUNION 2 zset1 zset2 AGGREGATE COUNT WITHSCORES

FT.HYBRID KNN 增强

向量混合检索 FT.HYBRID 支持 KNN 子句里每分片更少候选,并新增 FT.PROFILE HYBRID 做性能剖析。大规模向量检索时,这是个「用召回换延迟」的旋钮——先用 FT.PROFILE 量一遍再调生产。


PART 05 性能数字 + 上线前清单

8.8 在多处端到端吞吐做了优化。下面是官方给的一组数字——但先泼盆冷水:这是 Redis 官方在 Intel Sapphire Rapids m7i.metal-24xl 单实例上的基准,你的机器、数据分布、客户端链路不一样,别直接当承诺。真要信,自己跑一遍:

redis-benchmark -t set,get,mset -n 1000000 -q
数据类型/操作(官方基准) 提升幅度
Strings · MGET(IO 线程) 最高 +68%
Streams · XREADGROUP(COUNT 100) 最高 +83%
Sorted Set · ZADD/ZINCRBY 最高 +74%
扫描类 SCAN/HSCAN/SSCAN/ZSCAN 最高 +40%
Hash · HGETALL(1K+ 字段) 最高 +25%
Bitmap 操作(x86) 最高 +28%
HyperLogLog · PFCOUNT(x86) 最高 +18%
持久化与全量复制 最高快 60%

底层还有几处通用优化:x86 版默认开 LTO 链接时优化、部分代码迁到 Rust 降低 FFI 开销、ARM64 专项优化,以及 batched prefetch 批量预取。另外别忘了 Array 比 List 多约 18% 内存——不是纯赚,量级评估时算进去。

上线前 checklist(照着过一遍)

  1. 版本:生产直接上 8.8.1。8.8.0 有多个 RESTORE 相关 RCE(含 RedisBloom/TimeSeries 模块),公网暴露或处理不可信数据的实例尤其要升。
  2. 升级顺序:8.x 之间 RDB/AOF 向后兼容,可滚动重启。但 Array/INCREX/XNACK 是新类型和命令,老版本从节点认不出——按「先升从、再切主、最后升老主」的标准滚动流程走。
  3. 客户端 SDK:go-redis 9.20.0+ 才带 AR*/INCREX/XNACK 封装;老版本用 EVAL/裸命令字符串发也行,但没有类型安全。
  4. INCREX 语法变了:RC1→GA 改过参数顺序,RC 阶段写的脚本上线前重测。
  5. 大数组先看内存:初版 Array 上线前用 MEMORY USAGE 看一眼,稀疏数组靠 ARCOUNT 算实际占用。
  6. 别硬上:Array 不等于 List/Hash;XNACK 之后消息仍在 PEL,记得配 XAUTOCLAIM 兜底,别只靠它。

写在最后:要不要升

判断标准很朴实:你现在是不是正被三件事烦——用 Lua 做限流、用 List 维护滑动窗口、流消息只能等超时回收?是的话,8.8 能直接减代码量,升。不是的话,这版对你就是「性能小幅提升 + 几个用不上的命令」,可以等下次大版本再动。

版本上别纠结:8.8.0 于 2026 年 5 月 GA,8.8.1(2026 年 7 月)修了一批安全漏洞,生产直接用 8.8.1 即可。命令细节以官方文档为准,本文命令已在 8.8.1 上核对过语法。

⚠️ 安全提醒:8.8.0 修复了多起 RESTORE 相关的远程代码执行漏洞(CVE-2026-25243 等,含 RedisBloom/TimeSeries 模块)。涉及公网暴露或处理不可信数据的实例,请优先升级到 8.8.1。


我是 {{作者名}},{{一句话简介,如:后端中间件爱好者,喜欢把新版本特性踩完坑再写清楚}}。如果这篇对你有参考价值,欢迎关注,后续会带来更多 Redis 与中间件的落地笔记。

觉得有用就点个赞、在看、转发,让更多被 Lua 限流折磨过的同学看到。

posted @ 2026-08-01 15:17  peachyy  阅读(4)  评论(0)    收藏  举报