写点什么

Redis「1」流水线、事务、Lua 脚本

作者:Samson
  • 2022 年 5 月 12 日
  • 本文字数:2046 字

    阅读完需:约 7 分钟

Redis「1」流水线、事务、Lua 脚本

01-Pipeline

This way it is possible to send multiple commands  to the server without waiting for the replies at all, and finally read the replies in a single step.


Redis 是一个基于 TCP 的 C/S 架构程序,客户端和服务端基于所谓的 Request / Response 协议。客户端与服务端之间通过网络连接(本地回环接口或互联网),因此,其性能受 RTT 影响。


使用流水线技术,可以显著的降低 RTT 对性能的影响。其思想为将多个命令打包在一块,通过一次 Request 发送到服务端,服务端将对多个命令的响应打包到一个 Response 中返回给客户端。每个命令的平均 RTT 时间缩短,提高了单位时间处理命令的吞吐量。


不仅仅是 RTT,客户端和服务端通过 Socket 进行网络通讯,流水线也可以减少服务端和客户端因调用系统调用read()write()在用户态和内核态之间切换产生的性能损失。


[1] Redis pipelining

02-Transaction

Redis Transactions allow the execution of a group of commands in a single step.


事务涉及的 Redis 命令有:MULTI EXEC DISCARD WATCH


MULTI命令会开启一个事务,之后,该客户端的命令会被添加到一个队列中,等收到EXEC命令后再顺序执行。DISCARD命令会清空队列并退出事务。


事务中的错误分为两类:

  1. 入队时错误,并不会返回 QUEUED,而是返回错误;而且后续执行 EXEC 时,会报错:

    (error) EXECABORT Transaction discarded because of previous errors.
复制代码
  1. 执行时错误,不影响事务中后续命令的执行。

Redis 事务并不支持回滚,主要基于性能和简单性考虑。

WATCH命令可以用来实现 CAS 操作,或者说乐观锁。EXEC命令后,所有被WATCH命令监控的键都会UNWATCH

02.1-事务的实现

Redis 中的事务通常可以分为三个阶段:

  1. 事务开始(MULTI

  2. 命令入队

  3. 事务执行(EXEC


事务开始时,redisClient的事务标识会被打开clent.flags |= REDIS_MULTI

命令入队时,redisClient中包含了一个任务队列multiState mstate;该队列的结构如下:


typedef struct multiState {  multiCmd *commands;    // 任务队列  int count;             // 已入队命令数量} multiState;
typedef struct multiCmd { robj **argv; // 参数 int argc; // 参数数量 struct redisCommand *cmd; // 命令指针}
复制代码


当开启了事务的客户端提交EXEC命令后,将遍历执行已入队命令,最后移除client.flags &= ~REDIS_MULTI标识,清空事务队列。

02.2-WATCH 命令的实现

在 redisDb 中存储着一个被监控键的映射表dict *watched_keys;,保存的是键到 Redis 客户端的映射关系,键是 Redis 键,值是客户端链表,表示正在监控此键的客户端列表。


图 1. watch 实现原理 (from 《Redis 设计与实现》)


当对被监视的键进行修改时,会触发 touchWatchKey 方法,该方法会将被修改键所映射的客户端列表中的 redisClient 中的REDIS_DIRTY_CAS标识打开。当 redisClient 提交EXEC命令时,如果发现其REDIS_DIRTY_CAS标识被打开,将拒绝执行其事务。

02.3-Redis 事务的 ACID 性质

Redis 中的事务提供了两个保证:


  1. 一个 redisClient 的事务不会被其他 redisClient 的命令中断

  2. 当 redisClient 提交EXEC命令前断开了连接,命令队列中的命令不会执行。如果开启了 AOF,Redis 保证事务中的所有写命令都通过单个write()命令调用写入硬盘。如果系统调用期间 Redis 被强行关闭,AOF 文件中可能会记录不完整的事务,当 Redis 重启时会报错,需要手动删除不完整的事务。


Redis 事务的 ACID 性质:


  • 原子性,从事务的角度,一个事务要么全部被执行,要么全不执行。所以是原子性的。但 Redis 事务并不具备回滚的特性。

  • 一致性。

  • 定义:事务之前是一致的,事务执行以后(不论成功与否)也是一致的,指数据库没有包含非法或无效的错误数据。

  • 三种场景:

  • 入队错误,整个事务都不会执行,所以满足一致性。

  • 执行时出错,出错的命令会被识别出来,并不会影响数据库的一致性。

  • 服务器停机,与 Redis 的持久化选项有关。1)无持久化,重启后 Redis 空白;2)RDB 模式,重启后,恢复到上次快照。3)AOF 模式,重启后,恢复到一个一致状态,若存在不完整的事务,需要将整个事务删除。

  • 隔离性,Redis 服务端是单线程(串行),所以多个事务不会互相影响。

  • 持久性,与持久化的策略有关。

  • 未开启持久化,则不具备持久性。

  • RDB 模式,也不具备持久性,因为只在特定的条件下才会启动快照持久化。

  • AOF 模式,只有频率为 always 时是满足持久性的;其他情况下均不满足持久性。


[2] Transactions

03-Scripting

pipeline vs. scripting


A big advantage of scripting is that it is able to both read and write data with minimal latency, making operations like read, compute, write very fast (pipelining can't help in this scenario since the client needs the reply of the read command before it can call the write command).


transactions vs. scripting


Everything you can do with a Redis Transaction, you can also do with a script, and usually the script will be both simpler and faster.


[1] EVAL script numkeys

发布于: 刚刚阅读数: 2
用户头像

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

发布
暂无评论
Redis「1」流水线、事务、Lua 脚本_Redis 核心技术与实战_Samson_InfoQ写作社区