目录
1、对于事务的理解
1.1、回顾MySQL的事务
在MySQL中,事务有4个特性:
- 原子性:多个操作打包成一个整体,要么都执行成功,要么都失败
- 一致性:事务执行前后,数据都要合理
- 持久性:事务中做出的修改是会保存到硬盘的
- 隔离性:事务并发执行,各个事务互不干扰
详细可看文章:MySQL事务
1.2、Redis的事务
- 无原子性:Redis虽然会将多个操作打包成一个整体,但是Redis中没有回滚机制,所以Redis的事务制作保证这些操作是一起执行,但不能保证全部执行成功或全部失败,而是失败就失败了,不会回滚,前面执行成功的操作成功就成功了~
- 不保证一致性:因为不涉及到约束,没有回滚机制,导致事务执行前后数据不一定合理。例如MySQL中A给B转账,A成功了,B失败了,A这边就会回滚,前后金额无变化(合理);而在Redis中,A成功了,B失败了,不会回滚A这边,导致A的金额减了,B的却没有加(不合理)
- 不需要隔离性:Redis是一个单线程的服务器程序,所有的事务都是串行执行的~
- 不需要持久性:Redis本身就是内存数据库,数据是存储在内存中的,所有不涉及到持久性。Redis中虽然有持久化机制,但这里的持久化和事务是没啥关系的~
那么以上特点都没有,Redis的事务到底用来干啥的?
Redis的事务其主要意义其实就是打包操作,例如将操作1、2、3、4操作打包在一起,这个事务执行时,就是为了保证执行操作1时,后面就会执行2、3、4,这几个操作是连着执行,中间不会插入操作5来执行~
也就是说,Redis中实现事务是引入了队列(每个客户端都有一个),开启事务时,此时客户端输入的命令,就会发送给服务器并且进入这个队列中(不是立即执行,先放在对队列中),当遇到了“执行事务”命令的时候,此时就会把队列中的这些任务都按照顺序来一次执行。执行事务是Redis主线程完成的,主线程是先把事务中的操作执行完毕后,再处理别的客户端~
Redis如果是按照集群模式部署,就不支持事务~
Redis事务为啥不能设计成和MySQL的事务一样强大?
因为MySQL的事务在背后付出了很大的代价。空间上要花费更多的空间来存储更多的数据;时间上,也要有更大的开销~
如果Redis的事务也这样设计的话,就会消耗更多的时间和空间,但Redis最重要的一点就是快呀,这岂不是影响了它最重要的一个特点了~
2、事务命令使用
- 开始事务:multi
- 执行事务:exec
- 放弃当前事务:discard
使用举例:
注:当事务开启时,队列中也已经存放了一些操作,此时服务器重启了,这时事务就相当于discard~
3、watch的实现原理
3.1、watch用来干什么的?
注意上述提到过的一句话:Redis中实现事务是引入了队列(每个客户端都有一个)
看以下场景:客户端1开启了事务,并在队列中已经存放了一个key1(value为111),此时客户端2又存放key1(value为222),然后客户端1执行事务,此时redis中key1的value值应该为多少?描述不够清楚,看下图:
此时redis中key1的value值应该为多少?
大家也动手试一下,会看到值为111,因为事务中的操作只是放在队列中,还没有执行,执行命令exec时,才真的执行了事务中的操作~
那如果我们想要保证,值为222怎么办?
需要使用watch来监控一下某个key是否在事务执行之前发生了改变,也支持同时监控多个key~
使用举例:
我们会发现,使用watch监控后,只要其他客户端有修改被监控的key时,事务执行就会执行失败(事务中的所有操作都不行)
注意:watch命令执行需要在事务开启之前~
总的来说就是,不使用watch时,哪个是最终执行的操作,最终的值就是谁,使用watch监控后,除了事务所在的这个的客户端如果对key有修改,则事务就会执行失败~
3.2、watch的实现原理
使用watch监控时,在事务开启之前就会记录当前的key的版本号,如果其他客户端对key有修改,版本号就会随之被修改,当事务提交时,当前服务器就会检查key的版本号,如果不是事务开启时的版本号,则会直接让事务执行失败~
好啦,下期见咯~