Redis 事务

Redis 事务

Redis 事务:一次执行多个命令

Redis事务工作特点:

  • 批量操作放入队列缓存。(执行有顺序)
  • 事务中任意命令执行失败,其余的命令依然被执行。(不保证原子性)
  • 在事务执行过程,其他命令请求不会插入到事务执行命令序列中。(排他性)

三个阶段:

  • 开始事务。(multi)
  • 命令入队。
  • 执行事务。(exec)

实例

127.0.0.1:6379> multi #开启事务
OK

########## 命令入队 开始##################
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
########### 命令入队 结束 #################

127.0.0.1:6379(TX)> exec # 执行事务
1) OK
2) OK
3) "v1"
4) OK

放弃事务

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> set k5 v5
QUEUED
127.0.0.1:6379(TX)> discard #取消事务
OK
127.0.0.1:6379> get k4 ####取消执行事务中所有命令,k4就会没值
(nil)

命令有误,事务中所有命令都不执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> getset k2 #命令错误
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常

127.0.0.1:6379> set k1 v1 
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> INCR k1 #k1自加一,必须时数值,有错误,不影响其他命令执行
QUEUED
127.0.0.1:6379(TX)> set k2 v2 
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range
2) OK
127.0.0.1:6379> get k2
"v2"

Redis 事务--锁

Watch:相当于乐观锁

通过监视key的值是否改变来决定事务能否正常提交

实例

正常执行

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #监视money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec #正常执行
1) (integer) 80
2) (integer) 20

事务正常执行完成后,watch监视结束

多线程操作

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 50
QUEUED
127.0.0.1:6379(TX)> INCRBY out 50
QUEUED
127.0.0.1:6379(TX)> 

事务没有执行,另一个线程修改了监视的key

执行事务

127.0.0.1:6379(TX)> exec #执行失败
(nil)

重新执行事务,使用UNWATCH命令(用于取消 WATCH 命令对所有 key 的监视)来保证下一个事务的执行不会受到影响

127.0.0.1:6379> watch money #开启监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 50
QUEUED
127.0.0.1:6379(TX)> INCRBY out 50
QUEUED
127.0.0.1:6379(TX)> exec # 执行之前,另一个线程执行了 set money 200
(nil)
127.0.0.1:6379> UNWATCH  #取消 WATCH 命令对所有 key 的监视
OK
127.0.0.1:6379> WATCH money #开启监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 50
QUEUED
127.0.0.1:6379(TX)> INCRBY out 50
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 150
2) (integer) 50

分布式锁(setnx)

利用setnx命令的特征(存在Key则返回设置失败,不存在Key则返回设置成功),并通过del命令释放锁。

127.0.0.1:6379> set num 10
OK
127.0.0.1:6379> setnx lock-num 2 #设置key
(integer) 1
127.0.0.1:6379> incrby num -1
(integer) 9
127.0.0.1:6379> del lock-num #释放,如果不释放的话,就无法再次通过setnx设置key
(integer) 1
127.0.0.1:6379> setnx lock-num 1 
(integer) 1
127.0.0.1:6379> setnx lock-num 1
(integer) 0

使用setnx可能会出现忘记释放的情况,需要针对key设置有效时间

  • expire lock-key second
  • pexpire lock-key milliseconds
127.0.0.1:6379> setnx lock-num 1
(integer) 0
127.0.0.1:6379> expire lock-num 1
(integer) 1
127.0.0.1:6379> setnx lock-num 1
(integer) 1

 由于操作通常都是微秒或毫秒级,因此该锁定时间不宜设置过大。具体时间需要业务测试后确认。

 例如:持有锁的操作最长执行时间127ms,最短执行时间7ms。

 测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时

 锁时间设定推荐:最大耗时*120%+平均网络延迟*110%

 如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可

原文地址:https://www.cnblogs.com/WarBlog/p/15245561.html