详解Redis中lua脚本和事务

devtools/2025/2/12 17:54:27/

In learning knowledge, one should be good at thinking, thinking, and thinking again.

                                                                                               —-Albert Einstein

引言

Lua脚本的原子性和事务的ACID特性想必大家都很熟悉,本篇文章将从性能表现和原理帮助我们快速理解他们

基本概念

1. Redis Lua 脚本

  • 从 2.6 版本起,Redis 开始支持 Lua 脚本。开发者能够将一系列 Redis 命令封装于一个 Lua 脚本内,Redis 会以原子方式执行该脚本。在脚本执行的过程中,不会执行其他客户端的命令,即脚本中的所有操作要么全部成功,要么全部失败。常见的我们会基于Lua脚本实现限流器,或者很多游戏脚本也用Lua实现

2. Redis 事务

  • Redis 的事务通过 MULTI、EXEC、DISCARD 和 WATCH 等命令来实现。当使用 MULTI 开启事务后,后续的命令会被放入队列,直到调用 EXEC 时,队列中的命令才会依次执行。WATCH 命令可用于实现乐观锁,以确保事务执行时被监视的键未被其他客户端修改。需要特别注意的是,Redis 的事务不支持回滚,这与数据库事务可回滚的特性有所不同。

3. Redis Pipeline

  • Pipeline 是一种客户端优化技术,它允许客户端将多个命令一次性发送给 Redis 服务器,服务器处理完这些命令后再一次性将结果返回给客户端。这样做可以有效减少客户端与服务器之间的网络往返次数,从而提高性能。Pipeline类似于批处理方式,优化了与Redis服务器沟通的次数

性能表现

1. Lua 脚本

  • Lua 脚本将多个操作封装在一个脚本中,减少了客户端与服务器之间的网络往返次数,并且脚本在服务器端执行,减少了命令解析的次数,通常在处理复杂逻辑时性能更优。

2. 事务

  • 事务也能减少网络往返次数,因为多个命令可以一次性发送。但事务中的每个命令都需要在服务器端进行解析和执行,在处理大量命令时可能会有一定性能开销。

3. Pipeline

  • Pipeline 主要通过减少网络往返时间来提高性能。对于大量独立的命令,使用 Pipeline 可以显著提高执行效率。但由于命令之间没有原子性保证,在需要保证数据一致性的场景下可能不适用。

三者的特性和开销也就决定了他们将适用于不同的场景

使用场景(实战错误处理)

1. Lua 脚本

  • 适用于需要实现复杂业务逻辑、保证操作原子性的场景,如分布式锁、限流、库存扣减等。通过 Lua 脚本可以将多个操作封装在一起,避免了多次网络交互和并发问题。

  • Lua 脚本具有原子性,在脚本执行过程中,不会被其他客户端的命令打断。若脚本执行过程中出现错误,整个脚本的执行会停止,并且没有回滚机制,已执行的部分操作不会被撤销。

    redis.call('set', 'key1', 'value1')
    redis.call('incr', 'key1')  -- 这里会报错,因为 key1 是字符串
    redis.call('set', 'key2', 'value2')

2. 事务

  • 适用于简单的批量操作,需要保证一组命令原子执行的场景,如同时更新多个相关的键值对。同时,WATCH 命令可以用于实现乐观锁,处理并发更新的情况。

  • Redis 事务保证了命令执行的原子性,执行过程不会被其他客户端的命令插入。如果某个命令出错,其他命令仍会继续执行,同样没有回滚机制。不过,若在命令入队时出现语法错误(执行之前),整个事务会失败。

    MULTI
    SET key1 value1
    INCR key1  -- 对字符串执行 INCR 会在执行时出错
    EXEC

3. Pipeline

  • 适用于需要批量执行大量独立命令的场景,如批量插入数据、批量查询数据等。通过 Pipeline 可以提高操作效率,减少网络延迟的影响。

  • Pipeline 不保证原子性,它只是将多个命令批量发送,服务器按顺序执行这些命令,且这些命令之间相互独立。一个命令执行失败不会影响其他命令的执行

    public static void main(String[] args) {// 创建 Jedis 实例并连接到 Redis 服务器try (Jedis jedis = new Jedis("localhost", 6379)) {// 创建 Pipeline 对象Pipeline pipeline = jedis.pipelined();// 向 Pipeline 中添加命令pipeline.set("key1", "value1");Response<String> getResponse = pipeline.get("nonexistent_key");Response<Long> incrResponse = pipeline.incr("counter");// 执行 Pipeline 中的所有命令List<Object> results = pipeline.syncAndReturnAll();// 打印执行结果System.out.println(results);// 也可以通过 Response 对象单独获取每个命令的结果System.out.println("Get result: " + getResponse.get());System.out.println("Incr result: " + incrResponse.get());}
    }


http://www.ppmy.cn/devtools/158276.html

相关文章

2526考研资料分享 百度网盘

通过网盘分享的文件&#xff1a;01、2026【考研数学】 链接:https://pan.baidu.com/s/1PwMzp_yCYqjBqa7492mP3w?pwd98wg 提取码:98wg--来自百度网盘超级会员v3的分享 通过网盘分享的文件&#xff1a;01、2026【考研政治】 链接:https://pan.baidu.com/s/1PwMzp_yCYqjBqa7492…

flutter isolate到底是啥

在 Flutter 中&#xff0c;Isolate 是一种实现多线程编程的机制&#xff0c;下面从概念、工作原理、使用场景、使用示例几个方面详细介绍&#xff1a; 概念 在 Dart 语言&#xff08;Flutter 开发使用的编程语言&#xff09;里&#xff0c;每个 Dart 程序至少运行在一个 Isol…

【Pytorch实战教程】让数据飞轮转起来:PyTorch Dataset与Dataloader深度指南

文章目录 让数据飞轮转起来:PyTorch Dataset与Dataloader深度指南一、为什么需要数据管理组件?二、Dataset:数据集的编程接口2.1 自定义Dataset三要素2.2 实战案例:图像分类数据集三、Dataloader:高效数据流水线3.1 核心参数解析3.2 数据流可视化3.3 多卡训练支持四、综合…

【C++高并发服务器WebServer】-17:阻塞/非阻塞和同步/异步、五种IO模型、Web服务器

本文目录 一、阻塞/非阻塞、同步/异步1.1 辨析1.2 异步io接口 二、五种IO模型2.1 阻塞 blocking 模型2.2 非阻塞 NIO 模型2.3 IO多路复用2.4 信号驱动Signal-driven2.5 异步 三、Web Sever 网页服务器3.1 HTTP的请求响应步骤3.2 HTTP请求与响应报文格式3.3 HTTP请求方法3.4 HTT…

element-plus 解决el-dialog背后的页面滚动问题,及其内容有下拉框出现错位问题

这个问题通常是因为 el‑dialog 默认会锁定 body 的滚动&#xff08;通过给 body 添加隐藏滚动条的样式&#xff09;&#xff0c;从而导致页面在打开对话框时跳转到顶部。解决方法是在使用 el‑dialog 时禁用锁定滚动功能。 <el-dialogv-model"dialogVisible":lo…

数据结构 单链表的模拟实现

一、链表的定义 线性表的链式存储就是链表。 它是将元素存储在物理上任意的存储单元中&#xff0c;由于⽆法像顺序表⼀样通过下标保证数据元素之间的逻辑关系&#xff0c;链式存储除了要保存数据元素外&#xff0c;还需额外维护数据元素之间的逻辑关系&#xff0c;这两部分信息…

VideoWorld技术在智能货柜商品识别与数量统计的总结

&#x1f31f; “VideoWorld” 模型仅凭视觉信息即可实现知识学习&#xff0c;不依赖语言模型。 &#x1f916; 模型在围棋和机器人模拟任务中展现出卓越的推理和规划能力。 一、技术实现核心 生成式数据增强 功能&#xff1a;通过VideoWorld生成多样化的合成数据&#xff08;…

【C语言】球球大作战游戏

目录 1. 前期准备 2. 玩家操作 3. 生成地图 4. 敌人移动 5. 吃掉小球 6. 完整代码 1. 前期准备 游戏设定:小球的位置、小球的半径、以及小球的颜色 这里我们可以用一个结构体数组来存放这些要素,以方便初始化小球的信息。 struct Ball {int x;int y;float r;DWORD c…