前言
Redis事务命令(开启事务、提交事务、取消事务、监视)、乐观锁、悲观锁。
一、概述
Redis事务的本质是一组命令的集合
事务支持一次执行多个命令,一个事务中所有命令都会被序列化。
在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
所以说:Redis 事务就是一次性、顺序性
、排他性的执行一个队列中的一系列命令。
事务支持一次执行多个命令,一个事务中所有命令都会被序列化。
批量操作在发送 EXEC
命令前被放入队列缓存,并不会被实际执行。
Redis 事务不保证原子性
Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis事务的三个阶段
- 开启事务
- 命令入队
- 执行事务
二、实例演示
1.正常执行事务
127.0.0.1:6379> multi # 开启事务
OK
## 命令入队
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> set k4 v4
QUEUED
127.0.0.1:6379(TX)> exec # 执行事务
OK
OK
OK
2.取消事务
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)> set k3 v3
QUEUED
127.0.0.1:6379(TX)> discard # 取消事务,事务队列中命令都不会执行
OK
127.0.0.1:6379> DBSIZE # 可以看出set命令未执行
0
3.编译型异常
若在事务队列中存在命令性错误(类似于java编译性错误),则执行
exec
命令时,所有命令都不会执行。
127.0.0.1:6379> flushall
OK
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)> getset k2 # 错误命令
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 # 执行事务,报错 ,所有命令都不执行
EXECABORT Transaction discarded because of previous errors.
4.运行时异常
若在事务队列中存在语法性错误(类似于 Java 的的运行时异常),则执行
exec
命令时,其他正确命令会被执行,错误命令抛出异常。
127.0.0.1:6379> set k1 v1 # k1的值为字符串,无法实现增减
OK
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379(TX)> incr k1 # 执行失败
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> exec
ERR value is not an integer or out of range # 第一条命令报错,其他命令正常执行
OK
v2
5.监听
watch(乐观锁实现)
- 悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
这样别人想拿到这个数据就会 block 直到它拿到锁。
传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在操作之前先上锁。- 乐观锁
乐观锁(Optimistic Lock),顾名思义,就是很乐观。
每次去拿数据的时候都认为别人不会修改,所以不会上锁。
但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。
乐观锁策略:提交版本必须大于记录当前版本才能执行更新。
(1)事务过程未有数据改变
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 # 余额减20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20 # 支出金额多20
QUEUED
127.0.0.1:6379(TX)> exec # 提交事务,成功执行
1) (integer) 80
2) (integer) 20
(2)事务过程其他线程改变数据
利用多线程测试
窗口1:
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
窗口2:
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> set money 200
OK
窗口1:
127.0.0.1:6379(TX)> exec # 事务提交,执行失败
(nil)
在窗口1事务提交前,窗口2修改了money的值。所以窗口1提交事务,执行失败。文章来源:https://www.toymoban.com/news/detail-745695.html
解决:
窗口1:文章来源地址https://www.toymoban.com/news/detail-745695.html
127.0.0.1:6379> unwatch # 执行失败就先解除监听
OK
127.0.0.1:6379> watch money # 获取新值,重新监听,开启事务
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> DECRBY money 10
QUEUED
127.0.0.1:6379(TX)> INCRBY out 10
QUEUED
127.0.0.1:6379(TX)> exec # 提交事务,执行成功(对比监视的值是否发生变化,没有则执行成功,否则失败)
1) (integer) 190
2) (integer) 30
总结
- 一旦执行
exec
开启事务后,无论事务是否执行成功,watch
对变量的监听都将被取消。 - 当事务执行失败后,需重新执行
watch
命令对变量进行监听,并开启新的事务进行操作。 -
watch
指令类似于乐观锁,在事务提交时,如果 watch 监控的多个 key 中任何 key 的值已经被其他客户端更改。则使用exec
执行事务时,事务队列将不会被执行,同时返回 (nil) 应答以通知调用者事务执行失败。
到了这里,关于Redis之事务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!