Redis之事务解读

2023-10-15 10:28:29 浏览数 (1)

什么是Redis的事务

Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
  • 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。

 Redis事务的三个特性:一致性,顺序性,排他性 

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

 Redis事务不可回滚

相关指令解读

序号

命令及描述

1

DISCARD 取消事务,放弃执行事务块内的所有命令。

2

EXEC 执行所有事务块内的命令。

3

MULTI 标记一个事务块的开始。

4

UNWATCH 取消 WATCH 命令对所有 key 的监视。

5

WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

MULTI

Redis Multi 命令用于标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。

代码语言:javascript复制
redis 127.0.0.1:6379> MULTI            # 标记事务开始
OK
 
redis 127.0.0.1:6379> INCR user_id     # 多条命令按顺序入队
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> PING
QUEUED
 
redis 127.0.0.1:6379> EXEC             # 执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG

返回值:总是返回 OK 。

EXEC

Redis Exec 命令用于执行所有事务块内的命令。

代码语言:javascript复制
# 事务被成功执行
 
redis 127.0.0.1:6379> MULTI
OK
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> INCR user_id
QUEUED
 
redis 127.0.0.1:6379> PING
QUEUED
 
redis 127.0.0.1:6379> EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG

返回值:事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。

DISCARD

Redis Discard 命令用于取消事务,放弃执行事务块内的所有命令。

代码语言:javascript复制
redis 127.0.0.1:6379> MULTI
OK
 
redis 127.0.0.1:6379> PING
QUEUED
 
redis 127.0.0.1:6379> SET greeting "hello"
QUEUED
 
redis 127.0.0.1:6379> DISCARD
OK

返回值:总是返回 OK 。

WATCH

 Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断(也就是在这个事务里面的全部指令都会失效)

代码语言:javascript复制
redis> WATCH lock lock_times
OK
UNWATCH

Redis Unwatch 命令用于取消 WATCH 命如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。令对所有 key 的监视, 因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。

代码语言:javascript复制
redis 127.0.0.1:6379> WATCH lock lock_times
OK
 
redis 127.0.0.1:6379> UNWATCH
OK

返回值:总是返回 OK 。

执行事务过程特殊情况
编译型异常

特点:事务中有错误的命令,会导致默认放弃事务,所有的命令都不会执行

代码语言:javascript复制
127.0.0.1:6379> multi # 开启事务 
OK
127.0.0.1:6379> set name ls
QUEUED
127.0.0.1:6379> append name2 # 错误的命令
ERR wrong number of arguments for 'append' command

127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> EXEC # 执行事务,出现编译型异常
EXECABORT Transaction discarded because of previous errors.
运行时异常

特点:在事务执行的过程中语法没有出现任何问题,但是它对不同类型的key执行了错误的操作,Redis只会将返回的报错信息包含在执行事务的结果中,并不会影响Redis事务的一致性

代码语言:javascript复制
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name ls
QUEUED
127.0.0.1:6379> incr name # 语法正确,但是对一个String类型的k1执行了错误的操作
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> exec # 执行事务,出现运行时异常
OK # 执行ok
ERR value is not an integer or out of range  # 命令报错,但是不影响事务整体的运行

ls# 依旧执行
 为什么 Redis 不支持回

如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。

以下是这种做法的优点:

  • Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。
  • 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。

 举个例子, 如果你本来想通过 incr 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了incr ,回滚是没有办法处理这些情况的。

0 人点赞