Lua 脚本

原子性问题

在使用 Redis 时,我发现了一个棘手的问题。

问题在哪里?

场景:用户快速点击两次点赞

时间线:
T1: 线程 A 检查 → 未点赞
T2: 线程 B 检查 → 未点赞(同时)
T3: 线程 A 添加点赞 → 成功
T4: 线程 B 添加点赞 → 成功(重复!)
T5: 线程 A 计数 +1 → 101
T6: 线程 B 计数 +1 → 102

结果:
- 点赞集合:用户 ID 重复
- 计数:102(应该是 101)
→ 数据不一致!

原因:三个操作不是原子的。

解决方案:Lua 脚本。

Lua 脚本原理

什么是 Lua 脚本?

Lua 脚本:
- Redis 支持的脚本语言
- 在 Redis 服务器端执行
- 保证原子性
- 减少网络往返

为什么能保证原子性?

Redis 执行 Lua 脚本的过程:

1. 客户端发送 Lua 脚本
2. Redis 解析脚本
3. Redis 执行脚本(单线程,不会被打断)
4. 返回结果

关键点:
- Redis 是单线程的
- 执行 Lua 脚本时,其他命令等待
- 整个脚本是一个原子操作

示意图:

不使用 Lua 脚本:
客户端 → Redis: SISMEMBER → 客户端 → Redis: SADD → 客户端 → Redis: INCR
         ↑____________↑____________________↑
         3 次网络往返,可能被其他请求打断

使用 Lua 脚本:
客户端 → Redis: 执行整个脚本 → 返回结果
         ↑________________________↑
         1 次网络往返,原子执行

基础用法

点赞操作

取消点赞

高级用法

批量操作

条件更新

复杂业务逻辑

性能优化

脚本缓存

性能对比

注意事项

脚本超时

脚本调试

实际应用

点赞 + 限流

课后练习

练习 1

Lua 脚本如何保证原子性?

参考答案(3 个标签)
Lua原子性Redis

Redis 单线程模型:

Redis 执行命令的顺序:
1. 客户端 A 发送命令
2. Redis 执行命令
3. 客户端 B 发送命令
4. Redis 执行命令
5. ...

关键点:
- Redis 是单线程的
- 命令按顺序执行
- 一个命令执行期间,其他命令等待

Lua 脚本:
- 整个脚本作为一个命令
- 执行期间不会被其他命令打断
- 保证原子性

练习 2

Lua 脚本如何处理错误?

参考答案(3 个标签)
Lua错误处理调试

错误处理方案:

原子操作要点

  • 这里关注的是把读取、判断和更新合成一个不可打断的状态变化,而不是记住脚本语法。

练习 3

如何优化 Lua 脚本的性能?

参考答案(3 个标签)
Lua性能优化最佳实践

优化技巧:

原子操作要点

  • 这里关注的是把读取、判断和更新合成一个不可打断的状态变化,而不是记住脚本语法。

练习 4

Lua 脚本如何访问全局变量?

参考答案(3 个标签)
Lua全局变量作用域

全局变量限制:

原子操作要点

  • 这里关注的是把读取、判断和更新合成一个不可打断的状态变化,而不是记住脚本语法。

练习 5

如何实现 Lua 脚本的”版本管理”?

参考答案(3 个标签)
Lua版本管理运维

方案:脚本版本化

思考题

  1. Lua 脚本能否访问外部资源(如数据库、网络)?

  2. 如何测试 Lua 脚本的正确性?

  3. Lua 脚本和 Redis 事务有什么区别?

💡 提示:这些问题没有标准答案,建议结合实际情况深入思考。