当前位置:

首页 > 编程开发 > Redis事务模式和Lua脚本的原理是什么

Redis事务模式和Lua脚本的原理是什么

Redis Assistant
Redis Assistant

Redis Assistant 是一款为Redis设计的高效可视化管理与监控工具,提供多种操作模式,高效处理服务器数据。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

立即下载
¥19
Windows/macOS/Linux 2026-08-22
正版软件 Windows macOS软件 Linux 数据库管理工具

准确的讲,Redis事务包含两种模式:事务模式和Lua脚本。先说结论:Redis的事务模式具备如下特点:保证隔离性;无法保证持久性;具备了一定的原子性,但不支持回滚;一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis的事务可以保证一致性。但Lua脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。Lua脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。1事务

本文目录

    准确的讲,Redis 事务包含两种模式 : 事务模式 和 Lua 脚本。

    先说结论:

    Redis 的事务模式具备如下特点:

    • 保证隔离性;

    • 无法保证持久性;

    • 具备了一定的原子性,但不支持回滚;

    • 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。

    但 Lua 脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。

    Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。

    1 事务原理

    Redis 的事务包含如下命令:

    序号命令及描述
    1MULTI 标记一个事务块的开始。
    2EXEC 执行所有事务块内的命令。
    3DISCARD 取消事务,放弃执行事务块内的所有命令。
    4WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
    5UNWATCH 取消 WATCH 命令对所有 key 的监视。

    事务包含三个阶段:

    1. 事务开启,使用 MULTI , 该命令标志着执行该命令的客户端从非事务状态切换至事务状态 ;

    2. 命令入队,MULTI 开启事务之后,客户端的命令并不会被立即执行,而是放入一个事务队列 ;

    3. 执行事务或者丢弃。如果收到 EXEC 的命令,事务队列里的命令将会被执行 ,如果是 DISCARD 则事务被丢弃。

    下面展示一个事务的例子。

    1
     redis> MULTI 
    2
     OK
    3
     redis> SET msg "hello world"
    4
     QUEUED
    5
     redis> GET msg
    6
     QUEUED
    7
     redis> EXEC
    8
     1) OK
    9
     1) hello world

    这里有一个疑问?在开启事务的时候,Redis key 可以被修改吗?

    Redis事务模式和Lua脚本的原理是什么

    在事务执行 EXEC 命令之前 ,Redis key 依然可以被修改。

    在事务开启之前,我们可以 watch 命令监听 Redis key 。在事务执行之前,我们修改 key 值 ,事务执行失败,返回 nil 。

    Redis事务模式和Lua脚本的原理是什么

    通过上面的例子,watch 命令可以实现类似乐观锁的效果 。

    2 事务的ACID

    2.1 原子性

    原子性是指:一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。

    第一个例子:

    在执行 EXEC 命令前,客户端发送的操作命令错误,比如:语法错误或者使用了不存在的命令。

    1
     redis> MULTI
    2
     OK
    3
     redis> SET msg "other msg"
    4
     QUEUED
    5
     redis> wrongcommand  ### 故意写错误的命令
    6
     (error) ERR unknown command 'wrongcommand' 
    7
     redis> EXEC
    8
     (error) EXECABORT Transaction discarded because of previous errors.
    9
     redis> GET msg
    10
     "hello world"

    在这个例子中,我们使用了不存在的命令,导致入队失败,整个事务都将无法执行 。

    第二个例子:

    事务操作入队时,命令和操作的数据类型不匹配 ,入队列正常,但执行 EXEC 命令异常 。

    1
     redis> MULTI  
    2
     OK
    3
     redis> SET msg "other msg"
    4
     QUEUED
    5
     redis> SET mystring "I am a string"
    6
     QUEUED
    7
     redis> HMSET mystring name  "test"
    8
     QUEUED
    9
     redis> SET msg "after"
    10
     QUEUED
    11
     redis> EXEC
    12
     1) OK
    13
     2) OK
    14
     3) (error) WRONGTYPE Operation against a key holding the wrong kind of value
    15
     4) OK
    16
     redis> GET msg
    17
     "after"

    这个例子里,Redis 在执行 EXEC 命令时,如果出现了错误,Redis 不会终止其它命令的执行,事务也不会因为某个命令执行失败而回滚 。

    综上,我对 Redis 事务原子性的理解如下:

    1. 命令入队时报错, 会放弃事务执行,保证原子性;

    2. 命令入队时正常,执行 EXEC 命令后报错,不保证原子性;

    也就是:Redis 事务在特定条件下,才具备一定的原子性 。

    2.2 隔离性

    数据库的隔离性是指:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。

    事务隔离分为不同级别 ,分别是:

    • 未提交读(read uncommitted)

    • 提交读(read committed)

    • 可重复读(repeatable read)

    • 串行化(serializable)

    首先,需要明确一点:Redis 并没有事务隔离级别的概念。这里我们讨论 Redis 的隔离性是指:并发场景下,事务之间是否可以做到互不干扰。

    我们可以将事务执行可以分为 EXEC 命令执行前和 EXEC 命令执行后两个阶段,分开讨论。

    1. EXEC 命令执行前

    在事务原理这一小节,我们发现在事务执行之前 ,Redis key 依然可以被修改。此时,可以使用 WATCH 机制来实现乐观锁的效果。

    1. EXEC 命令执行后

    因为 Redis 是单线程执行操作命令, EXEC 命令执行后,Redis 会保证命令队列中的所有命令执行完 。 这样就可以保证事务的隔离性。

    2.3 持久性

    数据库的持久性是指 :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

    Redis 的数据是否持久化取决于 Redis 的持久化配置模式 。

    1. 没有配置 RDB 或者 AOF ,事务的持久性无法保证;

    2. 使用了 RDB模式,在一个事务执行后,下一次的 RDB 快照还未执行前,如果发生了实例宕机,事务的持久性同样无法保证;

    3. 使用了 AOF 模式;AOF 模式的三种配置选项 no 、everysec 都会存在数据丢失的情况 。always 可以保证事务的持久性,但因为性能太差,在生产环境一般不推荐使用。

    综上,redis 事务的持久性是无法保证的 。

    2.4 一致性

    一致性的概念一直很让人困惑,在我搜寻的资料里,有两类不同的定义。

    1. 维基百科

    我们先看下维基百科上一致性的定义:

    Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key – foreign key relationship.

    在这段文字里,一致性的核心是“约束”,“any data written to the database must be valid according to all defined rules ”。

    如何理解约束?这里引用知乎问题 如何理解数据库的内部一致性和外部一致性,蚂蚁金服 OceanBase 研发专家韩富晟回答的一段话:

    “约束”由数据库的使用者告诉数据库,使用者要求数据一定符合这样或者那样的约束。当数据发生修改时,数据库会检查数据是否还符合约束条件,如果约束条件不再被满足,那么修改操作不会发生。

    关系数据库最常见的两类约束是“唯一性约束”和“完整性约束”,表格中定义的主键和唯一键都保证了指定的数据项绝不会出现重复,表格之间定义的参照完整性也保证了同一个属性在不同表格中的一致性。

    “ Consistency in ACID ”是如此的好用,以至于已经融化在大部分使用者的血液里了,使用者会在表格设计的时候自觉的加上需要的约束条件,数据库也会严格的执行这个约束条件。

    所以事务的一致性和预先定义的约束有关,保证了约束即保证了一致性。

    我们细细品一品这句话: This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct。

    写到这里可能大家还是有点模糊,我们举经典转账的案例。

    我们开启一个事务,张三和李四账号上的初始余额都是1000元,并且余额字段没有任何约束。张三给李四转账1200元。张三的余额更新为 -200 , 李四的余额更新为2200。

    从应用层面来看,这个事务明显不合法,因为现实场景中,用户余额不可能小于 0 , 但是它完全遵循数据库的约束,所以从数据库层面来看,这个事务依然保证了一致性。

    Redis 的事务一致性是指:Redis 事务在执行过程中符合数据库的约束,没有包含非法或者无效的错误数据。

    我们分三种异常场景分别讨论:

    1. 执行 EXEC 命令前,客户端发送的操作命令错误,事务终止,数据保持一致性;

    2. 执行 EXEC 命令后,命令和操作的数据类型不匹配,错误的命令会报错,但事务不会因为错误的命令而终止,而是会继续执行。正确的命令正常执行,错误的命令报错,从这个角度来看,数据也可以保持一致性;

    3. 执行事务的过程中,Redis 服务宕机。这里需要考虑服务配置的持久化模式。

      • 无持久化的内存模式:服务重启之后,数据库没有保持数据,因此数据都是保持一致性的;

      • RDB / AOF 模式: 服务重启后,Redis 通过 RDB / AOF 文件恢复数据,数据库会还原到一致的状态。

    综上所述,在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。

    1. 《设计数据密集型应用》

    这本书是分布式系统入门的神书。在事务这一章节有一段关于 ACID 的解释:

    Atomicity, isolation, and durability are properties of the database,whereas consistency (in the ACID sense) is a property of the application. The application may rely on the database’s atomicity and isolation properties in order to achieve consistency, but it’s not up to the database alone. Thus, the letter C doesn’t really belong in ACID.

    原子性,隔离性和持久性是数据库的属性,而一致性(在 ACID 意义上)是应用程序的属性。应用可能依赖数据库的原子性和隔离属性来实现一致性,但这并不仅取决于数据库。因此,字母 C 不属于 ACID 。

    很多时候,我们一直在纠结的一致性,其实就是指符合现实世界的一致性,现实世界的一致性才是事务追求的最终目标。

    为了实现现实世界的一致性,需要满足如下几点:

    1. 保证原子性,持久性和隔离性,如果这些特征都无法保证,那么事务的一致性也无法保证;

    2. 数据库本身的约束,比如字符串长度不能超过列的限制或者唯一性约束;

    3. 业务层面同样需要进行保障 。

    2.5 事务特点

    我们通常称 Redis 为内存数据库 , 不同于传统的关系数据库,为了提供了更高的性能,更快的写入速度,在设计和实现层面做了一些平衡,并不能完全支持事务的 ACID。

    Redis 的事务具备如下特点:

    • 保证隔离性;

    • 无法保证持久性;

    • 具备了一定的原子性,但不支持回滚;

    • 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。

    从工程角度来看,假设事务操作中每个步骤需要依赖上一个步骤返回的结果,则需要通过 watch 来实现乐观锁 。

    3 Lua 脚本

    3.1 简介

    Lua 由标准 C 编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。Lua 脚本可以很容易的被 C/C ++ 代码调用,也可以反过来调用 C/C++ 的函数,这使得 Lua 在应用程序中可以被广泛应用。

    Lua 脚本在游戏领域大放异彩,大家耳熟能详的《大话西游II》,《魔兽世界》都大量使用 Lua 脚本。Java 后端工程师接触过的 api 网关,比如 Openresty ,Kong 都可以看到 Lua 脚本的身影。

    从 Redis 2.6.0 版本开始, Redis内置的 Lua 解释器,可以实现在 Redis 中运行 Lua 脚本。

    使用 Lua 脚本的好处 :

    • 减少网络开销。将多个请求通过脚本的形式一次发送,减少网络时延。

    • 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。

    • 复用。客户端发送的脚本会永久存在 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

    Redis Lua 脚本常用命令:

    序号命令及描述
    1EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。
    2EVALSHA sha1 numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。
    3SCRIPT EXISTS script [script ...] 查看指定的脚本是否已经被保存在缓存当中。
    4SCRIPT FLUSH 从脚本缓存中移除所有脚本。
    5SCRIPT KILL 杀死当前正在运行的 Lua 脚本。
    6SCRIPT LOAD script 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。

    3.2 EVAL 命令

    命令格式:

    1
     EVAL script numkeys key [key ...] arg [arg ...]

    说明:

    • script是第一个参数,为 Lua 5.1脚本;

    • 第二个参数numkeys指定后续参数有几个 key;

    • key [key ...],是要操作的键,可以指定多个,在 Lua 脚本中通过KEYS[1], KEYS[2]获取;

    • arg [arg ...],参数,在 Lua 脚本中通过ARGV[1], ARGV[2]获取。

    简单实例:

    1
     redis> eval "return ARGV[1]" 0 100 
    2
     "100"
    3
     redis> eval "return {ARGV[1],ARGV[2]}" 0 100 101
    4
     1) "100"
    5
     2) "101"
    6
     redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second
    7
     1) "key1"
    8
     2) "key2"
    9
     3) "first"
    10
     4) "second"

    下面演示下 Lua 如何调用 Redis 命令 ,通过redis.call()来执行了 Redis 命令 。

    1
     redis> set mystring 'hello world'
    2
     OK
    3
     redis> get mystring
    4
     "hello world"
    5
     redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring
    6
     "hello world"
    7
     redis> EVAL "return redis.call('GET','mystring')" 0
    8
     "hello world"

    3.3 EVALSHA 命令

    使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。

    思路是先将 Lua 脚本先缓存起来 , 返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。

    Redis事务模式和Lua脚本的原理是什么

    EVALSHA 命令基本语法如下:

    1
     redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...]

    实例如下:

    1
     redis> SCRIPT LOAD "return 'hello world'"
    2
     "5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
    3
     redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0
    4
     "hello world"

    4 事务 VS Lua 脚本

    从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。

    因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。

    不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。

    -- redis.io/

    Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。

    Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。

    不过在编写 Lua 脚本时,要注意如下两点:

    1. 为了避免 Redis 阻塞,Lua 脚本业务逻辑不能过于复杂和耗时;

    2. 仔细检查和测试 Lua 脚本 ,因为执行 Lua 脚本具备一定的原子性,不支持回滚。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    编程语言的编程模型
    编程语言的编程模型

    编程模型涵盖面向过程的C语言、面向对象的Ruby、基于栈的PostScript、基于规则的Prolog、基于原型的Lua、函数式的Erlang/Haskell及混合模型的Scala/Clojure。类型系统区分强类型与弱类型、动态类型与静态类型,元编程允许运行时操作代码,提升表达力。

    php72w redis,docker php7安装php-redis
    php72w redis,docker php7安装php-redis

    在Docker环境下为PHP安装Redis扩展:手动编译需下载源码、安装phpize、配置编译并安装,生成redis.so并在php.d目录新建redis.ini加载,重启PHP-FPM;也可直接yum安装php72w-pecl-redis,免手动编译。

    php 如何配置 redis,php 如何设置redis
    php 如何配置 redis,php 如何设置redis

    配置PHP的Redis扩展:先安装Redis服务,从GitHub下载phpredis源码,用phpize编译安装,修改php.ini添加extension=redis.so,重启php-fpm或Apache,通过phpinfo验证并连接Redis。

    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】
    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】

    C++结构体无法直接序列化为RESP协议,需手动将字段映射为*N数组嵌套$lenbulkstring形式。整数需转换为字符串并加:前缀,空字符串用$0\r\n,null用$-1\r\n。长度计算需精确到字节数,特别注意二进制安全,同时确保字段顺序与结构体定义一致。

    如何在Golang架构中部署Redict作为高能数据临时存储中转站
    如何在Golang架构中部署Redict作为高能数据临时存储中转站

    Redis作为跨服务临时状态枢纽,需按消息队列、缓存和原子状态三重角色设计。选型用List实现任务分发及延迟重试,Stream用于事件广播与位点回溯。go-redis/v8需配置MinIdleConns、MaxRetries及超时。数据写入应JSON序列化并带type标签、设合理TTL、将关键字段提至顶层。消费通过原子GET+DEL或processedkey

    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制
    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制

    在高并发秒杀场景下,利用Redis的减库存原子操作预扣减库存拦截超卖请求,再通过数据库乐观锁(版本号与库存校验)确保最终一致性,异常时回滚Redis库存并借助Lua脚本保证原子性,从而有效防止库存超卖。

    如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑
    如何在 Go 中利用 go-redis 执行 Lua 脚本优化逻辑

    在Go中使用go-redis执行Lua脚本可借助原子性解决竞态问题,通过redis.Script封装脚本并自动处理SHA1缓存与重试。需注意KEYS必须为非空切片且所有键在Cluster模式下落在同一slot,ARGV以字符串传入并由tonumber转换。返回值需先检查错误再调用Val(),避免panic。

    TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】
    TP6.0 基于 Redis SetNX 实现的简单互斥锁机制【并发】

    先看一个典型场景:电商秒杀、库存扣减、定时任务重复调度——这些并发冲突在 TP6 应用里屡见不鲜。用 Redis 的 SETNX 做互斥锁,确实是轻量又直接的方案,但“能用”和“稳妥”之间,隔着三层硬功夫:原子加锁、身份校验、合理超时。这三条,差一条就翻车。 很多人第一次上手,直接写 SETNX 加

    TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】
    TP6.0 如何处理高并发抢购?Redis 原子锁与队列削峰详解【秒杀】

    高并发抢购中,Redis的DECR原子操作可安全扣库存;配合think-queue异步削峰,接口仅扣库存和推消息;分布式锁需用SETNXEX加唯一标识,释放时用Lua脚本校验;成功资格后须以数据库事务双写为准,失败时补回Redis库存。

    Golang 实现基于 Redis 的延时任务队列
    Golang 实现基于 Redis 的延时任务队列

    基于Redis的ZSET实现延时队列需解决并发重复消费、失败重试混乱、精度不足及内存爆炸等问题。通过Lua脚本保证取删原子性,使用毫秒级时间戳,对临时错误采用指数退避重试,永久错误记入独立哈希表,并限制最大延迟、定期清理过期任务、使用紧凑序列化格式。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    即将离开本站
    您即将前往第三方网站,请确认是否继续?