重新认识一下redis 的lua脚本 和 事务

重新认识一下redis 的lua脚本 和 事务

前言

redis 的 lua 脚本大家都了解过或者用过吧。 lua 脚本执行具有原子性特点,使其在分布式锁、限流、库存扣减 等场景广泛使用!

redis 中 原子性 这一概念经常被错误的理解成 “要么全部执行,要么全都不执行”。以及很多同学把redis 中的事务 等同于数据库中的事务。

✔本篇文章就带大家理解一下redis 中的事务 和 lua 脚本有啥区别,以及命令执行过程发生错误会如何处理。同时也把 Redis Pipeline 带上一起分析了吧。

lua 脚本、事务、pipline 对比

基本概念

1. Redis Lua 脚本

Redis 从 2.6 版本开始支持 Lua 脚本,允许开发者将一系列 Redis 命令封装在一个 Lua 脚本中。Redis 会以原子方式执行这个脚本,在脚本执行期间,不会执行其他客户端的命令。这意味着脚本中的所有操作要么全部成功,要么全部失败。

2. Redis 事务

❗❗❗Redis 的事务不支持回滚,这个和数据库事务可回滚是有区别的

Redis 事务通过 MULTIEXECDISCARDWATCH 等命令实现。使用 MULTI 开启事务后,后续的命令会被放入队列,直到调用 EXEC 时,队列中的命令会被依次执行。WATCH 命令可以用于实现乐观锁,确保事务执行时被监视的键未被其他客户端修改。

3. Redis Pipeline

Pipeline(管道)是一种客户端优化技术,允许客户端将多个命令一次性发送给 Redis 服务器,服务器处理完这些命令后再一次性将结果返回给客户端。这样可以减少客户端与服务器之间的网络往返次数,提高性能。

✔原子性保证,执行中出错怎么处理

❗❗❗这里说的原子性,指的是不可拆分,不可中断的原子性操作。所以,需要注意的是,不管是Redis的事务还是Lua,都没办法回滚,一旦执行过程中有命令失败了,都是不支持回滚的。

1. Lua 脚本

具有原子性。在脚本执行过程中,不会被其他客户端的命令打断。

如果脚本执行过程中出现错误,整个脚本的执行会停止,且没有回滚机制,已执行的部分操作不会被撤销

示例 :

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

在这个脚本中,incr 命令会报错,脚本执行会停止,set key2 value2 不会执行,但 set key1 value1 已经生效。

2. 事务

Redis 事务保证了命令执行的原子性,执行过程,不会被其他客户端的命令插入。

如果某个命令出错,其他命令仍会继续执行,同样没有回滚机制。不过,如果在命令入队时出现语法错误(执行之前),整个事务会失败

执行之前是在什么时候:
命令可能排队失败,因此在调用 EXEC 之前可能会出现错误。例如,命令可能在语法上是错误的(错误的参数数量、错误的命令名称等),或者可能存在一些关键情况,如内存不足情况(如果服务器使用 maxmemory 指令配置为具有内存限制)

示例:

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

执行结果中,SET 命令成功,INCR 命令报错,但事务不会回滚 SET 操作。

3. 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());
        }
    }

在这个 Pipeline 中,get('nonexistent_key') 可能会失败,但 setincr 命令依然会正常执行。

性能表现

1. Lua 脚本

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

2. 事务

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

3. Pipeline

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

适用场景

1. Lua 脚本

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

2. 事务

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

3. Pipeline

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

总结

主要描述了Redis 事务、lua脚本、pipeline 执行过程中出错是如何处理的。以及 对redis 的原子操作以及事务的概念做了阐述,区别于数据库事务。

记住 redis 事务 、 lua脚本在执行期间出错,不可回滚❗❗❗

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT枫斗者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值