Redis 已正式接入 AI。这几天类似的说法在好几个技术群里来回刷,有人以为官方悄悄发布了一个新大模型,有人觉得这就是个标题党。作为一名常年跟缓存、数据中间件、LLM 应用工程化打交道的开发者,我更愿意把这句话理解成一个明确的信号:Redis 已经从单纯的缓存中间件,变成了 AI 应用里绕不开的数据底座。不管你是做 RAG、做 Agent、做模型推理加速,还是单纯想用 AI 辅助自己写 Redis 代码,Redis 和 AI 的结合都不是“未来趋势”,而是今天就能落地的工程事实。这篇文章我不讲 PPT,也不做产品预告,就把我在实际项目里跑通的几类 Redis + AI 场景,以及踩过的坑,一次性说清楚。
我默认读者分两类:一类是后端工程师,Redis 基础没问题,但对 LLM 应用怎么用 Redis 还比较模糊;另一类是算法/全栈同学,平时写 Python 调模型很溜,但对 Redis 的数据结构、部署、分布式锁这些工程细节不熟。文章里的内容两端都能覆盖,你可以按需跳到对应章节。
1. Redis 与 AI 结合的四条真实路径,别只盯着“官方新品”
很长一段时间里,人们提到 Redis 就两个词:缓存、队列。但 LLM 应用大规模落地之后,Redis 的角色被硬生生拓宽了。我自己归纳下来,当前 Redis 和 AI 的真实交集至少有四条路径,每条路径解决的实际问题都不同。
1.1 语义缓存:给大模型接口加一道成本闸门
先说最直接的一条:大模型接口调用贵、延迟高,而且大量用户问题在语义上高度重复。任何一个 AI 产品上线后,你去看日志,问“怎么退款”“退款流程是什么”“如何申请退款”的用户可能是同一批话术。如果每次都打到模型服务,等待 2 到 5 秒太常见了。这时候如果把问题和答案缓存起来,下次直接用缓存结果,成本能降一个数量级。
但传统 Redis String 缓存只能做精确匹配——问题文本完全一样才会命中。用户话术稍微换个说法就 miss 了。于是“语义缓存”出现:先把用户问题做向量化,把向量存进 Redis,用向量相似度判断“这个问题以前是不是问过”,如果相似度足够高,直接把历史答案返回。这要求 Redis 具备向量检索能力,而这不是什么科幻场景,向量索引和相似度检索已经是 Redis 生态里的成熟能力。
1.2 向量检索:RAG 应用的记忆层
RAG(检索增强生成)的核心是“先检索相关资料,再让模型基于资料回答”。检索的数据存在哪里?常见方案是专门的向量数据库。但如果你是中小团队,或者刚开始做 RAG,并不想为了检索单独维护一套集群,Redis 的向量检索能力就非常划算。它能把业务状态、会话数据、向量数据放在同一个底座里,少一个组件,就少一堆运维事。
当然我不是说 Redis 能完全取代专业向量数据库。数据量到千万级、高并发向量检索要求特别极端的场景,专用向量库有它的优势。但对绝大多数刚起步的 AI 应用来说,Redis 的向量能力已经够用,而且数据模型统一带来极大的便利。这个大家要根据自己的规模来判断。
1.3 Agent 运行时状态协调:从缓存到会话中枢
Agent 应用比普通 RAG 复杂得多。一个任务会被拆成多步,每一步可能要调用模型、调用工具、读取中间状态。这些中间状态放哪?数据库太重,本地内存不共享,文件系统不靠谱。Redis 的 Hash、Stream、List、分布式锁、过期时间这些能力,恰好覆盖了 Agent 运行时的全部需求:会话短期记忆、步骤状态、并发锁、速率控制。
这条路径非常有意思,也是我认为“Redis 已正式接入 AI”这句话最有分量的一层:AI 应用的运行时状态机,正在把 Redis 变成会话中枢。
1.4 AI 辅助开发与运维:反向接入
最后一类比较特别——不是 Redis 服务 AI,而是 AI 工具反过来帮开发者更好地用 Redis。实际工作中,用大模型生成 Lua 脚本、排查 Redis 慢查询、设计缓存 Key 结构,已经成了我日常的一部分。AI 写出 Redis 脚本骨架,我负责审边界条件、压测、上线,配合效率确实提升很大。这部分我也放到后面详细展开,并且会带上提示词模板和避坑提醒。
提醒一下:四种路径并不是非此即彼的关系。很多生产系统同时用 Redis 做语义缓存、向量检索、会话状态和限流。关键是你要对 Redis 的数据结构有整体认知,然后才知道每个场景该用哪种结构。
为了帮你快速建立认知,我把 AI 场景里最常用的 Redis 结构列了个对照表,拿走即用:
| Redis 结构 | 典型 AI 应用场景 | 为什么选它 |
|---|---|---|
| String | 缓存模型响应、存储 JSON 结果、限流计数 | 简单直接,天然支持过期时间 |
| Hash | 会话状态、Agent 记忆字段、用户画像 | 可按字段单独读写,方便只更新某个状态变量 |
| List | 消息队列、任务队列、Agent 步骤日志 | 左右端推拉,顺序天然可控 |
| Set / ZSet | 去重、已处理任务记录、按相似度排序 | ZSet 的分值可以作为热度/时间排序依据 |
| Stream | Agent 事件流、日志流、消费者组 | 支持消费组,配合多 Worker 做任务分发 |
| 向量索引 | 语义缓存、RAG 检索、记忆召回 | 把相似度计算和存储放在一个系统里 |
2. 语义缓存落地:一次向量化查询把大模型调用挡住一大半
我在第一家公司做 AI 客服助手时,最痛的就是成本账单。上线当天没做缓存,模型调用量直接冲上去,财务看到账单都来问怎么回事。后来我给 Redis 加了一层语义缓存,效果立竿见影:热问题命中率大概在 25% 到 35%,对用户体验几乎无感知,因为命中后返回时间从两秒降到几十毫秒。这一章我把完整落地思路讲透。
2.1 精确缓存和语义缓存:差的是一个向量
传统的做法很容易理解:把用户问题原文作为 Key,模型答案作为 Value,设置 TTL。
import redis r = redis.Redis(decode_responses=True) answer = r.get(f"cache:{question}") if not answer: answer = call_llm(question) r.setex(f"cache:{question}", 300, answer)问题很明显:用户说“怎么退钱”和“退款流程是什么”,语义几乎一样,但 Key 不一样,缓存命中率为零。于是我们引入 semantic cache:
- 用户问题进来,先调用 Embedding 模型,转成一个向量。比如 1536 维浮点数组。
- 把这个向量写入 Redis 的向量索引。
- 查询时用同样的 Embedding 模型把新问题向量化,然后在 Redis 里做 K 近邻相似度搜索。
- 如果最相似的历史问题距离低于阈值,直接把对应的历史答案返回;否则走大模型,然后入库。
核心差异就在于:精确缓存用字符串匹配,语义缓存用向量相似度匹配。
2.2 用 Redis 做语义缓存的实操代码
先建一个向量索引。假设你用的是 Redis Stack 或支持 Search 模块的 Redis 服务,可以直接用 FT.CREATE 命令:
FT.CREATE llm_cache_idx ON HASH PREFIX 1 llmcache: SCHEMA \ question TEXT \ answer TEXT \ q_vector VECTOR FLAT 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE这条命令的意思是:创建一个索引,名字叫llm_cache_idx,作用于 Hash 类型的数据,Key 前缀是llmcache:。每个 Hash 需要包含question文本、answer文本、q_vector向量三个字段,向量的维度是 1536,距离度量用余弦距离。
写入缓存时,需要把问题、答案、向量一起写进 Hash:
HSET llmcache:1b0f3a question "怎么退款" answer "您可以在订单页..." q_vector "0.0123,0.0456,...,0.0789"查询时用 FT.SEARCH 做 KNN 向量检索:
FT.SEARCH llm_cache_idx "*=>[KNN 3 @q_vector $query_vec AS score]" \ SORTBY score ASC DIALECT 4 \ RETURN 3 question answer score \ PARAMS 2 query_vec "0.0111,0.0322,...,0.0655"得到结果后,取最小的距离分数。如果小于你设定的阈值,就把对应答案返回;否则认为缓存未命中,调用大模型。Python 端配合 redis-py 的搜索模块或者官方 RedisVL 客户端都能实现,代码量很少。
2.3 阈值、TTL 与 Key 设计:三个参数决定成败
这个玩法看似简单,真正决定效果的是三个参数:
- 相似度阈值。余弦距离越小代表越相似。我常用的阈值区间是 0.1 到 0.2,对应很多向量模型的相似度大约是 0.8 到 0.9。注意不同 Embedding 模型的分布不一样,不要照搬别人的阈值,最好在你自己的历史问题上采样测试。
- TTL 时长。推荐“短期严格、长期宽松”:客服类高频问题可以缓存 5 到 15 分钟;知识库类答案可以放到几小时甚至一天。但涉及价格、库存、政策这类高频变化的内容,TTL 要压缩到 1 分钟以内,否则用户会看到过期数据。
- Key 设计。建议用
llmcache:{hash_id}这种带前缀的方式,hash_id 可以用问题的向量哈希或随机 ID,要保证可追踪性,后面查问题、清缓存都方便。
2.4 哪些场景适合语义缓存,哪些别硬上
我在项目里总结出一条经验:语义缓存适合“答案稳定、问题重复、延迟敏感”的场景。比如产品介绍、使用帮助、政策说明、常见故障排查,都是很好的对象。
反过来,下面三种场景不要硬上:
- 答案强个性化:比如根据用户画像生成专属推荐,每个人的结果都不同,缓存命中率低且容易给出陈旧结论。
- 强实时信息:比如“现在有没有促销”“今天天气怎么样”,命中缓存反而是灾难。
- 涉及合规或权限差异:不同用户能看到的内容不同,缓存一条答案给所有人,会造成越权泄露,这种场景慎用。
另外,就算用了语义缓存,也一定要设置合理的最大缓存条数和内存淘汰策略,防止 Embedding 数据把内存堆爆。向量数据的体积比纯文本大得多,后面第 5 章我会专门讲 Big Key 的问题。
3. Agent 记忆与状态协调:Redis 从缓存变成会话中枢
如果说语义缓存还停留在“缓存”的舒适区,那么 Agent 场景才是 Redis 在 AI 时代真正的新身份。多个 Agent、多轮任务、中间状态、并发写入,这些需求天然适合 Redis 的数据结构和原子操作。这一章我按自己在开源 Agent 项目里沉淀的经验来写。
3.1 短期记忆用 Hash + TTL,长期记忆靠向量召回
Agent 的记忆分两层:
短期记忆:当前会话内的上下文。比如用户说“我叫小王”“帮我查明天的航班”,这些信息时效性强,只需要在当前会话内保留。用 Hash 存非常合适:
HSET agent:conversation:abc123 user_name "小王" query_time "2025-..." status "querying_flight" EXPIRE agent:conversation:abc123 1800Hash 的好处是可以单独更新某个字段。比如 Agent 执行到“查询航班”这一步时,只需更新status,不需要把整个会话状态重新写一遍。TTL 设 30 分钟,会话结束或者用户离开后自动清理,不会留下垃圾数据。
长期记忆:跨会话的用户偏好、历史行为。比如用户每次都说“尽量选靠窗座位”,这种信息应该存下来,下次新会话开始时就召回。长期记忆不能用简单 KV 做全文匹配,更适合向量化召回——把用户历史描述向量化后存进 Redis 向量索引,新会话开始时检索最相关的历史记忆,塞进 Prompt。这里的存储结构可以复用上一章的语义缓存技术栈,只是字段从“问题/答案”换成“记忆内容/向量”。
3.2 多 Agent 并发写记忆,分布式锁终于不是面试题了
很多后端同学对 Redis 分布式锁的印象就是“面试会考,工作用不上”。但在 Agent 工程里,分布式锁真真正正成了高频组件。原因很现实:现在主流的 Agent 框架允许多个 Worker 并行处理同一个任务,或者多个线程同时更新同一个用户的记忆。如果不加锁,两个 Agent 同时读到同一个会话状态,然后各自更新,后写的覆盖先写的,会话直接乱掉。
我用的锁实现方式很经典:
import redis, uuid r = redis.Redis(decode_responses=True) def acquire_lock(lock_key: str, token: str, ttl: int = 10) -> bool: return bool(r.set(lock_key, token, nx=True, ex=ttl)) def release_lock(lock_key: str, token: str) -> bool: # 用 Lua 保证比较和删除是原子的 script = """ if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end """ return bool(r.eval(script, 1, lock_key, token))要点就两个:加锁用SET key token NX EX一次性完成,避免分开执行导致锁超时问题;解锁用 Lua 脚本先比对 token 再删除,防止误删别人的锁。Agent 任务执行完后,马上释放锁;极端情况下业务逻辑卡住,锁也会自动过期,不会死锁。
还有一个小技巧:锁的 Key 建议用lock:agent:task:{task_id}这种细粒度命名,只锁住具体任务,不要搞一个全局大锁,否则 Agent 之间互相等待,并行能力直接废掉。
3.3 序列化是 AI 状态存储里最容易被埋的雷
Agent 状态里经常要存结构化对象,比如工具调用参数、中间结果、上下文列表。怎么存?很多人图省事直接 pickle 或者 Java 原生序列化,这是我在项目里踩过的最大坑。
先说结论:跨语言场景一律用 JSON。如果你的 Agent 只有 Python,pickle 确实方便,可一旦你想用 Java/Go 写一个消费者去读状态,甚至只是想用 redis-cli 手动排查数据,pickle 的内容根本没法看。Java 端如果用 JDK 序列化,会出现大量\xAC\xED\x00\x05这种二进制头,跨语言读取更是直接失败。我自己调试线上问题时,打开 Redis 看到一堆这种二进制垃圾,整个人都麻了。
JSON 也有一点需要注意:时间字段必须存成标准 ISO 格式,不要存 Python 的datetime默认字符串,否则不同语言解析会有偏差。另外如果对象特别大,建议拆成 Hash 的多个字段而不是存一个超大 JSON 字符串,这样修改单个字段时不需要全量读写,性能和可维护性都好得多。
3.4 Key 命名空间:从第一天就按 agent 和 session 级规划
Agent 应用通常不是单一 Agent,你可能同时有“客服 Agent”“下单 Agent”“售后 Agent”,每个 Agent 还有多个会话。如果不提前规划 Key 命名,上线一周后 Redis 里的 Key 就会变成一锅粥。
我建议从第一天开始就用三段式命名:
agent:{agent_name}:session:{session_id}:{field}举例:
agent:cs_agent:session:abc123:mem agent:cs_agent:session:abc123:status agent:order_agent:session:def456:order_amount这样至少有三个好处:第一,用 SCAN 就能按前缀把某个 Agent 的会话数据捞出来;第二,排查问题时能一眼看出数据属于哪个业务;第三,后续做数据清理和迁移,可以用通配匹配按 Agent 批量操作。命名规范看起来是小事,但真能帮你省下无数 Debug 时间。
4. 主从部署、Windows 开发与 AI 辅助编程:AI 时代的 Redis 环境
Redis 接入 AI 场景之后,部署和开发环境的要求也变了。以前几个缓存节点挂了影响不大,重启就行;现在 Redis 里很可能存着模型缓存和 Agent 会话状态,挂了直接导致 AI 应用大面积不可用。所以部署层面的稳定性、开发工具链、辅助编程方式,都要跟上。
4.1 为什么我建议直接上 Docker 主从而不是单点 Redis
本地开发环境单点 Redis 没问题,但凡是上了测试环境、准备联调 AI 应用,我强烈建议直接上主从架构。原因很简单:AI 应用是串行链路,模型调用后面跟着 Redis 读写,任何一步 redis 不可用,整个 Agent 任务就断了。
用 Docker Compose 拉一套主从架构非常快。下面这个配置可以直接参考:
services: redis-master: image: redis:7-alpine container_name: redis-master ports: - "6379:6379" command: ["redis-server", "--appendonly", "yes"] redis-replica: image: redis:7-alpine container_name: redis-replica ports: - "6380:6379" command: ["redis-server", "--replicaof", "redis-master", "6379", "--appendonly", "yes"] depends_on: - redis-master主从架构解决的最大问题就是“主节点坏了,只读流量还能顶一会儿”。注意两点:第一,命令里--appendonly yes开启 AOF 持久化,防止重启丢数据;第二,主从复制是异步的,极端情况下主节点宕机时最后一小段数据可能丢失。如果业务要求更高,可以上 Sentinel 做自动故障转移,但那是生产环境的话题,开发环境用主从已经完全够了。
再多说一句:AI 应用里缓存数据可以重建,但 Agent 会话状态和数据一致性比普通缓存敏感。所以我不建议把所有 Redis 数据全部交给 LRU 自动淘汰来管理,至少要给会话状态设置明确的 Key 前缀,并对这部分数据关闭淘汰或单独设置策略,避免内存压力上来时把用户会话给挤掉。
4.2 Windows 开发环境与可视化客户端的正确选择
很多同学在 Windows 上做 AI 开发,然后发现 Redis 官方并不提供 Windows 原生版本。这里的选择很明确:
- 开 Docker Desktop,在容器里跑 Redis,这是最推荐的方式,和 Linux 生产环境一致,避免“本地能跑、上线跑不了”的尴尬。
- 用 WSL2 装 Redis,性能和体验都很好,适合不想太依赖 Docker 的同学。
- 使用 Memurai 这种 Windows 原生 Redis 兼容实现,适合必须原生运行的环境。
可视化工具方面,我个人常用的是 Another Redis Desktop Manager 和 RedisInsight。前者界面清爽、跨平台,适合日常看 Key 和 Debug;后者是官方出品,对 Search 索引、向量数据、慢日志的支持更完整,调试技术问题时我更喜欢用 RedisInsight。
需要特别提醒一点:可视化工具只是用来辅助排查,千万别在 GUI 里直接乱删数据。尤其在 AI 应用里,一个友好但有误导性的 Key 名可能让你误删模型缓存或会话状态,删完后再恢复就不容易了。养成先备份再操作的习惯。
4.3 让 AI 帮你写 Redis 代码的正确姿势
“AI 接入 Redis”还有另一面:让我这种老工程师也能用 AI 快速生成 Redis 相关代码。我日常工作里有三类任务特别适合交给 AI:
- 分布式锁、限流、幂等等通用 Redis 逻辑;
- 缓存 Key 设计、TTL 策略选择;
- 排查一段 Redis Lua 脚本的性能问题和边界 bug。
但 AI 生成代码从来不是零风险。我自己总结了一套提示词模板:
请用 Python redis-py 实现一个带过期时间的分布式锁,要求: 1. 加锁使用 SET key token NX EX seconds,token 用 uuid4; 2. 解锁必须用 Lua 脚本比较 token 后删除,不能直接 DEL; 3. 提供加锁失败后的重试等待逻辑,最多重试 3 次; 4. 给出完整的异常处理和资源释放代码。拿到生成的代码后,我至少做四件事:
- 检查所有 Redis 命令是否用了 KEYS/ARGV 传参,有没有把值拼进命令字符串。
- 检查 TTL 是否合理,锁过期时间应该大于业务最大执行时间。
- 检查解锁逻辑是否原子,能不能防误删。
- 本地压测一遍,看高并发下有没有异常。
AI 辅助编程的本质是提高效率,代码质量仍然由你把关。这一点在 Redis 这种人命关天的数据组件上尤其重要。
5. 模型抖动、大 Key 与序列化:AI 场景最容易炸的 Redis 雷区
做了半年多 Redis + AI 应用,我整理了一份“最容易炸”的问题清单。这些问题在传统 Redis 场景里也有,但 AI 场景会把它放大,不提前预防,线上就要手忙脚乱。
5.1 模型服务超时,会以你意想不到的方式打崩 Redis
最常见的事故路径是这样的:大模型服务偶发超时,应用端设置了重试机制;一旦模型服务抖动,重试流量突然增大;重试请求打到缓存层,大量并发查询 Redis;Redis 本身没事,但缓存未命中的请求同时涌向模型服务,形成雪崩。等模型恢复后,Redis 里的缓存又全部过期,新一轮流量又全部穿透。
这类问题的标准解法是三板斧:
- 给大模型调用加熔断器,连续失败 N 次直接降级,不再继续加重试压力;
- 给语义缓存设置合理的 TTL,并在缓存 Miss 时加 jitter,避免所有请求在同一时刻穿透;
- 对于特别昂贵的模型调用,可以缓存“模型临时不可用”的降级结果几秒钟,防止瞬间冲击。
我们在项目中做过一次压测:没有熔断时,模型返回 500 的瞬间,Redis QPS 飙升到正常值的 8 倍,部分慢查询挡住了后续业务;加上熔断和本地兜底后,整个系统在模型抖动期间依然能保证基础可用。
5.2 向量字段是怎么变成 Big Key 的
这是向量化进入 Redis 后特有的新问题。以 1536 维 float32 向量为例,一个向量大约 6KB。如果你把一组向量塞进一个 Hash,比如一万条历史记忆,这个 Hash 就接近 60MB,妥妥的 Big Key。Redis 对单个 Key 的操作是单线程的,Big Key 会导致所有客户端操作同一把“锁”,延迟瞬间拉高。
应对方案:
- 单条向量单独作为一个 Key,不要把所有向量塞进一个 Hash。
- 如果业务需要批量加载,按业务维度分片,比如按用户 ID 分片,每个分片控制在合理大小。
- 定期用
redis-cli --bigkeys扫描,把新增的大 Key 揪出来。
Big Key 在 AI 场景比传统业务更容易被忽略,因为业务量看起来不大,但向量体积不小,一不留神就把内存和延迟吃掉了。
5.3 序列化兼容:跨语言读缓存前先想清楚
前面提过 Agent 状态序列化的坑,这里再展开说一个典型案例。你有一个 Python 写的 Agent 服务,另一方有一个 Java 写的报表服务,两边共享同一个 Redis。Python 服务把模型结果用 pickle 写进 Redis,Java 服务读取时直接乱码。排查很久才发现是序列化问题。
我在项目上定的规矩很简单:
- 通用数据一律用 JSON 序列化;
- 必须用二进制序列化时,明确约定数据格式和协议版本;
- 禁止在跨服务共享的缓存里使用语言默认原生序列化。
JSON 的体积可能比二进制大一些,但换来的是跨语言可读、可排查、可版本迭代。这个代价值得。
5.4 那些 Redis 面试题,AI 场景下的新问法
这些年 Redis 面试题翻来覆去就是缓存穿透、击穿、雪崩,分布式锁,主从一致性。AI 场景下这些题并没有消失,而是换了个马甲:
- “大模型接口超时重试导致缓存雪崩怎么处理?”——问的是雪崩,但场景更具体。
- “Agent 会话状态用 Redis 的什么结构存?”——问的是哈希和序列化。
- “多 Agent 并发写用户记忆,怎么保证不覆盖?”——问的是分布式锁和乐观锁。
- “语义缓存的 TTL 应该设多少?”——问的是业务理解与缓存策略权衡。
如果你是为了面试准备,别只顾着背原题,多想想这些应用场景。能结合 AI 场景把原理讲清楚,比单纯背出“Redis 单线程为什么快”更能体现水平。
6. 用 AI 生成 Lua 脚本,把 Redis 的原子能力变成日常操作
最后一章,我想认真聊聊 AI 写 Redis Lua 脚本这个实操方向。这既是我体验最深的部分,也是我认为“Redis 已正式接入 AI”落实到个人工作流之后最明显的收益点。
6.1 Lua 脚本和 AI 编程为什么合拍
Redis 的 Lua 脚本有一个核心价值:原子性。脚本执行期间不会插入其他命令,多个命令要么全部成功要么全部失败。在 AI 应用里,限流、计数、状态更新、锁释放,往往都需要这种“要么不做要么做完整”的原子语义。但 Lua 脚本写起来有不少细节:KEYS 和 ARGV 的传参方式、返回值类型、Redis 命令在 Lua 里的调用语法。这些细节对很多人来说是有一定学习成本的,而 AI 大模型最擅长的恰恰就是生成这种短小、模式化、规则明确的代码片段。
我在项目里的习惯是:让 AI 先出一版完整脚本,再请它用通俗语言解释逻辑,最后我拿几个关键场景做测试。遇到 bug 时,直接让 AI 根据报错信息修订。这套流程已经帮我处理了限流、幂等、防重等多个需求。
6.2 一个令牌桶限流脚本的生成与审查过程
先看一个典型的 AI 生成的令牌桶限流 Lua 脚本。这个脚本是简化版,重点演示思路:
-- KEYS[1]: 限流 key -- ARGV[1]: 桶容量 -- ARGV[2]: 每秒恢复速率 -- ARGV[3]: 当前毫秒时间戳 local key = KEYS[1] local capacity = tonumber(ARGV[1]) local rate = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local token_key = key .. ":tokens" local ts_key = key .. ":ts" local tokens = tonumber(redis.call("GET", token_key)) if not tokens then tokens = capacity end local last = tonumber(redis.call("GET", ts_key)) if not last then last = now end -- 计算从上次到现在恢复的令牌数 local delta = math.max(0, now - last) / 1000 * rate tokens = math.min(capacity, tokens + delta) -- 无论是否通过,都要更新时间戳,避免下次重复计算 redis.call("SET", ts_key, now, "EX", 3600) if tokens < 1 then redis.call("SET", token_key, tokens, "EX", 3600) return 0 end tokens = tokens - 1 redis.call("SET", token_key, tokens, "EX", 3600) return 1这个脚本关键点有三个:一是令牌补充计算必须用毫秒差乘以速率;二是无论请求是否被限流,都要更新时间戳,否则下一次请求会把这段时间恢复的令牌又计算一遍,导致限流失效;三是所有内部 Key 都设置 EX,防止长时间不使用的数据占内存。
拿到 AI 生成的脚本后,我一般会在 redis-cli 里先用 EVAL 跑几个边界用例:第一次调用、连续调用超过容量、停顿一秒后恢复、并发调用。确认这四个场景的结果都符合预期,才会集成到代码里。
6.3 AI 写 Lua 的常见翻车点,以及我的使用习惯
AI 生成 Lua 脚本虽然快,翻车点也很集中:
- 参数拼接:有些生成结果会在脚本内部用拼接字符串的方式生成 Key 或命令参数,这是大忌,会导致注入风险和性能问题。必须改成 KEYS/ARGV 传参。
- 返回值类型混淆:Redis 的 RESP 协议在不同版本下对空值、数组、数字的处理有细微差异。AI 有时会把空值判断写成
nil和false混淆。 - 忽略脚本传播方式:Redis 主从架构下,脚本可能在副本上重放执行。如果脚本依赖时间或随机数,可能导致主从数据不一致。生产环境要特别注意,必要时改用纯命令组合或 Redis 7 的 Functions 特性来管理。
我自己现在写 Redis 复杂逻辑的习惯是:先是让 AI 生成一版骨架,然后重点审查边界条件和异常分支,再用真实流量做一轮压测。这大概就是“Redis 已正式接入 AI”在我这里的真实含义——不是让 Redis 变成 AI,而是让 AI 变成 Redis 的上层大脑,Redis 继续干它最擅长的事:把状态、缓存和并发协调稳到毫秒级。
如果你正在做 AI 应用,别只盯着模型本身,把 Redis 这层数据底座用好,你会少踩很多坑。