Redis - 事务
Redis - 事务
Section titled “Redis - 事务”Redis 事务允许你将一组命令作为单个原子操作执行。这确保了没有其他客户端可以中断命令序列的执行。
Redis 事务的特性
Section titled “Redis 事务的特性”- 串行执行: 事务中的所有命令都是串行化并按顺序执行的。一旦触发
EXEC命令,Redis 将不会响应其他任何客户端请求,直到事务完成。 - 原子性(附带条件): 事务是原子性的。这意味着块中的所有命令要么全部执行,要么一个都不执行(在
EXEC命令之前的语法错误情况下)。然而,Redis 不支持回滚。如果一个命令在运行时失败(例如,尝试对非整数值执行INCR),事务将继续,并且所有之前成功的命令将保持提交状态。
核心命令:MULTI 和 EXEC
Section titled “核心命令:MULTI 和 EXEC”事务由 MULTI 命令启动。在 MULTI 之后,客户端可以发送多个命令。这些命令不会立即执行,而是由 Redis 排队。事务最终由 EXEC 命令触发。
示例:原子性递增和设置
Section titled “示例:原子性递增和设置”# 启动事务127.0.0.1:6379> MULTIOK
# 将设置用户名的命令排队。Redis 回复 QUEUED。127.0.0.1:6379> SET user:1:name "Carol"QUEUED
# 将递增用户登录计数的命令排队。127.0.0.1:6379> INCR user:1:loginsQUEUED
# 执行事务。Redis 运行这两个命令并返回它们的结果。127.0.0.1:6379> EXEC1) OK2) (integer) 1条件事务:WATCH
Section titled “条件事务:WATCH”WATCH 提供了一种检查并设置(CAS)机制。如果你 WATCH 一个键,只有当该键自从被监视以来未被其他客户端修改时,事务才会执行。如果键被修改,EXEC 命令将失败,返回 nil 回复,客户端可以重试操作。
示例:乐观锁
Section titled “示例:乐观锁”假设两个客户端试图同时更新产品的库存。
# 初始状态:库存为 10127.0.0.1:6379> SET product:123:inventory 10OK
# --- 客户端 A 开始 ---127.0.0.1:6379> WATCH product:123:inventoryOK127.0.0.1:6379> MULTIOK127.0.0.1:6379> DECRBY product:123:inventory 2QUEUED
# --- 在客户端 A 执行 EXEC 之前,客户端 B 快速购买 1 件商品 ---# --- 客户端 B 的会话(立即执行)---# 127.0.0.1:6379> DECR product:123:inventory# (integer) 9# --- 客户端 B 完成 ---
# --- 现在,客户端 A 尝试执行其事务 ---127.0.0.1:6379> EXEC(nil) # 事务失败,因为被监视的键已被更改。
# 客户端 A 的应用程序逻辑现在应该检测到 nil 回复,# 重新获取库存,并重试整个 WATCH/MULTI/EXEC 序列。所有事务命令
Section titled “所有事务命令”| Command | Description |
|---|---|
| MULTI | 标记事务块的开始。后续命令将被排队。 |
| EXEC | 执行在 MULTI 之后排队的所有命令。 |
| DISCARD | 清除 MULTI 之后排队的所有命令,退出事务。 |
| WATCH key [key …] | 监视给定的键。如果在 EXEC 之前有任何键被修改,事务将中止。 |
| UNWATCH | 清除所有正在被监视的键。 |
常见陷阱和替代方案
Section titled “常见陷阱和替代方案”- 无运行时回滚: 请记住,如果像
INCR这样的命令在EXEC期间对字符串值失败,事务中之前的命令(如SET)不会回滚。你的应用程序必须处理这种可能性。 - 语法错误: 如果命令存在语法错误(例如,将
SET写成SETT),现代 Redis 版本会在EXEC时中止整个事务。 - 替代方案:Lua 脚本: 对于需要服务器端逻辑的复杂原子操作,Redis Lua 脚本通常是
WATCH/MULTI/EXEC更强大、更灵活的替代方案。脚本总是原子性执行的。