华子目录
事务定义
redis
事务是一个单独
的隔离
操作:事务中的所有命令
都会序列化
,按顺序
的执行。事务在执行的过程中,不会被其他客户端
发送来的命令
请求所打断。redis
事务的主要作用就是串联多个命令
防止别的命令插队
multi、exec、discard
- 从输入
multi
命令开始,输入
的命令
都会依次
进入命令队列
中,但不会执行
,直到输入exec
后,Redis
会将之前的命令队列
中的命令依次执行
。 组队
的过程中可以通过discard
来放弃组队
。
multi #事务开始
指令1 #入队列,只记录不执行。先进先出。顺序执行。
指令2
指令3
exec #真正去执行指令
discard #抛弃,取消事务
- 正常状态(
组队成功,提交成功的情况
)
#在会话1中
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)>
#在会话2中
127.0.0.1:6379> get k1
(nil)
#切换到会话1
127.0.0.1:6379(TX)> exec #执行命令
1) OK
2) OK
#切换到会话2
127.0.0.1:6379> get k1
"v1"
- 正常情况(
取消组队
)
#在会话1中
127.0.0.1:6379> del k1 k2
(integer) 2
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k11 v11
QUEUED
127.0.0.1:6379(TX)> set k22 v22
QUEUED
127.0.0.1:6379(TX)> discard #相当于取消组队
OK
#在会话2中
127.0.0.1:6379> get k11
(nil)
事务的错误处理
1.组队中
某个命令出现了报告错误
,执行时整个的所有队列
都会被取消
。
组队阶段
报错,提交失败
的情况
127.0.0.1:6379> del k11 k22
(integer) 0
#在会话1中
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 #(出现语法错误)
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>
2.如果执行阶段
某个命令报出了错误,则只有报错的命令不会被执行
,而其他的命令都会执行
,不会回滚
。
组队成功
,执行阶段
有失败的情况
#在会话1中
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> incr k3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range #(incr k3出现逻辑错误,文字string不能作加法)
3) OK
127.0.0.1:6379>
#在会话2中 (只有incr k3执行错误,其他命令都会执行,不会回滚)
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> get k4
"v4"
事务的冲突问题
事务场景
有一账户余额为 10000
元,现在三个请求
,第一个请求
想给金额减 8000
元,第二个请求
想给金额减 5000
元,第三个请求
想给金额减 1000
元。如果没有事务
,可能会发生如下情况。
悲观锁(比如:MySQL
)
悲观锁(Pessimistic Lock)
顾名思义,就是很悲观
,每次
去拿数据的时候都认为别人会修改
,所以每次在拿数据的时候都会上锁
,这样别人想拿这个数据就会block
直到它拿到锁。传统的关系型数据库
就用到了很多这种锁机制
,比如行锁,表锁,读锁,写锁
等,都是在做操作
之前先上锁
。
乐观锁(比如:redis
)
乐观锁(Optimistic Lock)
顾名思义,就是很乐观
,每次去拿数据的时候都认为别人不会修改
,所以不会上锁
,但是在更新的时候会判断
一下在此期间别人有没有去更新这个数据
,可以使用版本号
等机制。乐观锁适用于多读
的应用类型
,这样可以提高吞吐量
。Redis
就是利用这种check-and-set
机制实现事务
的。
watch
- 在执行
multi
之前,先执行watch key1 [key2]
可以监视
一个(或多个)key
,如果在事务执行之前这些key
被其他命令所改动
,那么事务将被打断
。 - 为了演示这个功能,我们先设置一个
balance
的初始值为10000
。然后打开一个客户端
,在两个
客户端中都去监视 balance
,同时也开启事务
。
#在server上
127.0.0.1:6379> set balance 10000
#在node1上
192.168.80.129:6379> watch balance
OK
192.168.80.129:6379> multi
OK
192.168.80.129:6379> decrby balance 8000
QUEUED
192.168.80.129:6379> exec
1) (integer) 2000
#在node2上
192.168.80.129:6379> watch balance
OK
192.168.80.129:6379> multi
OK
192.168.80.129:6379> incrby balance 2000
QUEUED
192.168.80.129:6379> exec
1) (nil)
- 可以看到,
node2
中没有操作成功。
unwatch
- 用于取消
watch
命令对所有key
的监视。 - 如果在执行
watch
命令之后,exec
命令或discard
命令先被执行了的话,那么就不需要再执行unwatch
了。
事务三特性
Redis
的事务有以下三个特性:
单独的隔离
操作- 事务中的
所有命令
都会序列化、按顺序
地执行。事务在执行的过程中,不会被其他客户端
发送来的命令请求所打断
。
- 事务中的
- 没有
隔离
级别的概念- 队列中的命令没有
提交之前
都不会实际被执行
,因为事务提交前任何指令都不会被实际执行
。
- 队列中的命令没有
不保证原子性
- 事务中如果有
一条命令执行失败
,其后的命令仍然会被执行
,没有回滚
。
- 事务中如果有