Redis 事务

Redis 事务允许将多个命令打包,然后一次性、按顺序地执行。Redis 事务的实现主要通过以下几个命令:MULTIEXECDISCARDWATCH

Redis 事务特点:

  1. 原子性(Atomicity):事务中的所有命令都会被序列化并连续执行,事务在执行过程中不会被其他客户端发送来的命令请求所中断。

  2. 没有隔离级别的概念:在事务执行期间,事务中的命令不会看到其他客户端对数据库所作的变更。

  3. 不保证持久性和一致性:如果服务器在执行事务过程中崩溃,即使部分命令已经被执行,事务也不会被持久化;如果事务中的某个命令失败,其他命令仍然会被执行。

事务的工作流程:

  1. 开始事务 :使用 MULTI 命令开始一个事务。

  2. 命令入队 :在 MULTI 之后输入的所有命令不会立即执行,而是被放入一个队列,客户端会收到一个回复表示命令已成功入队。

  3. 执行事务 :通过 EXEC 命令执行所有队列中的命令。如果事务中的任何命令执行失败,Redis 仍将继续执行事务队列中的剩余命令。事务执行完毕后,会一次性将事务产生的所有结果返回给客户端。

  4. 取消事务 :使用 DISCARD 命令可以取消事务,这将清空事务队列并退出事务。

  5. 乐观锁WATCH 命令可以在事务开始之前监视一个或多个键,在执行 EXEC 命令时,如果任何被监视的键被修改,那么整个事务都会被取消。

源码解析:

Redis的事务功能主要体现在multi.c 文件中,该文件包含了处理事务命令的相关函数。Redis 使用一个叫做 multiState 的结构体来记录事务状态,包括命令队列和被 WATCH 命令监视的键。

当调用 MULTI 命令时,Redis会为当前客户端初始化一个 multiState 结构体,并将其标记为处于事务状态。在事务状态中,客户端发来的命令会被添加到 multiState 的命令队列中而不会被执行。

当调用 EXEC 命令时,Redis会按顺序执行 multiState 的命令队列中的所有命令,并将结果发送给客户端。如果之前有过 WATCH,并且监视的键发生了变化,则事务会被打断,客户端收到空回复表示事务执行失败。

Java代码演示:

以下是使用 Jedis 客户端库在 Java 中操作 Redis 事务的示例:

java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class RedisTransactionExample {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("localhost", 6379);

        // 乐观锁:监视一个key
        jedis.watch("key");

        // 开始事务
        Transaction t = jedis.multi();
        try {
            // 事务中的操作
            t.set("key", "value");
            t.incr("counter");

            // 提交事务
            t.exec();

        } catch (Exception e) {
            // 事务出错,取消事务
            t.discard();
        } finally {
            jedis.unwatch(); // 移除监视
            jedis.close();
        }
    }
}

在这个例子中,我们首先通过 jedis.watch("key") 对 key 进行监视,然后我们通过 jedis.multi() 开始事务,并向事务中添加了一些命令。通过 t.exec() 提交事务,如果在执行 execkey 的值被其他客户端修改了,则事务会被取消,exec 返回 null。如果事务中途出现异常,我们使用 t.discard() 取消事务。

注意事项:

  • Redis事务不支持回滚,即使事务中的某些命令失败,事务也不会回滚之前已执行的命令。
  • Redis 事务中,即使命令语法错误,也会在 EXEC 时全部执行其他命令,错误的命令会导致事务失败。
  • 使用乐观锁时,如果其他客户端修改了被监视的键,事务将不会执行任何命令。

Redis事务虽然具有原子性,但它并不完全等同于传统关系型数据库中的事务。了解其工作原理和限制对于正确使用Redis事务至关重要。

相关推荐
MC皮蛋侠客8 小时前
Redis 系列(一):全景与最小闭环——从 `SET` 命令到内存数据结构
数据结构·数据库·redis
渣渣盟11 小时前
当 Redis 写入成为性能瓶颈时,如何利用 异步批量 Sink 将吞吐量从 1w QPS 提升到 10w+?
数据库·redis·php
MC皮蛋侠客13 小时前
Redis 系列(六):过期、内存淘汰与内存优化——让 Redis 活得更久
数据库·redis·bootstrap
MC皮蛋侠客15 小时前
Redis 系列(七):事务、Pipeline 与 Lua 脚本——从原子性到 Functions
数据库·redis·lua
MC皮蛋侠客16 小时前
Redis 系列(八):缓存设计模式与一致性——从 Cache Aside 到防雪崩
redis·缓存·设计模式
渣渣盟16 小时前
当 Redis 写入不再是瓶颈后,Flink 任务的反压可能来自哪里?如何系统性地定位和解决 Flink 反压问题?
数据库·redis·flink
仍然.16 小时前
服务端高并发分布式结构演进之路
大数据·数据库·redis
MC皮蛋侠客16 小时前
Redis 系列(三):底层实现(一)——对象系统、SDS 与 dict
数据库·redis·缓存
渣渣盟17 小时前
当 Redis 集群发生主从切换(Failover)时,Flink 任务会崩溃吗?如何利用 Sentinel 实现高可用?
redis·flink·sentinel
海上小飞龙1 天前
Redis 分布式锁原理:从 SET NX EX 到 Redisson 看门狗
数据库·redis·分布式