Redis 从入门到精通之Redis事务实现原理

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,182元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介: Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务功能,本章首先讨论使用 MULTI 、 DISCARD 和 EXEC 三个命令实现的一般事务,然后再来讨论带有 WATCH 的事务的实现。因为事务的安全性也非常重要,所以本章最后通过常见的 ACID 性质对 Redis 事务的安全性进行了说明

Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务功能,本章首先讨论使用 MULTI 、 DISCARD 和 EXEC 三个命令实现的一般事务,然后再来讨论带有 WATCH 的事务的实现。

0.前言

因为事务的安全性也非常重要,所以本章最后通过常见的 ACID 性质对 Redis 事务的安全性进行了说明
好的,下面给出 Redis 事务相关命令的示例:

0.Redis 事务基本概念

Redis 事务是一组命令的集合,这些命令会被作为一个整体执行。事务中的所有命令要么全部执行,要么全部不执行,这样可以保证事务的原子性。在执行事务期间,其他客户端发送的命令不会被执行,这样可以保证事务的隔离性。

1.事务详解

1.1. 开始事务

使用 MULTI 命令可以开始一个事务。该命令会将客户端的状态从非事务状态切换为事务状态。
使用 MULTI 命令可以开始一个事务:

127.0.0.1:6379> MULTI
OK

1.2. 命令入队

在事务状态下,客户端发送的所有命令都不会立即执行,而是先被放入一个队列中,等到 EXEC 命令被调用时,所有命令会被依次执行。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 value1
QUEUED
127.0.0.1:6379> INCR key2
QUEUED
127.0.0.1:6379> MGET key1 key2
QUEUED

image.png

1.3. 执行事务

image.png

使用 EXEC 命令可以执行事务中的所有命令。如果事务中的任何一个命令执行失败,整个事务会被回滚。

127.0.0.1:6379> EXEC
1) OK
2) (integer) 1
3) 1) "value1"
   2) "1"

1.4. 在事务和非事务状态下执行命令

在事务状态下,客户端发送的所有命令都不会立即执行,而是被放入队列中。在非事务状态下,发送的命令会立即执行。

127.0.0.1:6379> SET key1 value1
OK

1.5. 事务状态下的 DISCARD 、 MULTI 和 WATCH 命令

  • DISCARD 命令:用于取消事务,清空事务队列。
    MULTI 命令:用于开始一个事务。
    WATCH 命令:用于监视一个或多个键,如果这些键在事务执行之前被其他客户端修改,事务会被回滚。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 value1
QUEUED
127.0.0.1:6379> DISCARD
OK
  • MULTI 命令:用于开始一个事务。
127.0.0.1:6379> MULTI
OK
  • WATCH 命令:用于监视一个或多个键,如果这些键在事务执行之前被其他客户端修改,事务会被回滚。
127.0.0.1:6379> WATCH key1
OK

1.6. 带 WATCH 的事务

带 WATCH 的事务可以保证事务执行期间,监视的键不会被其他客户端修改。如果任何一个监视的键被其他客户端修改,事务会被回滚。在 WATCH 命令和 EXEC 命令之间,客户端可以向监视的键发送任意数量的命令,这些命令会被入队,但不会立即执行。
image.png

127.0.0.1:6379> WATCH key1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 value1
QUEUED
127.0.0.1:6379> EXEC
(nil)

1.7. WATCH 命令的实现

Redis 中的 WATCH 命令使用乐观锁实现。在执行事务之前,Redis 会记录监视的键的值,当事务执行时,Redis 会再次检查这些键的值是否发生变化。如果变化了,事务就会被回滚。。

1.8. WATCH 的触发

当执行 EXEC 命令时,Redis 会检查所有监视的键是否发生变化。如果没有变化,事务会继续执行。如果发生变化,事务会被回滚

1.9. 事务的 ACID 性质

Redis 事务具有 ACID 性质,即原子性、一致性、隔离性和持久性。

  • 原子性(Atomicity):事务中的所有命令要么全部执行,要么全部不执行,这样可以保证事务的原子性。
  • 一致性(Consistency):事务执行前后,数据库的数据应该保持一致,即事务执行前后的数据状态应该满足一定的约束关系。
  • 隔离性(Isolation):在事务执行期间,其他客户端发送的命令不会被执行,这样可以保证事务的隔离性。
  • 持久性(Durability):事务执行完成后,数据应该持久化到磁盘中,这样可以保证事务的持久性。

下面是一个包含 WATCH 命令的事务示例:

127.0.0.1:6379> SET key1 10
OK
127.0.0.1:6379> SET key2 5
OK
127.0.0.1:6379> WATCH key1 key2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR key1
QUEUED
127.0.0.1:6379> INCR key2
QUEUED
127.0.0.1:6379> EXEC
(nil)

在这个例子中,首先设置了 key1 和 key2 的值。接下来,使用 WATCH 命令监视 key1 和 key2。然后,使用 MULTI 命令开始一个事务,将对 key1 和 key2 的操作入队。在 EXEC 命令执行之前,另一个客户端修改了 key2 的值,这导致事务被回滚。

需要注意的是,Redis 事务并不是真正的 ACID 事务,因为 Redis 的事务无法保证隔离性。在一个事务执行期间,其他客户端发送的命令不会被执行,但是如果这些命令是针对已经入队的事务命令所涉及的键,它们仍然会修改这些键的值,并且这些修改可能会影响到事务的执行结果。因此,在使用 Redis 事务时,需要注意这一点

2.代码实践

我们分别用jedis 和 Spring Boot 实现 redis 事务

1. jedis

(1)pom.xml 文件依赖:

<dependencies>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.6.0</version>
    </dependency>
</dependencies>

(2)代码示例:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

public class JedisTransactionDemo {
   
    public static void main(String[] args) {
   
        Jedis jedis = new Jedis("localhost", 6379);
        Transaction transaction = jedis.multi(); // 开启事务
        try {
   
            transaction.set("name", "张三");
            transaction.set("age", "18");
            transaction.exec(); // 提交事务
        } catch (Exception e) {
   
            transaction.discard(); // 回滚事务
        } finally {
   
            jedis.close(); // 关闭连接
        }
    }
}
  1. Spring Boot 实现 redis 事务

(1)pom.xml 文件依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
</dependencies>

(2)application.properties 配置:

# Redis 配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0

(3)代码示例:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Component;

@Component
public class RedisTransactionDemo {
   
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public void test() {
   
        redisTemplate.execute(new SessionCallback<Object>() {
   
            @Override
            public Object execute(RedisOperations ops) throws DataAccessException {
   
                ops.multi(); // 开启事务
                try {
   
                    ops.opsForValue().set("name", "张三");
                    ops.opsForValue().set("age", "18");
                    ops.exec(); // 提交事务
                } catch (Exception e) {
   
                    ops.discard(); // 回滚事务
                }
                return null;
            }
        });
    }
}

以上就是 jedis 和 Spring Boot 实现 redis 事务的示例工程,可以根据实际需求进行修改和调整。

3.总结

3.1. 在事务和非事务状态下

  1. 无论在事务状态下,还是在非事务状态下,Redis 命令都由同一个函数执行,所以它们共享很多服务器的一般设置,比如 AOF 的配置、RDB 的配置,以及内存限制,等等。

  2. 不过事务中的命令和普通命令在执行上还是有一点区别的,其中最重要的两点是:
    非事务状态下的命令以单个命令为单位执行,前一个命令和后一个命令的客户端不一定是同一个;

  3. 事务状态则是以一个事务为单位,执行事务队列中的所有命令:除非当前事务执行完毕,否则服务器不会中断事务,也不会执行其他客户端的其他命令。

  4. 在非事务状态下,执行命令所得的结果会立即被返回给客户端;
    而事务则是将所有命令的结果集合到回复队列,再作为 EXEC 命令的结果返回给客户端。

  5. 事务状态下的 DISCARD 、 MULTI 和 WATCH 命令
    除了 EXEC 之外,服务器在客户端处于事务状态时,不加入到事务队列而直接执行的另外三个命令是 DISCARD 、 MULTI 和 WATCH 。
    DISCARD 命令用于取消一个事务,它清空客户端的整个事务队列,然后将客户端从事务状态调整回非事务状态,最后返回字符串 OK 给客户端,说明事务已被取消。

  6. Redis 的事务是不可嵌套的,当客户端已经处于事务状态,而客户端又再向服务器发送 MULTI 时,服务器只是简单地向客户端发送一个错误,然后继续等待其他命令的入队。MULTI 命令的发送不会造成整个事务失败,也不会修改事务队列中已有的数据。

  7. WATCH 只能在客户端进入事务状态之前执行,在事务状态下发送 WATCH 命令会引发一个错误,但它不会造成整个事务失败,也不会修改事务队列中已有的数据(和前面处理 MULTI 的情况一样)。

3.2. 小结

Redis 事务是一组命令的集合,具有原子性、一致性、隔离性和持久性的 ACID 特性。事务中的所有命令要么全部执行,要么全部不执行,可以保证事务的原子性。在事务执行期间,其他客户端发送的命令不会被执行,可以保证事务的隔离性。在执行事务之前,可以使用 WATCH 命令监视一个或多个键,如果这些键在事务执行之前被其他客户端修改,事务会被回滚。事务的 ACID 性质可以保证数据的一致性和可靠性。

目录
相关文章
|
缓存 NoSQL Redis
Redis 事务
10月更文挑战第18天
135 1
|
存储 缓存 NoSQL
Redis 服务器全方位介绍:从入门到核心原理
Redis是一款高性能内存键值数据库,支持字符串、哈希、列表等多种数据结构,广泛用于缓存、会话存储、排行榜及消息队列。其单线程事件循环架构保障高并发与低延迟,结合RDB和AOF持久化机制兼顾性能与数据安全。通过主从复制、哨兵及集群模式实现高可用与横向扩展,适用于现代应用的多样化场景。合理配置与优化可显著提升系统性能与稳定性。
350 0
|
3月前
|
监控 NoSQL 关系型数据库
Redis:事务(Transactions)
Redis事务支持将多个命令打包执行,但与MySQL不同,它不保证原子性、一致性、持久性和隔离性。Redis事务的核心在于“打包”命令,避免其他客户端插队,通过MULTI、EXEC、DISCARD等命令实现。此外,Redis提供WATCH和UNWATCH机制,用于监控键变化,实现类似“乐观锁”的功能,提升并发操作的安全性。
|
3月前
|
存储 缓存 监控
Redis分区的核心原理与应用实践
Redis分区通过将数据分散存储于多个节点,提升系统处理高并发与大规模数据的能力。本文详解分区原理、策略及应用实践,涵盖哈希、范围、一致性哈希等分片方式,分析其适用场景与性能优势,并探讨电商秒杀、物联网等典型用例,为构建高性能、可扩展的Redis集群提供参考。
185 0
|
10月前
|
消息中间件 缓存 NoSQL
Redis原理—5.性能和使用总结
本文详细探讨了Redis的阻塞原因、性能优化、缓存相关问题及数据库与缓存的一致性问题。同时还列举了不同缓存操作方案下的并发情况,帮助读者理解并选择合适的缓存管理策略。最终得出结论,在实际应用中应尽量采用“先更新数据库再删除缓存”的方案,并结合异步重试机制来保证数据的一致性和系统的高性能。
Redis原理—5.性能和使用总结
|
10月前
|
NoSQL 算法 安全
Redis原理—1.Redis数据结构
本文介绍了Redis 的主要数据结构及应用。
Redis原理—1.Redis数据结构
|
10月前
|
缓存 NoSQL Redis
Redis原理—2.单机数据库的实现
本文概述了Redis数据库的核心结构和操作机制。
Redis原理—2.单机数据库的实现
|
10月前
|
存储 缓存 NoSQL
Redis原理—4.核心原理摘要
Redis 是一个基于内存的高性能NoSQL数据库,支持分布式集群和持久化。其网络通信模型采用多路复用监听与文件事件机制,通过单线程串行化处理大量并发请求,确保高效运行。本文主要简单介绍了 Redis 的核心特性。
|
10月前
|
缓存 NoSQL Redis
Redis原理—3.复制、哨兵和集群
详细介绍了Redis的复制原理、哨兵原理和集群原理。
|
10月前
|
运维 NoSQL 算法
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理
本文深入探讨了基于Redis实现分布式锁时遇到的细节问题及解决方案。首先,针对锁续期问题,提出了通过独立服务、获取锁进程自己续期和异步线程三种方式,并详细介绍了如何利用Lua脚本和守护线程实现自动续期。接着,解决了锁阻塞问题,引入了带超时时间的`tryLock`机制,确保在高并发场景下不会无限等待锁。最后,作为知识扩展,讲解了RedLock算法原理及其在实际业务中的局限性。文章强调,在并发量不高的场景中手写分布式锁可行,但推荐使用更成熟的Redisson框架来实现分布式锁,以保证系统的稳定性和可靠性。
574 0
【📕分布式锁通关指南 04】redis分布式锁的细节问题以及RedLock算法原理

热门文章

最新文章