Redis事务
1 为什么要用事务
Redis的单个命令是原子性的(比如get set mget mset),要么成功要么失败,不存在并发干扰的问题。如果涉及到多个命令的时候,需要把多个命令作为一个不可分割的处理序列,就必须要依赖redis的功能特性来实现了。
Redis提供了事务的功能,可以把一组命令一起执行。Redis的事务有3个特点:
按进入队列的顺序执行。
不会受到其他客户端的请求的影响
事务不能嵌套,多个multi命令效果一样
2 事务的用法
Redis的事务涉及到四个命令:multi(开启事务),exec(执行事务),discard(取消事务),watch(监视)
案例场景:tom和mic各有1000元,tom向mic转账100元。
1 | |
通过multi命令开启事务。Multi执行后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即执行,而是被放到一个队列中。当exec命令被调用时,所有队列中的命令才会被执行。如果没有执行exec,所有的命令都不会被执行。如果中途不想执行事务了,可以调用discard可以清空事务队列,放弃执行。
1 | |
3 Watch命令
为了防止事务过程中某个key‘的值被其他客户端请求修改,在Redis中还提供那个了一个watch命令。也就是多个客户端更新变量的时候,会跟原值做比较,只有它没有被其他线程修改的情况下,才更新成新的值。它可以为Redis事务提供CAS乐观锁行为。
可以一个用watch监视一个或者多个key,如果开启事务之后,至少一个被监视key键在exec执行之前被修改了,那么整个事务都会被取消(key提前过期除外)。可以用unwatch取消。
| client 1 | client 2 |
|---|---|
| set balance 1000 watch balance multi incrby balance 100 |
|
| decrby balance 100 | |
| exec [返回null] get balance |
4 事务可能遇到的问题
4.1 在执行exec之前发生错误
比如:入队的命令存在语法错误,包括参数数量,参数名称等等(编译器错误)。事务会被拒绝执行,也就是队列中所有的明林都不会得到执行。
4.2 在执行exec之后发生错误
比如String使用了hash的命令,参数个数正确,但是数据类型错误,这是一种运行时错误。
1 | |
最后发现set k1 1的命令是成功的,也就是在这种发生了运行时异常的情况下,只有错误的命令没有被执行,但是其他命令没有收到影响。这个显然不符合对原子性的定义,也就是没办法用redis的这种事务机制来实现原子性,保证数据的一致。
4.3 为什么不回滚?
官方的解释是这样的:
redis命令只会因为错误的语法而失败,也就是说,从实用性的角度来说,失败的命令是由代码错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
因为不需要对回滚进行支持,所以redis的内部可以保持简单且快速。需要知道的是:回滚不能够解决代码问题。
Redis从2.6版本开始引入了Lua脚本,也就是说redis可以用lua来执行redis命令。