redis事务
- 一、事务概念
- 二、redist事务执行流程
- 三、redis事务相关指令
- watch的实现阐述:
- 四、redis的事务总结
一、事务概念
Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执⾏. 但是注意体会 Redis 的事务和 MySQL 事务的区别:
-
弱化的原⼦性: redis 没有 “回滚机制”. 只能做到这些操作 “批量执⾏”. 不能做到 “⼀个失败就恢复到初始状态”.
-
不保证⼀致性: 不涉及 “约束”. 也没有回滚. MySQL 的⼀致性体现的是运⾏事务前和运⾏后 , 结果都是合理有效的, 不会出现中间⾮法状态.
-
不需要隔离性: 也没有隔离级别, 因为不会并发执⾏事务 (redis 单线程处理请求) .
-
不需要持久性: 是保存在内存的. 是否开启持久化, 是redis-server ⾃⼰的事情, 和事务⽆关.
redis的原子性:把多个操作打包到一起,要么全部执行,要么全部不执行,redis不在乎这些操作是否存在失败,只关心最终结果。
二、redist事务执行流程
redis会维持一个队列,每次开启事务的时候,客户端发来的命令就会发给服务器并且进入这个队列中而不是立即执行,当遇到执行指令的时候,才会取出队列中的数据依次执行。(这里是由redis主线程执行的,它会把事务中的操作执行完,再去执行其他客户端)
为什么redis的事务不设计得和mysql一样呢,反而设计得这么简单?
原因:Mysql的事务付出了非常多的代价,
- 在空间上,要花费很多的空间来存储更多的数据,例如:MySQL事务执行中途出错,会引发回滚机制,这种回滚机制也是需要花费空间去记录每条执行的指令的。
- 时间上,也需要大量的执行时间支撑,例如MySQL事务的持久化机制是存储在磁盘上的,操作缓慢;Mysql中事务并发执行是进行加锁的,加锁是也是需要开销的。
redis事务的应用场景:商品的抢购
redis事务会结合lua脚本实现一些条件判定,保证判定这些条件时,是原子的。
三、redis事务相关指令
开启事务
MULTI
执行事务
EXEC
放弃当前事务
DISCARD
监控事务
WATCH
取消监控事务
UNWATCH
开启事务,并创建一些记录:
#开启事务
multi
#创建一些记录
mset key "hello" key1 "world" key2 "redis"
每次添加⼀个操作, 都会提⽰ “QUEUED”, 说明命令已经进⼊客⼾端的队列了
然后在另一个窗口打开redis客户端,进行查看:
可以发现我们创建的记录不存在redis中,这是为什么呢?原因很简单因为我们还没有开始执行事务。下面开始执行事务吧:
exec
然后再查看一下我们插入的记录:
执行完事务以后,我们插入的记录就成功保存在redis中了。
注意:当开启事务,并且给服务器发送诺干个命令之后,此时服务器重启,那么就等同于discard(放弃当前事务)。
watch命令的使用演示:
首先添加一个key:
set key "hello"
然后使用watch监听这个key:
watch key
开启事务,并对key进行修改:
#开启事务
multi
#对key修改
set key "redis"
在另一个窗口登入客户端,修改key的值:
set key "world"
最后执行事务:
可以发现此时,事务并没有执行成功,因为我们虽然将修改key的操作放入了事务队列中,但是在事务的外部key值被修改了,且被watch命令监视到了,所以这个事务就不会进行执行。
watch的实现阐述:
乐观锁:加锁之前,就有一个心理预期,预期接下来锁冲突的概率比较低
悲观锁:加锁之前,就有一个心理预期,预期接下来锁冲突的概率比较高
watch实现是基于乐观锁来实现的。
watch监控过程:
比如当watch key 时,会给这个key分配一个版本号,并且记录了这个版本号,只要其他客户端是针对这个key做出了修改,就会引起版本号变大。当执行事务中的操作的时候会做出判定,判定这个key的版本号是否和watch时的版本号一致,如果一致那么就可以执行该事务中的操作,否则就将事务中的该操作丢弃。
四、redis的事务总结
- redis的事务不支持回滚。
- redis并不会保证事务执行前和执行后,内容统一。
- redis主要通过内存在存储数据,不具备持久性。
- redis自身作为一个单线程的服务器模型,处理请求本质上都是串行执行的。
- redis中的lua脚本也能起到类似于事务的效果,官网上说事务这里的任何能实现的效果,都可以使用lua脚本代替。