Redis 进阶 03:Pipeline、事务与 Lua
实验基线:Redis 7.4.11,先完成环境准备。除标注为宿主机终端的代码外,Redis 命令在该课指定的 redis-cli 中执行;不复制输出结果作为命令。
学习目标#
分清 Pipeline、事务与 Lua 的能力边界,复现 WATCH 冲突,原子地建立有有效期的计数器。
原理:吞吐与正确性分别设计#
Pipeline 把多条命令批量发送,减少网络往返;它不是事务,客户端要检查每条响应。MULTI/EXEC 将一组命令排队执行,执行阶段不会被其他客户端命令插入,但 Redis 事务没有数据库式回滚。运行时某条命令出错,其他排队命令仍可能成功。
WATCH 是乐观并发控制。如果被监视的键在 EXEC 前被其他客户端修改或过期,EXEC 返回空响应,需要重新读取并有界重试。不要无限重试。
Lua 在 Redis 中原子执行,但会阻塞其他命令,因此脚本应短小、避免无界循环,且显式传入所有键。Cluster 中脚本涉及的键必须在同一槽,下一课学习该约束。
实验:事务执行#
在主实验容器 redis-learning 的 redis-cli 中:
UNLINK learn:atomic:count
MULTI
INCR learn:atomic:count
EXPIRE learn:atomic:count 60
EXEC
GET learn:atomic:count
MULTI 返回 OK,中间两条命令返回 QUEUED;EXEC 的两条响应都是 1,GET 返回 1。计数与过期设置在执行阶段连续完成。
实验:WATCH 冲突#
打开两个宿主机终端,分别执行 docker exec -it redis-learning redis-cli。
终端 A:
SET learn:atomic:balance 100
WATCH learn:atomic:balance
GET learn:atomic:balance
终端 B:
INCRBY learn:atomic:balance 10
回到 A:
MULTI
SET learn:atomic:balance 90
EXEC
GET learn:atomic:balance
EXEC 返回 nil,最后余额为 110。A 的基于旧值计算的更新没有提交。重新读取后可再次 WATCH,并在有限次数内重试;连接断开后不要继续假设原监视状态有效。
实验:Lua 原子计数与首次设置 TTL#
先清理,再在 redis-cli 输入一行脚本:
UNLINK learn:atomic:window
EVAL "local n=redis.call('INCR',KEYS[1]); if n==1 then redis.call('EXPIRE',KEYS[1],ARGV[1]); end; return n" 1 learn:atomic:window 60
EVAL "local n=redis.call('INCR',KEYS[1]); if n==1 then redis.call('EXPIRE',KEYS[1],ARGV[1]); end; return n" 1 learn:atomic:window 60
TTL learn:atomic:window
预期 1、2,剩余 TTL 不大于 60,第二次不会刷新到期时间。numkeys=1 表示只有一个键,60 属于 ARGV。这是从首次请求开始的 60 秒窗口,不是按整分钟对齐的窗口。
Pipeline 如何练习#
使用官方客户端的 Pipeline API 同时发送 100 次 INCR,并逐条检查结果,与串行模式测量总耗时。redis-cli 交互逐行输入不构成对网络 Pipeline 的有效性能比较。不要用 MULTI 代替 Pipeline 然后宣称测到了同一种行为。
常见错误#
- Redis 事务某个运行时错误后希望全部回滚:无此保证。
- Lua 内用动态拼接产生未在 KEYS 声明的键:不利于 Cluster 正确路由。
- 脚本成功后数据库写入失败:Redis 原子性不跨越数据库。
- Lua 非原子地调用外部 HTTP:Redis 脚本不能这样执行网络请求。
练习与参考答案#
把窗口计数换成每次增加 2,需要同时改变“首次创建”判断,不能只把 INCR 换成 INCRBY:
UNLINK learn:atomic:step
EVAL "local exists=redis.call('EXISTS',KEYS[1]); local n=redis.call('INCRBY',KEYS[1],2); if exists==0 then redis.call('EXPIRE',KEYS[1],ARGV[1]); end; return n" 1 learn:atomic:step 60
预期返回 2,TTL 存在。脚本针对受控整数键,不支持把任意已有类型自动修复。
清理#
UNWATCH
UNLINK learn:atomic:count learn:atomic:balance learn:atomic:window learn:atomic:step
官方参考#
学习导航#
上一课:Redis 进阶 02:RDB、AOF 与备份恢复 · 学习目录 · 下一课:Redis 进阶 04:缓存故障与数据一致性







