Redis的事务操作
Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务功能, 我们首先讨论使用 MULTI 、 DISCARD 和 EXEC 三个命令实现的一般事务, 然后再来讨论带有 WATCH 的事务的实现。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
Redis的事务做的很简单,没有像关系型数据库那样把事务的隔离级别划分的那么细,Redis在事务没提交之前不会执行事务中的命令,会等到事务提交的那一刻再执行事务中的所有命令。
事务对异常的处理机制
Redis执行命令的错误主要分为两种:
- 命令错误:执行命令语法错误,比如说将 set 命令写成 sett
- 运行时错误:命令语法正确,但是执行错误,比如说对 List 集合执行 sadd 命令
Redis事务中如果发生上面两种错误,处理机制也是不同的。
命令错误处理机制
开启事务之后,往事务中添加的命令如果有命令错误(语法错误),那么整个事务中的命令都不会执行。
192.168.1.4:0>multi
"OK"
192.168.1.4:0>set a1 a
"QUEUED"
192.168.1.4:0>sett a2 b
"ERR unknown command 'sett'"
192.168.1.4:0>exec
"EXECABORT Transaction discarded because of previous errors."
192.168.1.4:0>get a1
null
上面案例中,开启事务后第一条命令添加返回QUEUED,第二条命令语法错误,最后提交事务。
可以看到,事务提交后 get a1
返回值是null,所以第二条命令的语法错误导致整个事务中的命令都不会执行。
运行时错误处理机制
如果语法没有错误,而执行过程中发生了运行时错误,Redis不仅不会回滚事务,还会跳过这个运行时错误,继续向下执行命令
192.168.1.4:0>lpush l1 a
"1"
192.168.1.4:0>lpush l2 b
"1"
192.168.1.4:0>lpush l3 c
"1"
192.168.1.4:0>multi
"OK"
192.168.1.4:0>lpush l1 aa
"QUEUED"
192.168.1.4:0>sadd l2 bb
"QUEUED"
192.168.1.4:0>lpush l3 cc
"QUEUED"
192.168.1.4:0>exec
1) "2"
2) "WRONGTYPE Operation against a key holding the wrong kind of value"
3) "2"
可以看到最后事务的执行结果是第一条和第三条命令执行成功,第二条命令执行失败,所以第二条命令的执行失败不仅没有回滚事务而且还不会影响后续第三条命令的执行。
reids的乐观锁机制
大多数是基于数据版本(version)的记录机制实现的。
即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个”version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。
此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。
redis中可以使用watch命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败,也可以调用watch多次监视多个key。这样就可以对指定的key加乐观锁了。
注意watch的key是对整个连接有效的,事务也一样。如果连接断开,监视和事务都会被自动清除。当然了exec,discard,unwatch命令都会清除连接中的所有监视。
redis事务的特性
- 单独的隔离操作:事务中所有的命令多会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际的执行,也就是不存在 “ 事务内的查询要看到事务里面的更新,在事务外查询不能看到 ” 这个是让人万分头痛的问题
- 不保证原子性:Redis 同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚