Redis事务机制详解

基本概念

1)什么是redis的事务?

简单理解为命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞,并且有如下两个特点:
a)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

b)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

  • redis事务命令
命令描述
DISCARD该命令将会取消事务
EXEC执行一个事务里面的所有redis命令
MULTI标记事务的开始
UNWATCH取消WATCH命令对所有KEY的监视
WATCH key [key…]监视一个或多个key,如果在事务执行之前,key被改变,将会终止整个事务的执行

MULTI

MULTI: 标记事务的开始,即开启一个事务块命令队列。所有的命令并不会立即执行,而是先存储到一个队列中,等待执行,例如下面标记事务开始后,使用redis的命令成功后返回QUEUED。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set haha 1
QUEUED
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> get haha
QUEUED
127.0.0.1:6379> hset cache::num userId 123456
QUEUED

EXEC

  • EXEC:执行队列里所有的命令,整个过程是批处理且原子性操作,要么全部成功,要么全部失败。
// 全部成功
127.0.0.1:6379> EXEC
1) OK
2) OK
3) "1"
4) (integer) 1

上面的命令因为执行都是正确,当执行EXEC时候,全部返回成功的案列,下面举一个失败的案列。

// 全部失败的案列
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set haha 2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set xixi
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
// 获取k3的值
127.0.0.1:6379> get k3
(nil)

在上面的新开启标记事务时,特意写入set xixi ,但是没有给这个xixi这个key进行赋值,显然这个命令是错误的,当我们获取上面命令的执行结果如获取k3的值,发现set k3 v3 这个命令并没有成功,其他的一些key操作也是如此,所以我们得出结论:如果命令有一条错误的,则整个队列的命令数据都会失败

那么如果是下面的案列呢,请注意以下的命令

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set haha 1
QUEUED
127.0.0.1:6379> hincrby cache::schema userid 123
QUEUED
127.0.0.1:6379> set java spring
QUEUED
127.0.0.1:6379> incr java
QUEUED

上面最后一个命令,很显然java这个key对应的value值并不是数值类型,是不是当执行这个队列任务时候,也是全部失败呢?那我们执行一个EXEC命令,结果如下:

127.0.0.1:6379> EXEC
1) OK
2) (integer) 123
3) OK
4) (error) ERR value is not an integer or out of range

居然有3条命令成功了!!!!,而第四条命令是失败的,不是说好的如果一个队列中,有一个命令是错误的,那么整个队列的命令都将会失败。

搞清楚这个问题之前,做个java开发的都知道,我们在写代码的时候,进行IO流的操作的时候,经常不得不手动将IOE EXCEPTION给处理了,否则代码编译都通不过。所以在上面全部失败的案列中set xixi 这个命令可理解为预编译的异常。所以set xixi还没有运行就直接返回异常了。而我们使用的命令incr java这个命令,只有在执行的时候,才会报错,这点类似java平常中的RuntimeException一样。

DISCARD

  • DISCARD:取消事务,整个队列里的所有redis命令不会执行。
// 标记事务开始
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set haha 2
QUEUED
127.0.0.1:6379> set xixi 3
QUEUED
127.0.0.1:6379> get haha
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> get haha
"1"

通过上面命令我们发现haha的值并不是等于2而还是之前的值1,这是因为我们将加入队列中的命令进行了取消。

WATCH

解释WATCH之前,让我们举个小小的例子。
假设小明的账户上有80元钱,小明开启事务并花了20元,如下命令所示:

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> decrby money 30
QUEUED

而此时小明的女朋友给小明打了30元,她进行下面的命令操作:

127.0.0.1:6379> get money
"80"
127.0.0.1:6379> incrby money 30
OK

此时小明执行EXEC,并将要进行账户扣款操作如下命令所示:

127.0.0.1:6379> EXEC
(nil)

然而确没有成功,这是因为我们监控了小明的账户,而在监控过程中,小明的事务还没有执行的时候,小红进行了转账操作,导致小明账户扣款异常。这是一个典型的乐观锁思想:
watch的作用是:在事务提交的时候如果有其他客户端对key进行了操作和改变,那么整个事务队列的命令不会执行。即EXEC命令执行的事务中命令无效。

总结

开启:以MULTI开始一个事务
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行:由EXEC命令触发事务

redis事务并不像关系型数据库中那样,如果有一条记录失败,则会整个事务过程其他操作也都失败,也就是redis的事务并没有满足原子性。redis的事务也没有关系型数据库隔离性的概念,这是因为redis的事务提交前,队列中的命令都没有进行执行,所以也就不存在事务内的查询要看到事务里的更新,在事务外查询不能看到

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值