Redis 事务详解
类似于结构化数据库(MySQL,SQL Server 等),Redis也有事务,不过两者会不太一样。下面来简单聊下redis事务,下面的实验是在单机的redis上测试的,redis的版本是5.0.7。
特点:
- 不支持回滚(一般结构化事务是支持回滚的),但是在另外一个角度看是相对简洁和快速的。
- 在事务中的操作不会返回执行的结果,而是返回queued(命令被加入队列,并参与排队等待执行)。
- 如果事务里面只要有一条命令有语法错误,exec命令就不会执行。
- 如果事务中的命令出现的是运行错误,比如使用散列类型的命令操作集合类型的键,由于这种错误在实际执行前Redis是没法发现的,所以在事务里面这样的命令是会被接受且执行的,其他的命令也会执行(包括出错命令之后的命令)。
- Redis事务在执行过程中并不会阻塞其他连接的并发,而只是通过比较watch监控键值对去保证数据一致性,如果没有发生变化,那么它会执行事务队列中的命令。如果有发生变化,那么它不会执行任何事务中的命令。这个时候只能重新执行失败的事务。
对于语法错误和运行错误这两种错误小伙伴们可以亲自测试下,这里不做展示了
在了解完Redis事务的几个特性后,下面我们来分析两个场景来帮助理解:
场景1:比如用户A想关注用户B,也就是用户的关注和被关注的关系,如果使用Redis存储这样的关系可以使用集合类型。思路是对每个用户使用两个集合user:1:followers和user:1:following,分别用来存储关注该用户的用户集合和该用户关注的集合,如果在执行完第一条命令后由于某种原因导致第二条命令没法执行,就会出现用户A的粉丝有用户B,但是用户B的关注列表确没有用户A,这个时候就会想到事务。
redis>multi
OK
redis>sadd user:1:followers 2
QUEUED
redis> sadd user:2:following 1
QUEUED
redis>exec
1) (integer) 1
2) (integer) 1
当然这也要看是出现什么类型的错误,如果是语法错误,使用事务完全是没有问题的,但是如果出现的是运行错误,这种错误使用事务就不行了,但是这种类型的错误也不是完全不能避免,例如我们可以规范键的命名等。
场景2:我们了解到Redis事务中的命令执行是不会返回执行结果的,如果我们的业务是要根据上一个命令的结果来进行后面的操作,这个时候使用事务就达不到要求,但是我们又要防止竞态的发生,这个时候我们可以使用watch来解决,watch命令可以监控一个或多个键,一旦其中一个键被修改或者删除,之后的事务就不会执行。执行exec命令后会取消对所有键的监控。
下面来实际操作下,来帮助理解watch命令
redis>set height 180
OK
redis>watch height
OK
redis> set height 170
OK
redis> get height
170
redis> multi
OK
redis>set height 185
QUEUED
redis>set age 87
QUEUED
redis>exec
(nil)
redis>get age
"29" //之前设置的值
redis>get height
"170" //之前的值
redis>set height 190
OK //事务提交后设置成功了
redis>get height
"190"
如果在事务之前watch了一个key,并且watch操作后修改了这个key,在后面的事务中也对这个key进行了修改(或者删除)操作,那么这个事务是不会执行成功的。当执行完exec后所有的watch会被释放,这个时候就可以对key进行设置。有小伙伴可能会想如果我事务里面没有操作被watch(监控)的key呢?
redis> set color blue
OK
redis> watch color //监控键color
OK
redis> set color green
OK
redis> get color
"green"
redis> multi
OK
redis> set car 1 // 修改键car 而不是键color
QUEUED
redis> exec
(nil) //执行不成功
结果发现在事务中修改其他的key也不行,这个时候只能重新执行该事务,也就是说watch发现被监控的key被修改后,这个修改操作后的事务都不会执行成功,不论是否是修改当前被watch的key。
需要注意:watch命令只是当被监控的键值被修改后阻止之后一个事务的执行,而不能保证其他客户端不能修改这一键值,关于这一点这里就不展示具体的操作了,小伙伴们如果有不理解的地方可以多测试下。
先聊到这,有需要补充的小伙伴可以在下面和我留言哦,看到会及时回复的。