文章目录
- 事务的概念
- 事务操作
- MULTI
- EXEC
- DISCARD
- WATCH
- UNWATCH
事务的概念
Redis 的事务和 MySQL 的事务概念上是类似的. 都是把⼀系列操作绑定成⼀组. 让这⼀组能够批量执 ⾏
Redis 的事务和 MySQL 事务的区别
- 弱化的原⼦性: redis 没有 “回滚机制”. 只能做到这些操作 “批量执⾏”. 不能做到 "⼀个失败就恢复到 初始状态
- 不具备⼀致性: redis没有约束, 也没有回滚机制,事务执行过程当中如果某个操作出现失败,就可能引起不一致的问题。 MySQL 的⼀致性体现的是运⾏事务前和运⾏后, 结果都 是合理有效的, 不会出现中间⾮法状态
- 不需要隔离性: 因为redis是单线程模型的服务器程序,所有的请求都是串行执行,也没有隔离级别, 因为不会并发执⾏事务
- 不需要持久性: redis数据是保存在内存的,虽然redis有持久化机制,但是否开启持久化, 是
redis-serve
r ⾃⼰的事情, 和事务⽆关
关于redis是否有原子性的争议
原子:不可拆分的最小单位,原子性最原本的含义:把多个逻辑相关的操作放到一起,要么全部执行(不保证成功),要么全部不执行
redis确实做到了上述的含义,但是如果事务当中有若干个操作,存在有失败,那就失败吧,不会有回滚操作
但是MySQL的原子性:把多个逻辑相关的操作放到一起,要么全部执行 成功,要么全部不执行,如果事务当中有操作执行失败,要进行回滚,把中间已经执行的操作全部回退到最初的状态。正是因为MySQL提高了原子性的门槛,让人们讨论原子性的时候,更多的是想到的MySQL这种带有回滚的原子性
网上说:redis事务具有原子性(这里的原子性指的是:只是把相关操作打包放一起执行),有的说没有原子性(这里的原子性指的是:将操作打包放到一起执行&带有回滚机制 => 打包一起正确执行)
在旧版本的redis当中,认为redis的事务是原子性的,但是在新版本当中就把这句话删除了,潜台词就是也认同了原子性
更贴近MySQL这种原子性的设定
redis的事务主要的意义就是为了打包, 避免其它客户端的命令,插队插到中间
Redis 中实现事务是引入了队列(每个客户端都有一个队列),开启事务的时候,此时客户端输入的命令就会发给服务器并且进入到这个队列当中(但是并不会⽴即执⾏),当真正收到 EXEC 命令(执行事务的命令)之后, 才真正执⾏队列中的所有操作,将队列当中的这些任务都按照顺序依次执行(redis会把事务当中的操作都执行完再处理别的客户端)
因此, Redis 的事务的功能相⽐于 MySQL 来说, 是弱化很多的. 只能保证事务中的这⼏个操作是 “连续 的”, 不会被别的客⼾端阻塞
为什么redis事务这么简单,为什么不设计成和MySQL一样强大
因为MySQL的事务在背后付出了很大的代价:空间上要花费更大的空间来存储更大的数据,时间上要有更大的执行开销
事务的案例1:抢票
事务操作
MULTI
开启⼀个事务. 执⾏成功返回 OK.
EXEC
真正执⾏事务,每次添加⼀个操作, 都会提⽰ “QUEUED”, 说明命令已经进⼊客⼾端的队列了. 真正执⾏ EXEC 的时候, 客⼾端才会真正把上述操作发送给服务器
DISCARD
放弃当前事务. 此时直接清空事务队列. 之前的操作都不会真正执⾏到
WATCH
在执⾏事务的时候, 如果某个事务中修改的值, 被别的客⼾端修改了, 此时就容易出现数据不⼀致的问题
- 当开启事务的时候, 如果对 watch 的 key 进⾏修改, 就会记录当前 key 的 “版本号”.
- 在真正提交事务的时候, 会判断当前这个key的版本号和最初进行watch的时候记录的版本号是否一致
- 如果一致:说明当前key在事务开启到最终执行的过程当中,没有别的客户端修改,此时才能真正进行设置操作
- 如果不一直,说明key在其它客户端修改过了,此时就直接丢弃事务当中的操作,
exec
返回nil
注意:
1)redis的watch相当于是基于版本号这样的机制来实现乐观锁
2)watch本质上就是给exec加了判定条件,并且watch必须搭配事务使用,并且必须要再multi
之前使用
3)当执行watch key的时候,会给这个key安排一个版本号并且记录这个版本号,版本号是一个整数, 每次修改都会使版本号变⼤,服务器来维护每个 key 的版本号情况
UNWATCH
取消对 key 的监控
当开启事务,并且给服务器发送了若干个命令之后,此时服务器重启,这时候的事务怎么办
此时的效果就等同于discard