Redis 的事务
可以一次性执行多个命令,本质时一组命令的集合,一个事务中的所有命令都会序列化,按顺序的串行化执行而不会被其他命令插入,不许加塞。一个队列中,一次性,顺序的,排他性的执行一系列命令。
如何使用
MULTI
开启事务
EXEC
提交事务
DISCARD
取消事务
正常执行
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 添加k1 元素
QUEUED # 添加到队列中
127.0.0.1:6379> set k2 v2 # 添加k2 元素
QUEUED # 添加到队列中
127.0.0.1:6379> get k1 # 获取 k1 元素
QUEUED # 添加到队列中
127.0.0.1:6379> keys * # 获取所有的元素
QUEUED # 添加到队列中
127.0.0.1:6379> exec # 提交事务
1) OK # 事务提交后,命令返回的结果
2) OK
3) "v1"
4) 1) "k2"
2) "k1"
放弃事务
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set k1 v2 # 设置 k1 的值 为 v2
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> get k1 # 获取 k1 的值 发现还是 v1
"v1"
提一嘴:事务没有提交执行。别的连接也获取不到的
全部出问题
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set k1 v2
QUEUED
127.0.0.1:6379> set k2 v22
QUEUED
127.0.0.1:6379> setgetee v6 # 这里故意写错
(error) ERR unknown command `setgetee`, with args beginning with: `v6`,
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> EXEC # 提交事务
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 # 获取结果发现所有的语句统统被取消了
"v1"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k5
(nil)
谁出错,就找谁,冤有头债有主
127.0.0.1:6379> MULTI # 事务开启
OK
127.0.0.1:6379> incr k1 # k1 运算 k1 原来的值 v1
QUEUED
127.0.0.1:6379> set k2 22
QUEUED
127.0.0.1:6379> set k3 33
QUEUED
127.0.0.1:6379> set k4 44
QUEUED
127.0.0.1:6379> get k4
QUEUED
127.0.0.1:6379> EXEC # 提交事务
1) (error) ERR value is not an integer or out of range # 运算时报错 其他执行没有问题
2) OK
3) OK
4) OK
5) "44"
127.0.0.1:6379> get k4 # 获取 k4 成功
"44"
127.0.0.1:6379> get k1 # 获取k1 还是原来的值
"v1"
watch 监控
悲观锁
顾名思义,就是每次去拿数据的时候都会认为别人会修改,所以每次再拿数据的时候都会上锁,这样别人想拿这个数据就会block知道它拿到锁,传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等等,都是在做操作之前先上锁。
乐观锁
顾名思义,就是很乐观,每次去拿数据的时候都会认为别人不会修改,所以不会上锁,但是在更新的时候会判断以下在此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量。策略时提交版本必须要大于记录当前版本才能执行更新。
讲个例子:
初始化信用卡可余额和欠额:
127.0.0.1:6379> set balance 100 # 初始化余额100块钱
OK
127.0.0.1:6379> set debt 0 # 初始化欠额 0 元
OK
开启监控,没有加塞串改,先监控在开启MULTI
127.0.0.1:6379> WATCH balance # 监控余额
OK
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> decrby balance 20 # 余额减少 20
QUEUED
127.0.0.1:6379> incrby debt 20 # 欠额增加 20
QUEUED
127.0.0.1:6379> EXEC # 提交事务
1) (integer) 80 # 返回
2) (integer) 20
继续开启监控
127.0.0.1:6379> WATCH balance
OK
开启另一个连接
127.0.0.1:6379> get balance # 你的女朋友发现你只剩下 80 块钱了
"80"
127.0.0.1:6379> set balance 800 # 于是给你充到了 800 块钱
OK
这个时候,你继续刷卡吃饭
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> incrby balance 20 # 余额 减少 20
QUEUED
127.0.0.1:6379> incrby dept 20 # 欠额 增加 20
QUEUED
127.0.0.1:6379> EXEC # 提交事务 发现 返回 nil
(nil)
127.0.0.1:6379> get balance # 获取 余额 发现值为 800 ,没有 减少 20
"800"
放弃监控
127.0.0.1:6379> WATCH balance # 监控 余额
OK
127.0.0.1:6379> set balance 500 # 没有开启事务之前 修改
OK
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set balance 480 # 设置 480
QUEUED
127.0.0.1:6379> EXEC # 提交事务 执行失败
(nil)
127.0.0.1:6379> WATCH balance # 监控余额
OK
127.0.0.1:6379> get balance # 获取余额
"500"
127.0.0.1:6379> set balance 480 # 设置为 480
OK
127.0.0.1:6379> UNWATCH # 放弃监控
OK
127.0.0.1:6379> WATCH balance # 重新监控余额
OK
127.0.0.1:6379> MULTI # 开启事务
OK
127.0.0.1:6379> set balance 480 # 设置为480
QUEUED
127.0.0.1:6379> set balance 460 # 设置为460
QUEUED
127.0.0.1:6379> EXEC # 提交事务,发现执行成功
1) OK
2) OK
一旦执行了EXEC,WATCH 会自动取消
WATCH 指令,类似乐观锁事务提交时,如果key的值已经被别的客户端改变,比如list的值已经被别的客户端 push 了,整个事务队列都不会执行,通过WATCH命令在事务执行之前,监控了多个 key, 倘若在WATCH之后又任何的KEY的值发生了变化,EXEC命令执行的事务,都会被放弃,同时返回 Nullmulti-bulk应答以通知调用者事务执行失败。
使用事务
开启: 以MULTI 开始一个事务
入队: 将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列李米娜
执行: 由EXEC命令出发事务
特性
事务的隔离操作:事务中的所有命令都会序列化、技顺序地执行。事务在执行的过程中,不会被其他客户端发送米的命令请求所打断没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务里的更新,在事务外查询不能看到“这个让人万分头痛的问题不保证原子性。redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回浪