redis事务介绍
Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会插入执行其他客户端发送来的命令,也不会被打断。
- 事务的命令执行是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
对上面第一个保证的理解,最重要的是对执行过程中的理解,执行过程指的是exec()
执行 提交事务的所有命令到redis执行的这个执行过程,而非开启事务,直到提交为止的这个过程。在开启事务以后其他客户端仍然可以向redis发送命令执行。即事务开启以后,redis中的数据仍然可以被修改。
对上面第二个保证的理解,最重要的是要知道,redis没有回滚的概念,已经执行的动作不能undo
。当事务在执行EXEC命令之前出现错误,则redis会清空事务队列,放弃执行事务。在EXEC之后redis批量执行命令时产生的错误会被忽略,其他命令继续执行(这是全部执行的真正含义)。在执行的过程中,redis命令只会因为错误的语法而失败。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务
- 命令入队列
- 执行事务
redis事务的使用
MULTI、EXEC、DISCARD和WATCH命令是Redis事务功能的基础。
MULTI命令
MULTI命令用来开启一个事务,当事务开启之后,客户端可以继续向服务器发送多条命令,这些命令会缓存在队列里面,而非执行(这点很重要,这代表着在multi开启事务以后无法根据get到的数据做逻辑判断),只有当执行EXEC命令时,这些命令才会执行。
DISCARD命令
DISCARD命令可以清空事务队列,放弃执行事务。
如果使用了WATCH命令,DISCARD命令会将当前连接监控的所有键取消监控。
EXEC命令
EXEC命令会触发执行事务中的所有命令。当使用WATCH命令时,只有当受监控的键没有被修改时,EXEC命令才会执行事务中的命令,这种方式利用了检查再设置(CAS)的机制。
这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。
WATCH命令
WATCH命令可以添加监控的键,如果这些监控的键被其他客户端修改,那么事务就不能执行。
UNWATCH命令
清除所有先前为一个事务监控的键。
如果调用了EXEC或DISCARD命令,则不需要手动调用UNWATCH命令。
事务应用举例
监控test1键
127.0.0.1:6379> watch test1
OK
127.0.0.1:6379> get test1
"1234"
127.0.0.1:6379> multi
OK
# 在此时其他客户端修改了test1的值,成功
127.0.0.1:6379> set test1 12345
QUEUED
# 监控到test1键被修改,事务执行失败
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> multi
OK
# 在此时其他客户端修改了test1的值,成功
127.0.0.1:6379> set test1 12345
QUEUED
# 因为没有监控test1键,因此事务执行成功
127.0.0.1:6379> exec
1) OK
基于事务的redis CAS乐观锁实现
鉴于上面的介绍,乐观锁可以通过类似于下面的逻辑来实现
WATCH key
val = GET key
compare(val)
MULTI
SET key $val
EXEC
redis事务的ACID性质(参阅《redis设计与实现》19章)
对于redis事务来说,满足原子性、一致性和隔离性,对持久性的支持由redis的持久化模式决定
Redis脚本和事务
根据定义,Redis脚本也是事务型的。因此,可以通过Redis事务实现的功能,同样也可以通过Redis脚本来实现,而且通常脚本更简单、更快速。