Lua 脚本在 Redis 事务中的应用实践
使用过 Redis 事务的应该清楚,Redis 事务实现是通过打包多条命令,单独的隔离操作,事务中的所有命令都会按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。事务中的命令要么全部被执行,要么全部都不执行(原子操作)。但其中有命令因业务原因执行失败并不会阻断后续命令的执行,且也无法回滚已经执行过的命令。如果想要实现和 MySQL 一样的事务处理可以使用 Lua 脚本来实现,Lua 脚本中可实现简单的逻辑判断,执行中止等操作。
1 初始 Lua 脚本
Lua 是一个小巧的脚本语言,Redis 脚本使用 Lua 解释器来执行脚本。 Reids 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。编写 Lua 脚本就和编写 shell 脚本一样的简单。Lua 语言详细教程参见
示例:
示例代码中 redis.call(), 是 Redis 内置方法,用与执行 redis 命令
if () then end 是 Lua 语言基本分支语法
KEYS 为 Redis 环境执行 Lua 脚本时 Redis Key 参数,如果使用变量入参使用 ARGV 接收
“—”代表单行注释 “—[[ 多行注释 —]]”
2 实践应用
2.1 需求分析
经典案例需求:库存量扣减并检测库存量是否充足。
基础需求分析:商品当前库存量>=扣减数量时,执行扣减。商品当前库存量<扣减数量时,返回库存不足
实现方案分析:
1)MySQL 事务实现:
利用 DB 行级锁,锁定要扣减商品库存量数据,再判断库存量是否充足,充足执行扣减,否则返回库存不足。
执行库存扣减,再判断扣减后结果是否小于 0,小于 0 说明库存不足,事务回滚,否则提交事务。
2)方案优缺点分析:
优点:MySQL 天然支持事务,实现难度低。
缺点:不考虑热点商品场景,当业务量达到一定量级时会达到 MySQL 性能瓶颈,单库无法支持业务时扩展问题成为难点,分表、分库等方案对功能开发、业务运维、数据运维都须要有针对于分表、分库方案所配套的系统或方案。对于系统改造实现难度较高。
Redis Lua 脚本事务实现:将库存扣减判断库存量最小原子操作逻辑编写为 Lua 脚本。
从 DB 中初始化商品库存数量,利用 Redis WATCH 命令。
判断商品库存量是否充足,充足执行扣减,否则返回库存不足。
执行库存扣减,再判断扣减后结果是否小于 0,小于 0 说明库存不足,反向操作增加减少库存量,返回操作结果
方案优缺点分析:
优点:Redis 命令执行单线程特性,无须考虑并发锁竟争所带来的实现复杂度。Redis 天然支持 Lua 脚本,Lua 语言学习难度低,实现与 MySQL 方案难度相当。Redis 同一时间单位支持的并发量比 MySQL 大,执行耗时更小。对于业务量的增长可以扩容 Redis 集群分片。
缺点:暂无
2.2 Redis Lua 脚本事务方案实现
初始化商品库存量:
库存扣减逻辑
初始化 Lua 脚本到 Redis 服务器
脚本执行
3 总结
Redis 在小数据操作并发可达到 10W,针对与业务中对资源强校验且高并发场景下使用 Redis 配合 Lua 脚本完成简单逻辑处理抗并发量是个不错的选择。
注:Lua 脚本逻辑尽量简单,Lua 脚本实用于耗时短且原子操作。耗时长影响 Redis 服务器性能,非原子操作或逻辑复杂会增加于脚本调试与维度难度。理想状态是将业务用 Lua 脚本包装成一个如 Redis 命令一样的操作。
作者:王纯
版权声明: 本文为 InfoQ 作者【京东科技开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/9a226aef8c21c8856be6a3f3a】。文章转载请联系作者。
评论