Redis之事务管理
事务简介
Redis事务可以一次执行多个命令,并且带有以下三个重要的保证:
- 批量操作在EXEC命令前被放入队列缓存。
- 收到EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历一下三个阶段:
- 开启事务
- 命令入列
- 执行事务
事务相关命令
序号 | 命令 | 描述 |
---|---|---|
1 | discard | discard 命令用于取消事务,放弃执行事务块内的所有命令 |
2 | exec | exec命令用于执行所有事务块内的命令 |
3 | multi | multi 命令用于标记一个事务块的开始。 事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由EXEC命令原子性(atomic)地执行 |
4 | unwatch | unwatch 命令用于取消watch命令对所有key的监视 |
5 | watch | WATCH key [key ...] watch 命令用于监视一个或多个key,如果再事务执行之前这个或这些key被其他命令所改动,那么事务将被打断。 |
实例
下面执行一个事务的例子。它是以 multi 开始一个事务,然后将多个命令入列到事务中,最后通过exec命令来触发事务,一并执行事务中的所有命令。如下:
1 | 127.0.0.1:6379> multi |
单个Redis命令的执行是原子性的,但是Redis没有在事务上增加任何原子性的机制,所以Redis事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量执行并不是原子性的操作,中间某条执行失败也不会导致前面的命令进行回滚,也不会导致后面的执行不会被执行。
例如:
1 | 127.0.0.1:6379> multi |
说明:如果再set b bbb处失败了,set a 已经成功执行,不会进行回滚。并且set c还会继续执行。
事务中的异常情况
redis 中事务的异常情况总的来说,分为两种:
- 进入队列之前就能发现的错误,比如命令输错。
- 执行exec命令之后才能发现的错误,比如给一个非数字字符加1。
那么对于这两种不同的异常,redis中有不同的处理策略。对于第一种错误,服务器会对命令入列失败的情况进行记录,并在客户端调用exec命令时,拒绝执行并自动放弃这个事务。如下:
1 | 127.0.0.1:6379> multi |
而对于第二种情况,redis并没有对它们进行特别处理,即使事务中有某个/某些命令在执行时产生了错误,事务中的其他命令仍然会继续执行。如下:
1 | 127.0.0.1:6379> multi |
不同于关系型数据库,redis的事务中出错时不会进行事务回滚。即redis的事务更像是命令操作的批处理。对此,官方解释如下:
Redis命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面。这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。因为不需要对回滚进行支持,所有redis的内部可以保持简单且快速。
watch 命令
事务中的watch命令可以用来监控一个key,通过一个或多个key。通过这种监控,我们可以为redis事务提供(CAS)行为。如果有至少一个被watch监视的键在exec执行之前被修改了,那么整个事务都会被取消。exec返回nil-reply 来表示事务已经失败。如下:
1 | 127.0.0.1:6379> set k4 2 |
watch 命令可以监控一个或多个key。一旦其中一个键被修改(或删除)。之后的事务就不会执行,监控一直持续到exec命令(事务中的命令是在exec之后才执行的,exec命令执行完之后被监控的键会自动被unwatch).
通过unwatch 命令,可以取消对一个key的监控。如下:
1 | 127.0.0.1:6379> set kk 3 |
关于Redis的事务问题就先到这了。