Redis 笔记(08)— 事务(一次执行多条命令、命令 watch/multi/exec/discard、错误处理)

news/2025/2/19 16:58:56/

1. 事务概念

Redis 中的事务 (transaction)是一组命令的集合。事务同命令一样是 Redis 的最小执行单位,一个事务中的命令要么都执行,要么都不执行。事务的原理是先将属于一个事务的命令发送给 Redis,然后再让 Redis 依次执行这些命令。

2. 事务命令

命令说明
watch key[key…]锁定key,直到执行了multi/exec命令
multi标记一个事务块开始
exec执行所有multi之后发的命令
discard丢弃所有multi之后发的命令

3. 事务使用

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a 1
QUEUED
127.0.0.1:6379> set b 2
QUEUED
127.0.0.1:6379> set c 3
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) OK
127.0.0.1:6379> 

multi 标记一个事务的开始,表示之后发送的命令都属于同一个事务,而 exec 命令则告诉 Redis 将等待执行的事务队列中的所有命令(即刚才返回 QUEUED 的命令)按照发送顺序依次执行。exec 命令的返回值是事务命令的返回值组成的列表,返回值顺序和命令的顺序相同。

Redis 会保证一个事务中的命令要么都执行,要么都不执行。 如果在执行 exec 命令之前客户端断线了那么 Redis 会自动清空事务队列,事务中的所有命令都不会执行;而如果客户端执行了 exec 命令后断线也没有关系,Redis 已经记录了所有要执行的命令。

所有的指令在 exec 之前不执行,而是缓存在服务器的一个事务队列中,服务器一旦收到 exec 指令,才开执行整个事务队列,执行完毕后一次性返回所有指令的运行结果。因为 Redis 的单线程特性,它不用担心自己在执行队列的时候被其它指令打搅,可以保证他们能得到的「原子性」执行。

Redis 为事务提供了一个 discard 指令,用于丢弃事务缓存队列中的所有指令,在 exec 执行之前。

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set x 1
QUEUED
127.0.0.1:6379(TX)> set y 2
QUEUED
127.0.0.1:6379(TX)> set z 3
QUEUED
127.0.0.1:6379(TX)> discard		# 丢弃 multi 之后发送的命令
OK
127.0.0.1:6379> get x
(nil)
127.0.0.1:6379> mget x y z
1) (nil)
2) (nil)
3) (nil)
127.0.0.1:6379> 

4. 错误处理

如果 Redis 事务中一个命令发生错误,那么其它的命令还会执行吗?我们主要看两种错误:

  • 语法错误

    只要事务中有一个命令的语法发生错误,那么整个事务中的命令都不会执行。

  • 运行错误

    运行错误是指在执行命令时出现的错误,这种错误在实际执行之前是无法发现的,当事务中出现这种运行错误时,其它命令仍然会继续执行的。

> multi
OK
> set books python
QUEUED
> incr books
QUEUED
> set phone huawei
QUEUED
> exec
1) OK
2) (error) ERR value is not an integer or out of range
3) OK
> get books
"python"
> get phone 
"huawei"

上面的例子是事务执行到中间遇到失败了,因为我们不能对一个字符串进行数学运算,事务在遇到指令执行失败后,后面的指令还继续执行,所以 phone 的值能继续得到设置。

到这里,你应该明白 Redis 的事务根本不能算「原子性」,而仅仅是满足了事务的「隔离性」,隔离性中的串行化——当前执行的事务有着不被其它事务打断的权利。

Redis 事务不支持关系型数据库事务提供的回滚功能。

5. watch 命令

watch 命令可以监控一个或者多个键,一旦其中有一个键被修改或删除,之后的事务就不会执行。监控一直持续到 exec 命令 (事务中的命令是在 exec 命令之后执行的,所以在 multi 命令之后可以修改 watch 监控的键值)。

watch 会在事务开始之前盯住 1 个或多个关键变量,当事务执行时,也就是服务器收到了 exec 指令要顺序执行缓存的事务队列时,Redis 会检查关键变量自 watch 之后,是否被修改了 (包括当前事务所在的客户端)。如果关键变量被人动过了,exec 指令就会返回 null 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。

127.0.0.1:6379> set a 1
OK
127.0.0.1:6379> watch a		# 监控 a 是否变化
OK
127.0.0.1:6379> set a 2		# 键被修改,则后面的 multi 不会被执行
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set a 3
QUEUED
127.0.0.1:6379> exec		# 变量被修改,服务器回复 nil
(nil)
127.0.0.1:6379> get a		# 被修改之后的值,multi 命令之后的值没有被执行
"2"
127.0.0.1:6379> 

也可以用 unwatch 命令来取消监控。

Redis事务在执行时是单线程运行的。但是在执行前有可能别的客户端已经修改了事务里执行的 key 。所以在 multi 事务开始之前用 watch 检测这个 key 避免被其他客户端改变的。如果这个 key 被改变 了 exec 的时候就会报错不执行这个事务。

Redis 禁止在 multiexec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。

6. 事务结合管道使用

Redis 事务在发送每个指令到事务缓存队列时都要经过一次网络读写,当一个事务内部的指令较多时,需要的网络 IO 时间也会线性增长。所以通常 Redis 的客户端在执行事务时都会结合 pipeline 一起使用,这样可以将多次 IO 操作压缩为单次 IO 操作。

比如我们在使用 PythonRedis 客户端时执行事务时是要强制使用 pipeline 的。

pipe = redis.pipeline(transaction=true)
pipe.multi()
pipe.incr("books")
pipe.incr("books")
values = pipe.execute()

http://www.ppmy.cn/news/603108.html

相关文章

Redis 笔记(09)— 过期时间 expire(设置、查询、取消过期时间)

1. 设置过期时间 Redis 使用 expire 命令设置一个键的过期时间,到时间后 Redis 会自动删除它。expire 命令的使用方法为 expire key seconds其中 seconds 表示键的过期时间,单位为秒且必须是整数,最小单位是 1 秒,expire 命令格…

Nova中 Vitalik R1CS例子 的 folding scheme

1. 引言 前序博客有: Nova代码解析Spartan中 Vitalik R1CS例子 SNARK证明基本思路rank-1 constraint system R1CS 其中 F ( x ) x 3 x 5 F(x)x^3x5 F(x)x3x5,注意其中 x , y x,y x,y均为public input/output。以连续调用两次为例,代码见…

Redis 笔记(10)— 发布订阅模式(发布订阅单个信道、订阅信道后的返回值分类、发布订阅多个信道)

1. 发布-订阅概念 发布-订阅 模式包含两种角色,分别为发布者和订阅者。 订阅者可以订阅一个或者若干个频道(channel);而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都可以收到此消息; 2. 发…

Redis 笔记(11)— 文本协议 RESP(单行、多行字符串、整数、错误、数组、空值、空串格式、telnet 登录 redis)

RESP 是 Redis 序列化协议Redis Serialization Protocol 的简写。它是一种直观的文本协议,优势在于实现异常简单,解析性能极好。 ​ Redis 协议将传输的结构数据分为 5 种最小单元类型,单元结束时统一加上回车换行符号 \r\n。 单行字符串 以…

Redis 笔记(12)— 单线程架构(非阻塞 IO、多路复用)和多个异步线程

Redis 使用了单线程架构、非阻塞 I/O 、多路复用模型来实现高性能的内存数据库服务。Redis 是单线程的。那么为什么说是单线程呢? Redis 在 Reactor 模型内开发了事件处理器,这个事件处理器分为多个 Socket(套接字)、IO 多路复用…

Redis 笔记(13)— scan 和 keys 寻找特定前缀key 字段(命令格式、使用示例、定位大key)

1. keys Redis 提供了一个简单暴力的指令 keys 用来列出所有满足特定正则字符串规则的 key。 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> set wohu1104go1 1 OK 127.0.0.1:6379> set wohu1104go2 2 OK 127.0.0.1:6379> set wohu1104go3 3 OK 127.0.0…

Redis 笔记(14)— 持久化及数据恢复(数据持久方式 RDB 和 AOF、数据恢复、混合持久化)

1. 持久化 所谓持久化是指将数据从内存中以某种形式同步到硬盘中,在 Redis 重启后能够根据硬盘中的记录恢复数据。Redis 持久化有两种方式,分别为 RDB(redis data base) 【快照】方式 和 AOF(append only file ) 【日志】方式。 Redis 的持久化机制有两…

Redis 笔记(15)— 管道 pipeline(客户端将批量命令打包发送用来节省网络开销)

Redis 是一种基于客户端-服务端模型以及请求/响应协议的 TCP 服务。这意味着通常情况下一个请求会遵循以下步骤: 客户端向服务端发送一个查询请求,并监听 Socket 返回,通常是以阻塞模式,等待服务端响应。服务端处理命令&#xff…