简介
说到事务,一般都会第一时间的想到MySQL的事务。
在MySQL中事务的提出是为了解决解决原子性操作的,一组执行命令要么全部执行成功,要么执行失败进行回滚,一条也不执行。
在Redis中也有事务这个概念,但与MySQL相比,就比较弟弟了~
Redis实现了个类似的效果,但是不能保证实现MySQL的原子性(在新版本中,官方文档Redis把拥有原子性这句话删了)。所实现的效果是:把一组命令打包去执行,中间如果有命令失败了,不回滚,失败了不管(摆烂)~
Redis事务 VS MySQL事务
众所周知,MySQL的事务具有四个特性:原子性、一致性、持久性、隔离性。
原子性
由于MySQL事务的原子性更被广为人知,而Redis并没有回滚机制,因此对于Redis的事务具有原子性这个说法比较有争议。
一种是认为Redis将一组命令看做是一个“原子的”,整个去执行,具有原子性,另一种是认为Redis事务执行过程中失败了不会回滚,不具有原子性~
一致性
在MySQL中存在着很多的约束条件:foreign key、unique、check.......
而在Redis中就没有这些东西,当事务执行出现失败的时候也不会有回滚机制,就可能出现不一致的情况,因此Redis没有一致性。
持久性
MySQL是存储在硬盘上的数据库,事务的操作一旦生效之后,就会去修改硬盘。
Redis的数据是存储在内存上的,一般用来做为缓存或者内存数据库,虽然也有Redis具有持久化,但事务提交以后,是先对内存上的数据进行修改,然后根据持久化的相应策略才会进行写硬盘。因此Redis没有持久性(这里说的是事务)。
隔离性
隔离性使用的场景在并发下,但Redis本身是一个单线程模型,所有的指令都是串行去执行的,不需要也不具备隔离性。
Redis的事务既然这么鸡肋,那还为什么要用呢?适用场景是啥?为啥搞的不和MySQL一样?
众所周知,鱼和熊掌不可兼得~Redis的宗旨本来就是简单、高效。
MySQL在事务中搞了这么多特性,是需要付出代价的——空间、时间的开销。
Redis的事务一般适用于需要将一组命令去打包执行,这样就可以避免在执行一个客户端命令的时候,突然跑去执行另一个客户端的命令,用来避免指令“插队”的现象,类似加锁~
事务指令操作
multi - 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> set k2 2
QUEUED
127.0.0.1:6379> set k3 3
QUEUED
exec - 执行事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> set k2 2
QUEUED
127.0.0.1:6379> set k3 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
Redis服务器会对每个客户端引入一个队列,用来存放客户端开启事务后执行的指令,即这些指令是不会马上去执行的,当调用exec的时候,才会去执行。
如果在调用exec之前,服务器重启了或者调用了discard,这些指令就没了~
discard - 放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k4 4
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
watch - 监视
用来检查在执行事务期间,监视的key是否修改了,如果修改了不执行,并返回nil.
127.0.0.1:6379> watch k1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 111
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get k1
"555"
watch指令的实现方式类似于CAS指令、乐观锁。
先记录当前的值,然后维护一个“版本号”,如果在这期间进行了修改,版本号就回++,然后最后再与版本号进行比对,就能知道期间有没有修改过了。