Redis(三)事务
2022-09-22 22:46:34

目录
[TOC]

事务概念

关系型数据库的事务

比如Mysql是支持事务的,要么同时成功,要么同时失败,一组操作具有原子性!

Redis事务

单条命令是保证原子性的,但一组命令不保证原子性!

Redis事务没有隔离级别的概念!

所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!

如何执行事务?

实例

先看一个事务的例子,对于Redis事务有一个实际又具体的理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# MULTI :开始事务
redis 127.0.0.1:6379> MULTI
OK
# 下面将这4个命令放入队列
# 设置 book-name 这个key 值是 HelloRedis
redis 127.0.0.1:6379> set book-name "HelloRedis"
QUEUED # 命令入队
# 获取 book-name
redis 127.0.0.1:6379> get book-name
QUEUED
# 存一个set
redis 127.0.0.1:6379> sadd tag "C++" "Programming" "Mastering Series"
QUEUED
# 查看 tag 里有哪些元素
redis 127.0.0.1:6379> smembers tag
QUEUED
# EXEC :执行事务
redis 127.0.0.1:6379> EXEC
1) OK
2) "HelloRedis"
3) (integer) 3
4) 1) "Mastering Series"
2) "C++"
3) "Programming"

事务经历的三个阶段

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

  • 开始事务:MULTI
  • 命令入队:QUEUED
  • 执行事务:EXEC

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

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

如何结束事务?

不执行EXEC,而执行DISCARD

1
2
3
4
5
6
7
8
9
10
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k4 v4 # 存入k4
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> get k4
(nil) # 不存在k4,说明上面的事务没有执行

出错了还会执行吗?

编译型异常:代码有问题,命令写错了,事务中所有的命令都不会执行!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看显示当前库没有数据
127.0.0.1:6379> keys *
(empty list or set)
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 # set一个值
QUEUED
# 故意写错语法
127.0.0.1:6379> getset k3
(error) ERR wrong number of arguments for 'getset' command # 提示错误
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> exec # 执行后 提示事务被丢弃 由于之前的错误
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k1 # 再次确认不存在k1,说明事务没有执行
(nil)

运行时异常:如果队列中执行时错误(执行时才发现命令执行有问题),那么其他命令还是可以正常执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 "v1"
QUEUED
127.0.0.1:6379> incr k1 # 由于k1是字符串类型的,不能自增,所以执行时会出错
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> get k1
QUEUED
# 执行事务
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
4) "v2"
5) "v1"
# 可以看出 除了第二条命令,都可以正常执行

再次理解

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

EXEC 命令负责触发并执行事务中的所有命令:

  • 如果客户端在使用 MULTI 开启了一个事务之后,却因为断线而没有成功执行 EXEC ,那么事务中的所有命令都不会被执行。
  • 另一方面,如果客户端成功在开启事务之后执行 EXEC ,那么事务中的所有命令都会被执行。

当使用 AOF 方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中。

然而,如果 Redis 服务器因为某些原因被管理员杀死,或者遇上某种硬件故障,那么可能只有部分事务命令会被成功写入到磁盘中。

如果 Redis 在重新启动时发现 AOF 文件出了这样的问题,那么它会退出,并汇报一个错误。

使用redis-check-aof程序可以修复这一问题:它会移除 AOF 文件中不完整事务的信息,确保服务器可以顺利启动。

关于事务更多知识:可以参考 官方文档菜鸟教程



Prev
2022-09-22 22:46:34
Next