0
点赞
收藏
分享

微信扫一扫

Redis 事务机制深入浅出

是波波呀 2022-02-17 阅读 70

什么是Redis事务

Redis的事务就是一组命令的集合。Redis提供了将一组命令打包集合的方法,并且能够一次性、按顺序的执行多个命令,事务执行期间,服务器不会中断事务改为执行其他客户端的命令请求,只有事务中所有命令都执行完毕后,服务器才会处理其他客户端的命令请求。

在开始说明事务的使用与基本流程之前,需要先说明关于Redis和事务的两个问题。

Redis事务具有原子性吗?

先给出结论:Redis单条命令具有原子性,Redis事务是否具有原子性存在争议

笔者认为关于Redis事务原子性的争议存在的主要原因是对原子性定义不统一。

关于原子性的定义,首先操作不能被中断是公认的必要条件,分歧的点在于,一说认为操作要么全部执行,要么全部不执行才能说操作具有原子性,另一说认为操作要么全部执行成功,要么全部执行失败才能说操作具有原子性。

在关系型数据库中,原子性体现在要么事务中的命令全部成功执行,要么全部不执行,事务中任一命令出现错误,会回滚此前已经执行了的命令。

在Redis中,只能保证事务不可中断,但若事务执行过程中某条命令出现错误,Redis不会回滚已经执行了的命令,因此Redis只做到了要么不执行,要么全部执行,但不能保证出现事务内命令错误时全部执行失败。

因此,无论是哪种对于原子性的理解,关系型数据库中的事务都满足,即关系型数据库中事务是具有原子性的。如果认同前者原子性定义(要么全做,要么全不做),那么可以说Redis的事务具有原子性,如果认同后者的原子性定义(要么全成功,要么全失败),那么Redis的事务不具有原子性

Redis支持回滚吗?

在Redis官方文档中明确指出,Redis不支持回滚

笔者在浏览其余关于Redis回滚问题的博文时,发现部分文章内提及,使用WATCH关键字监控事务时,发现被监控的数据库键被修改时,会进行回滚。产生这种误解是由于没有理解WATCH的判断时机,WATCH早在事务将要实际执行前就会进行判断,若被监控的数据库键被修改,整个事务都不会执行,而非执行到具体被监控的数据库键操作时才进行判断。

事务的执行流程

事务从开始到结束共经历三个阶段:

  1. 事务开始
  2. 命令入队
  3. 事务执行

事务开始

首先明确一点,底层实现中每个Redis客户端内都有一个用于标识事务状态的属性。

Redis中,使用MULTI命令来开启一个事务,命令执行时会判断当前客户端的事务状态,如果当前客户端事务状态已经开启,返回错误并提示事务不可嵌套,若客户端事务未开启,则修改客户端状态开启并返回OK。

演示如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> multi
(error) ERR MULTI calls can not be nested

命令入队

同样,Redis每一条命令执行前都会判断客户端事务标识状态,如果事务没有开启,命令自然直接执行,事务开开启时,不会执行执行命令,会将命令入队。

演示如下:

127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 2
QUEUED

命令执行

使用EXEC命令来执行当前事务,事务内的命令会按照入队顺序依次执行,事务执行完毕或执行失败时会改变当前客户端事务标识状态,事务未开启时使用EXEC命令会报错。

完整演示如下:

127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set b 1
QUEUED
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 2
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI

放弃事务

Redis提供了DISCARD命令来放弃当前事务,成功放弃事务时返回OK,事务为未开启状态时返回错误。

演示如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec
(error) ERR EXEC without MULTI

事务对于错误的处理

Redis事务有两种可能的错误

命令入队时错误

第一种可能的情况是在命令入队阶段,键入了错误的命令,Redis事务对此的处理是弃置整个事务。

演示如下:

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> abc
(error) ERR unknown command `abc`, with args beginning with:
127.0.0.1:6379> set b 1
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> mget a b
1) (nil)
2) (nil)

由以上演示可见,事务内包含错误命令时,会直接弃用整个事务,提示信息为由于此前(命令入队)的错误,已经放弃执行事务。

命令执行时错误

第二种可能的错误是,事务内的命令格式全部正确,但执行时出现错误,比如incr自增的对象不能被转换为整数,对于此种情况,Redis的处理是,事务内任一命令的执行错误不会影响其它命令的正常执行,更不存在事务回滚这一操作

演示如下:

127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> set b string
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr a
QUEUED
127.0.0.1:6379> incr b
QUEUED
127.0.0.1:6379> exec
1) (integer) 2
2) (error) ERR value is not an integer or out of range
127.0.0.1:6379> mget a b
1) "2"
2) "string"

总结

事务流程

流程图总结如下:
在这里插入图片描述

Redis事务的ACID性质

原子性

基本同文首解释的相同,不过多赘述。

首先Redis的事务一定能保证执行不会被中断。但Redis只能保证事务内命令要么全部执行,要么全部不执行,不能保证全部执行成功或全部执行失败,Redis事务是否具有原子性存在争议。

一致性

Redis能够保证,Redis事务在开始前和结束后数据库的完整性不会被破坏,因此Redis事务具有一致性。

隔离性

Redis能够保证,当数据库中有多客户端同时执行事务时(并发),各事务间不会互相影响,在并发状态下执行的事务和串行时执行事务的结果完全相同,Redis事务具有隔离性。

持久性

Redis事务本身没有持久化的操作,但如果服务器开启了AOF或者RDB的持久化,可以认为Redis事务具有持久性。

举报

相关推荐

0 条评论