Redis 事务
Redis 的事务不同于关系型数据库, 事务模型很不严格, 我们对比着 MySQL
来看
基本用法
命令分为:
multi
事务的开始 (类似 MySQL 的begin
)exec
执行事务 (类似 MySQL 的commit
)discard
丢弃事务不执行 (类似 MySQL 的rollback
)
// exec
127.0.0.1:6379> get jw
(nil)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set jw 666
QUEUED
127.0.0.1:6379> exec
1) OK
127.0.0.1:6379> get jw
"666"
这里要注意, 如果 exec
返回非 OK
(如nil
)则表示执行失败
// discard
127.0.0.1:6379> get jw
(nil)
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set jw 888
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get jw
(nil)
exec
之前的命令都被缓存在事务队列里并为执行, QUEUED
是一个简单的字符串, 表示指令已成功缓存到事务队列里.
原子性
文首我们说了 Redis 的事务模型不严格, 主要是指原子性, 请看下面代码
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set jw_str hehe
QUEUED
127.0.0.1:6379> incr jw_str
QUEUED
127.0.0.1:6379> set jw2 666
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
127.0.0.1:6379> get jw2
"666"
可以看出事务执行到第二个指令失败了(不能对字符串做+1的数学运算), 但是第三个指令仍然被执行. 所以 Redis 事务根本不具备原子性, 只仅仅满足了事务"隔离性"中的串行化--当前执行的事务不被其他事务打断.
watch
考虑一个业务场景, Redis 存储了账户余额, 现在需要对余额增减, 流程是先把值取出来, 修改后再写回去.
这时如果有多个客户端操作这个 key 就会出现并发问题, 而 Redis 提供的 watch
指令就可以解决这个问题., 使用方式如下:
127.0.0.1:6379> get jw
(nil)
127.0.0.1:6379> watch jw
OK
127.0.0.1:6379> set jw 666
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set jw 888
QUEUED
127.0.0.1:6379> exec
(nil)
从上面代码示例可以看出, watch
会在事务开始之前就盯住一个变量, Redis 在执行 exec
时会检查变量自 watch
后是否有被修改过, 包括当前客户端在内, 如果被修改过, 则事务执行失败.
注意, Redis 禁止在
multi
和exec
之间执行watch
指令, 必须在事务开启指令multi
之前盯住变量
- 上一篇: Redis 持久化原理
- 下一篇: Redis 过期策略