Appearance
第11章:Redis 事务(保证多命令原子性)
11.1 事务的核心概念
Redis事务是一组命令的集合,它可以将多个命令打包,然后一次性、按顺序地执行这些命令。事务的核心特性是原子性:要么所有命令都执行成功,要么所有命令都不执行。
通俗理解:就像银行转账,从A账户扣钱和给B账户加钱这两个操作必须作为一个整体执行,不能只执行其中一个。
11.2 事务的使用命令
核心命令
- MULTI:开启事务,后续命令会被加入事务队列而不立即执行
- EXEC:执行事务队列中的所有命令
- DISCARD:取消事务,清空事务队列
- WATCH:监视一个或多个键,用于实现乐观锁
事务执行流程
- 执行
MULTI开启事务 - 输入多个命令,这些命令会被加入事务队列
- 执行
EXEC执行事务队列中的所有命令,或执行DISCARD取消事务
示例:基本事务操作
bash
# 开启事务
MULTI
# 添加命令到事务队列
SET user:1:name "zhangsan"
SET user:1:age 25
INCR user:1:login_count
# 执行事务
EXEC11.3 事务中的错误处理
Redis事务中的错误分为两种类型:
1. 语法错误(在入队时检测)
当命令存在语法错误时,Redis会拒绝执行整个事务:
bash
MULTI
SET name "zhangsan"
SE # 语法错误
EXEC # 整个事务会被拒绝执行2. 运行时错误(在执行时检测)
当命令语法正确但执行时出错(如对字符串执行自增操作),Redis会继续执行事务中的其他命令:
bash
MULTI
SET name "zhangsan"
INCR name # 运行时错误:对字符串执行自增
SET age 25
EXEC # 第一个和第三个命令会执行成功,第二个命令会失败11.4 乐观锁与WATCH命令
WATCH命令用于实现乐观锁,它可以监视一个或多个键,当事务执行时,如果被监视的键被其他客户端修改,事务会被拒绝执行。
示例:使用WATCH实现乐观锁
bash
# 监视库存键
WATCH product:1001:stock
# 获取当前库存
GET product:1001:stock # 假设返回 10
# 开启事务
MULTI
# 扣减库存
DECR product:1001:stock
# 增加销量
INCR product:1001:sales
# 执行事务
EXEC # 如果期间库存被其他客户端修改,事务会失败11.5 应用场景
- 库存管理:扣减库存 + 增加销量,确保两个操作原子执行
- 用户积分:扣除积分 + 记录消费,确保积分操作的一致性
- 订单处理:创建订单 + 扣减库存,避免超卖
11.6 实操案例:实现库存扣减逻辑
需求分析
实现一个简单的库存扣减系统,需要保证以下操作的原子性:
- 检查库存是否充足
- 扣减库存
- 增加销量
实现步骤
初始化商品数据:
bash# 设置商品库存和销量 SET product:1001:stock 10 SET product:1001:sales 0使用事务实现库存扣减:
bash# 监视库存键 WATCH product:1001:stock # 获取当前库存 local stock = tonumber(redis.call('GET', 'product:1001:stock')) if stock > 0 then # 开启事务 redis.call('MULTI') # 扣减库存 redis.call('DECR', 'product:1001:stock') # 增加销量 redis.call('INCR', 'product:1001:sales') # 执行事务 return redis.call('EXEC') else return {err = '库存不足'} end测试并发场景:
- 打开两个Redis客户端
- 同时执行库存扣减操作
- 验证库存和销量的一致性
11.7 Redis事务与传统数据库事务的区别
| 特性 | Redis事务 | 传统数据库事务 |
|---|---|---|
| 隔离级别 | 无隔离级别,命令按顺序执行 | 支持多种隔离级别 |
| 原子性 | 部分支持(运行时错误不会回滚) | 完全支持 |
| 回滚 | 不支持回滚 | 支持回滚 |
| 锁机制 | 乐观锁(WATCH) | 悲观锁/乐观锁 |
新手易错点
- 误解Redis事务的原子性:运行时错误不会导致事务回滚
- 未使用WATCH命令:在并发场景下可能导致数据不一致
- 事务中使用非事务命令:某些命令(如INFO、CONFIG)在事务中会立即执行
- 长时间占用事务:事务队列会占用内存,应避免长时间开启事务
- 忽略事务执行结果:EXEC返回的数组包含每个命令的执行结果,应检查是否有错误
