redis事务
redis 的事务(transaction)提供了一种将多个命令组合执行的机制,确保多个操作按顺序执行,但不支持回滚(rollback),主要用于简化批量操作和处理并发场景。以下是 redis 事务的核心概念、命令、流程及特性的详细说明:
1、事务相关命令
命令 | 作用 |
---|---|
multi |
开启事务,标记后续命令进入队列(排队阶段),返回 ok 。 |
exec |
执行事务队列中的所有命令,返回各命令的结果列表;若中途监控键被修改,返回 nil 。 |
discard |
放弃事务,清空队列并退出事务,返回 ok 。 |
watch key [key ...] |
监控一个或多个键,若事务执行前(exec 前)键被修改,则事务自动失败(exec 返回 nil )。 |
unwatch |
取消所有监控,用于主动放弃监控机制。 |
2、事务执行流程
2.1.开启事务
127.0.0.1:6379> multi
ok # 进入事务模式,后续命令入队
2.2. 命令入队(queue commands)
- 调用任意命令(如
set
、get
等),命令不会立即执行,而是加入事务队列,返回queued
。 - 语法错误检查:命令入队时会检查语法,若有错误(如命令不存在),
exec
时会拒绝执行整个事务。127.0.0.1:6379> set key1 "value1" # 入队成功,返回 queued queued 127.0.0.1:6379> invalid_command # 语法错误,入队失败,标记事务为 discard (error) err unknown command `invalid_command`
2.3. 执行事务(exec)或放弃事务(discard)
- 正常执行:若队列中所有命令语法正确且监控键未被修改,
exec
按顺序执行所有命令,返回结果列表。127.0.0.1:6379> exec # 执行事务 1) ok # set key1 "value1" 的结果 2) (error) err unknown command `invalid_command` # 语法错误的命令导致执行失败
- 放弃事务:调用
discard
会清空队列,退出事务模式。127.0.0.1:6379> discard ok
3、事务的特性(与传统数据库的对比)
特性 | redis 事务 | 传统数据库事务 |
---|---|---|
原子性(atomicity) | 队列中的命令要么全部执行,要么全部不执行(仅在入队阶段有语法错误时); 执行阶段单命令失败不影响其他命令。 |
整个事务要么全部成功,要么回滚。 |
一致性(consistency) | 保证事务执行前后数据状态合法(依赖应用层逻辑)。 | 事务执行前后数据满足完整性约束。 |
隔离性(isolation) | 命令按入队顺序串行执行,无并发问题(单线程模型)。 | 支持多种隔离级别(如读未提交、可重复读)。 |
持久性(durability) | 取决于持久化策略(rdb/aof),与事务无关。 | 由数据库持久化机制保证(如 wal 日志)。 |
4、乐观锁机制:watch 命令
作用
- 监控键的变化,确保事务执行前键未被其他客户端修改,用于解决并发场景下的竞争条件(race condition)。
流程示例
- 监控键
balance
:127.0.0.1:6379> watch balance # 监控账户余额 ok
- 获取当前余额并开启事务:
127.0.0.1:6379> get balance "100" 127.0.0.1:6379> multi ok
- 模拟扣款操作(入队):
127.0.0.1:6379> decr balance # 扣款 1 元,入队 queued
- 其他客户端修改
balance
(模拟并发修改):127.0.0.1:6379> set balance "90" # 其他操作修改了余额 ok
- 执行事务(失败):
127.0.0.1:6379> exec # 发现 balance 被修改,事务失败,返回 nil (nil)
- 处理失败(重试或放弃):
127.0.0.1:6379> unwatch # 取消监控,重新开始流程 ok
5、事务的局限性
- 不支持回滚:
- 若命令在执行阶段失败(如类型错误),redis 会跳过该命令,继续执行后续命令,不会回滚已执行的命令。
127.0.0.1:6379> multi ok 127.0.0.1:6379> set key "value" # 入队成功 queued 127.0.0.1:6379> lpush key "element" # 类型错误(key 是字符串,非列表) queued 127.0.0.1:6379> exec 1) ok # set 成功 2) (error) wrongtype operation against a key holding the wrong kind of value # lpush 失败
- 若命令在执行阶段失败(如类型错误),redis 会跳过该命令,继续执行后续命令,不会回滚已执行的命令。
- 无法解决所有并发问题:
watch
仅能监控键是否被修改,无法处理复杂的业务逻辑冲突(如金额计算错误),需应用层处理。
- 性能限制:
- 事务本质是批量执行命令,若包含大量操作,可能阻塞 redis 主线程,需合理控制事务规模。
5、适用场景
- 批量操作:简化多个命令的顺序执行(如先查询再更新)。
- 乐观锁场景:通过
watch
防止并发修改(如库存扣减、账户余额调整)。 - 简单事务逻辑:无需回滚的场景(如缓存更新、计数器批量操作)。