Redis也提供了对事务的支持,在Redis中,我们常用的命令就是multi、exec、discard、watch这四个命令。其中multi命令用于开始一个事务,该语句之后的所有命令都会被视为事务之内的操作,而exec是提交一个事务,discard是回滚一个事务。
下面是对一些命令的具体介绍:
multi----用于标记事务的开始,其后执行的命令都被存放于命令队列,直到执行exec时,这些命令才会被原子的执行。它的返回值总是OK。
exec----执行在一个事务内命令队列中的所有命令,同时将当前连接的状态恢复为正常状态,也就是所谓的非事务状态。如果在事务中执行了watch命令,那么只有当watch所监控的keys没有被修改的前提下,exec命令才能执行事务队列中的所有命令,否则exec将放弃当前事务中的所有命令。它原子性的返回事务中各个命令的返回结果。如果在事务中使用了watch,一旦事务被放弃,那么exec将返回null-multi-bulk这个回复。
discard--回滚事务队列中的所有命令,同时将当前连接的状态恢复为正常状态,也就是非事务状态。如果watch命令被使用,该命令将unwatch所有的keys。始终返回OK。
watch---在multi命令执行之前,可以指定待监控的keys,在执行exec之前,如果被监控的keys发生了修改,那么exec将会放弃执行该事务队列中的所有命令。它始终返回OK。
unwatch----取消当前事务中指定监控的keys,如果执行了exec或者discard命令,则无需手工执行该命令,此时所有被监控的keys都会被自动取消。它始终返回OK。
在Redis的事务中,watch命令可以用于提供CAS功能,所谓CAS就是Check And Set功能。假设我们通过watch命令在事务执行之前监控了多个keys,加入在我们watch之后有任何的key的值发生了变化,那么exec命令执行的事务都会被放弃,同时返回一个Null multi-bulk应答以通知调用者事务执行失败。
首先我们看一段代码:
val = get key1
val = val + 1
set key1 val
如果在同一时刻有多个客户端执行上述代码,那么就可能会出现多线程中经常出现的一种问题----竞态争用。比如两个客户端都读取了key1的原有值,如果该值原来为18,那么都执行上述代码后,有可能这两个在读取数据的时候都读到的是18,导致最后的程序得到的结果是19,而不是我们想象中的20.我们使用如下命令则可以避免这个问题:
watch key1
val = get key1
val = val + 1
multi
set key1 val
exec
上面我们的代码中,因为watch key1命令在前面,也就是在获取key1对应的值的时候监控了改建,注意我们这里是把set包围在事务中,如果该值被修改过了,由于watch的存在,那么该操作将会失败,因此我们可以在判断返回值来知道val是否已经被修改过,然后我们可以选择是否需要重新执行该命令。