Redis的事务本质是一组命令的集合,当事务开启期间,所有的命令都按原有的顺序暂时存入一个缓存队列中,并对其命令进行序列化;当事务被执行时,缓存队列中所有的命令按实际情况进行执行。
所以缓存队列具有一次性,顺序性,排他性的特点。
Redis事务不具有隔离性概念
特别注意,因为Redis是一种基于乐观锁来保证数据一致性的机制。在其事务开启期间,所有的命令都被存入缓存队列中,命令并没有被执行,相当于不存在修改这一过程,所以也不存在各事务相互隔离的概念,所有的事务互不影响。
Redis事务不保证原子性
Redis事务执行时,会将缓存队列中的所有命令执行,当其中一条命令因为除命令本身的错误外
(即类似set写成sset),其他错误情况下,各命令的执行情况互不影响。
Redis事务的三个阶段
- 开启事务
- 命令入队
- 执行事务
事务执行的多种情况
当事务执行时,可能会因为实际情况而导致各命令的执行情况不同。
事务常用的指令
watch key [key...] # 监控key值,当事务开启中,key值发生变化,则事务中断
multi # 开启事务
exec # 执行事务,(执行事务后,之前的监控全部会清除)
discard # 取消事务,(缓存队列中的命令被清除)
unwatch # 放弃监控,取消watch中对所有的key的监控
事务正常执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name zhangsan
QUEUED
127.0.0.1:6379> set age 18
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> set addr china
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "zhangsan"
4) OK
放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "zhangsan"
QUEUED
127.0.0.1:6379> set age 19
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> discard # 放弃事务
OK
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI
127.0.0.1:6379> get name
(nil)
事务遇到命令错误事务异常结束
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name zhangsan
QUEUED
127.0.0.1:6379> set age 12
QUEUED
127.0.0.1:6379> qset addr china # 命令错误
(error) ERR unknown command `qset`, with args beginning with: `addr`, `china`,
127.0.0.1:6379> set addr china
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get name
(nil)
事务遇到语法错误抛出异常事务继续执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name zhangsan
QUEUED
127.0.0.1:6379> set age 12
QUEUED
127.0.0.1:6379> incr name # 语法错误
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> get age
"12"
使用watch
在事务开启期间,可能会有其他进程对数据进行修改,而为了保证数据的一致性,避免一些较极端的情况;可以对其数据进行监控,当数据被其他修改时,放弃当前修改。
第一步
# 1号窗口 开始事务,并监听数据
127.0.0.1:6379> set balance 100
OK
127.0.0.1:6379> set cost 0
OK
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby balance 10
QUEUED
127.0.0.1:6379> incrby cost 10
QUEUED
# 2号窗口 修改监听中的数据
127.0.0.1:6379> get balance
"100"
127.0.0.1:6379> decrby balance 15
(integer) 85
127.0.0.1:6379> get balance
"85"
# 1号窗口 执行事务
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get balance
"85"