简单例子讲明白Redis事务

Publish: June 26, 2019 Category: 运维 No Comments

熟悉关系型数据库的同学对应该对事务比较了解,简单的说:事务表示一组动作,要么全部执行,要么全部不执行。还是用经典的银行转帐来做说明,用户a要给用户b转一笔款,正确的执行流程是a账户减少指定金额,b账户增加指定金额。这两个动作要么同时执行,要么都不执行才能保证数据的准确性,以保证数据一致。

Redis事务理解和示例

Redis提供了“简单”的事务,将一组需要一起执行的命令放到multi和exec两个命令之间,multi代表事务开始,exec代表事务结束,他们之间的命令是原子顺序执行的。我们一转帐来进行模拟这个过程:

#设置用户a和b的额度都是100

127.0.0.1:6379> set user:a:quota 100
OK
127.0.0.1:6379> set user:b:quota 100
OK
#执行转帐事务:a减掉20 b增加20
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY user:a:quota 20
QUEUED
127.0.0.1:6379> INCRBY user:b:quota 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80
2) (integer) 120

#查看执行结果

127.0.0.1:6379> MGET user:a:quota user:b:quota
1) "80"
2) "120"

可以看到decrby和incrby命令返回结果都是QUEUED,代表命令并没有真正执行,而是暂时保存在redis中,当exec命令执行之后转帐行为才算完成。如果要停止执行该事务可以使用discard命令代替exec命令即可。

事务中的两种问题处理

如果事务中的命令出现错误,Redis的出现机制也不尽相同。通常情况下可以分为一下两种错误类型:
1、命令错误
例如上面的命令中DECRBY 写成了DECRRBY,这种错误属于语法错误,会造成整个事务无法执行,值也不会发生变化。

127.0.0.1:6379> set user:a:quota 100
OK
127.0.0.1:6379> set user:b:quota 100
OK
127.0.0.1:6379> MULTI
OK

#错误的语法
127.0.0.1:6379> DECRRBY user:a:quota 20
(error) ERR unknown command `DECRRBY`, with args beginning with: `user:a:quota`, `20`, 
127.0.0.1:6379> INCRBY user:b:quota 20
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
#结果并没有发生改变
127.0.0.1:6379> MGET user:a:quota user:b:quota
1) "100"
2) "100"

2、运行时错误

在处理a账户时候,把decrby写成了incrby,语法没有错误会执行
127.0.0.1:6379> set user:a:quota 100
OK
127.0.0.1:6379> set user:b:quota 100
OK
127.0.0.1:6379> MULTI
OK
#错误的将decrby 写成了incrby
127.0.0.1:6379> incrby user:a:quota 20
QUEUED
127.0.0.1:6379> INCRBY user:b:quota 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 120
2) (integer) 120
127.0.0.1:6379> MGET user:a:quota user:b:quota
1) "120"
2) "120"

Redis中“乐观锁”

另外redis还支持锁定功能,有些应用场景在事务之前,确保事务中的key没有被其他客户端修改过才执行事务,否则不执行(类似乐观锁)。Redis提供了Watch命令解决这类问题。
看下面一个简单例子
时间点 客户端A 客户端B

t1 set watchkey "only_me" 
t2 watch watchkey 
t3 multi 
t4  append watchkey "second"
t5 append watchkey jedis 
t6 exec 
t7 get watchkey 

可以看到”客户端A“在执行multi之前执行了watch命令,”客户端B“在”客户端A“执行exec之前修改了key值,使得事务没有执行(通过exec结果是nil可以判定)试验流程如下:

127.0.0.1:6379> set watchkey "only_me"
OK
127.0.0.1:6379> WATCH watchkey
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> APPEND watchkey "first"
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get watchkey
"only_mesecond"

在客户端A执行之前,新启动一个客户端执行如下命令

127.0.0.1:6379> APPEND watchkey "second"
(integer) 13

通过上面的示例可以看到,watchkey的最后结果是only_mesecond,事务内的‘append watch "first"’并没有执行。而客户端B的命令执行了。

总结

Redis提供了简单的事务,之所以说他简单是因为特不支持事务的回滚特性,同时无法实现命令直接爱嗯的逻辑关系计算,这也许你体现”keep it simple"的特性。

Tags: redis, reids事务

Related Posts:
  • [尚无相关文章]

Leave a Comment