🐼什么是事务,事务的特性
Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执行,也就是将一组 命令进行了打包。
MySQL有四个特性,我们知道,比如原子性,持久性,隔离性,一致性。那么Redis呢?
Redis有一致性吗?隔离性吗?原子性吗?持久性吗?
首先我们来说一下Redis有没有原子性。看了MySQL原子性的定义,就知道redis有没有原子性的特点了。
MySQL对原子性的定义:把多个操作打包在一起,要么全部执行成功,要么全部不执行。(如果执行操作失败,那么就会进行回滚操作)
Redis对原子性的定义:把多个操作打包在一起,要么全部执行 ,但不保证成功,要么全部不执行。(如果执行失败,那就失败吧,不会有回滚的操作了)
现在应该很清楚了,redis只保证了他能帮你把这一组命令打包然后执行,保证了这些命令的执行顺序是连续的, 中间不会插入任何一条命令 ,但是执行的对不对 ,就与我无瓜了。
所以redis具有原子性嘛?已经可以称得上是原子的了,可是MySQL这个"标杆",就使得redis的原子性好像有,但又那么弱。所以,一般我们说原子性,一般是能够**打包+回滚(打包一起正确执行)**才称的上原子性。
💮所以,对于redis是否具有原子性,那么就看你的理解咯~仁者见仁智者见智
💮redis没有约束,也没有回滚机制,如果事务在执行过程中哪一步 发生了错误,就可能影响到正确结果,当然保证不了数据的一致性,所以redis不具有一致性
💮redis本身就是内存级的数据库,数据都是在内存中存储的,虽然Redis也有持久化机制,但是和这里的事务的持久化可无关,所以,不具有持久性
💮隔离级别本质就为了保证多个MySQL客户端并发请求同一组数据,导致数据的错误而设立的一种机制,随着隔离级别的增加,但是效率反而降低了,包括但不亚于脏读幻读不可重复读。redis是一个单线程 的服务器程序,所有的请求/事务,都是串行 执行的,当然不会有并发冲突了,所以没有隔离性一说了,隔离性有点多余了~
所以,Redis这么一和MySQL比,好像就是个弟弟,给我们的感觉。可是,凡事不能一锤子定死,就像UDP和TCP,你说UDP不保证可靠性,但是快啊,不保证可靠性不是缺点,而是UDP的特点。Reids也是,Redis这样的事务设定也是特点。
正是因为MySQL要保证回滚机制,保证数据一致性,保证持久化,有了隔离性,所以慢啊,这是缺点嘛?不是,这也是特点啊!所以,Redis才有了上场机会。
所以,Redis的事务,就是为了**"打包"** ,在执行一组命令的时候,避免 其他客户端,进行**"插队"**
仅此而已~
🐼Redis实现事务
Redis实现事务的目的就是如何事务的本质,Redis为了实现事务,在服务器上搞了⼀个**"事务队列"** . 每次客户端在事务中进行⼀个操作, 都会把命令先发给服务器, 放到 "事务队列" 中(但是并不会立即执行),而是会在真正收到 EXEC 命令之后, redis主线程才真正执行 队列中该客户端 的所有 操作,执行完再执行其他客户端.
什么叫做真正收到了EXEC命令之后,才真正执行?我们先看一下事务的操作
🐼Redis事务操作
cpp
MULTI (maoti)
✅开启⼀个事务. 执行成功返回 OK.
cpp
EXEC
✅真正执行事务.
我们现在解决一下什么叫做真正执行事务,回答上面的问题:
如图:

现在有两个客户端同时请求redis-server,客户端一开启了事务,并且设置了key = 222,在客户端一执行exec之前,客户单而来了一个请求set key = 333,那么到底redis-server先执行谁?
从时间轴上看,好像是客户端一先发送了set key 222,客户端2后发送了set key 333,但是由于客户端一得是exec执行了,才会真正执行set key 222,这个操作反而变成了更晚的操作,最终值就是key = 222
如图:

cpp
DISCARD
✅放弃当前事务. 此时直接清空事务队列. 之前的操作都不会真正执行到.
还有一个操作会触发这个场景,比如,在开启事务,给服务器发送了一大堆的请求,此时服务器重启了,此时的效果就类似于discard
cpp
WATCH
✅监控某个key,是否在事务执行之前(exec) ,就发生了修改。在此时就容易出现数据不⼀致的问题,此时watch就派上用场了.注意执行watch命令要在MULTI之前执行。
对于上面的例子,这个时候, 其实就容易引起歧义.其实我们很难知道到底数据被修改为了222,还是333。watch就是解决这个问题的
因此, 即使不保证严格的隔离性, ⾄少也要告诉⽤⼾, 当前的操作可能存在⻛险.
还是上面的例子,客户端一监控了key,还没来得及exec,但客户端2已经对key修改了,此时客户端一执行了exec,返回nil,此时客户端一就知道了,在我执行事务期间,有别的客户端跟我写入了同一个key了,没有执行成功。

bash
UNWATCH
✅取消对 key 的监控.
相当于 WATCH 的逆操作
🌏这里浅浅谈一嘴watch的实现。
watch的实现,类似于一个"乐观锁".
乐观锁和悲观锁是什么,这里就不细谈了,总之,知道,他俩都不是指某一个具体的锁,而是锁的一种特性。
乐观锁:加锁之前,就要有一个心理预期,预期接下来锁冲突的概率低。
悲观锁:加锁之前,就要有一个心理预期,预期接下来锁冲突的概率高。
redis中的watch相当于实现版本号,这样的机制,来实现的"乐观锁"。
如图所示

最后,redis中任何关于事务能够实现的效果,都能使用lua脚本替代。