Redis 从入门到精通【进阶篇】之Redis事务详解

news2024/11/16 9:28:23

文章目录

  • 0.前言
    • 1.Redis 事务基本流程
  • 1.事务详解
    • 1.1. 开始事务
    • 1.2. 命令入队
    • 1.3. 执行事务
    • 1.6. 带 WATCH 的事务
    • 1.7. WATCH 命令的实现
    • 1.8. WATCH 的触发
    • 1.9. 事务的 ACID 性质
  • 2.总结
    • 2.1. 在事务和非事务状态下
    • 2.2. 小结
    • 2.3. 为什么Redis 的事务并不是真正的原子操作
    • 2.4. 为什么客户端崩溃等异常情况,Redis 无法自动回滚事务
  • 3. Redis从入门到精通系列文章

在这里插入图片描述

0.前言

Redis 事务是一种将多个命令打包在一起执行的机制,可以保证这些命令的原子性,即要么全部执行成功,要么全部执行失败。Redis 事务采用了乐观锁的方式实现,具体实现原理如下:

  1. 开启事务

在客户端执行 MULTI 命令时,Redis 会将该客户端标记为事务状态。此时,客户端发送的所有命令都会被暂存到一个事务队列中,而不是立即执行。

  1. 执行事务

在客户端执行 EXEC 命令时,Redis 会将客户端状态从事务状态中移除,并依次执行事务队列中的所有命令。在执行期间,Redis 会将事务队列中的命令全部执行完毕,而不会中途中断。如果在执行期间有任何错误发生,Redis 会将错误信息返回给客户端,同时回滚事务。

  1. 回滚事务

如果在执行事务期间发生错误,Redis 会将错误信息返回给客户端,并回滚事务。回滚事务的方式是将客户端之前执行的所有写命令全部撤销,恢复到事务开始之前的状态。

Redis 的事务并不是真正的原子操作,因为事务队列中的命令在执行期间并没有被锁定。如果多个客户端同时执行事务,并且这些事务涉及相同的关键数据,那么就有可能出现数据竞争的情况,从而导致数据不一致。因此,在实际应用中,需要根据具体业务需求进行评估和选择。
Redis 通过 MULTI 、 DISCARD 、 EXEC 和 WATCH 四个命令来实现事务功能,本章首先讨论使用 MULTI 、 DISCARD 和 EXEC 三个命令实现的一般事务,然后再来讨论带有 WATCH 的事务的实现。

1.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 事务的示例工程,可以根据实际需求进行修改和调整。

2.总结

2.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 的情况一样)。

2.2. 小结

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

2.3. 为什么Redis 的事务并不是真正的原子操作

学完本章节之后我们尝试回答一下这个问题。
Redis 的事务并不是真正的原子操作,主要有以下几个原因:

  1. Redis 的事务是基于乐观锁实现的,不会对任何关键数据进行加锁。在事务执行期间,如果有其他客户端对同样的关键数据进行了修改,那么事务就有可能无法成功。这种情况下,Redis 会回滚整个事务,并返回错误信息。因此,Redis 的事务并不能像数据库事务那样保证绝对的原子性。

  2. Redis 的事务在执行期间不会对事务队列中的命令进行锁定,而是在执行期间监控关键数据的变化情况。如果在事务执行期间关键数据发生了变化,Redis 会回滚事务,并返回错误信息。但是,如果在事务执行期间发生了网络故障或者客户端崩溃等异常情况,Redis 无法做到回滚事务,从而可能导致数据不一致。

虽然Redis 的事务不是真正的原子操作,但是对于大部分应用场景来说,Redis 的事务已经足够满足业务需求。需要注意的是,在使用Redis 事务时,需要注意事务的正确性和可靠性,避免出现数据不一致的情况。

2.4. 为什么客户端崩溃等异常情况,Redis 无法自动回滚事务

  1. 客户端崩溃等异常情况可能会导致 Redis 事务的执行过程中断,从而无法自动回滚事务。具体来说,当客户端执行 MULTI 命令开启事务后,所有的命令并没有立即被执行,而是暂存到一个事务队列中。当客户端执行 EXEC 命令来提交事务时,Redis 会依次执行事务队列中的所有命令。
  2. 如果在事务执行期间发生了网络故障或者客户端崩溃等异常情况,Redis 无法自动提交事务,因此也就无法自动回滚事务。在这种情况下,需要手动执行相应的命令来撤销已经执行的命令,从而实现事务回滚。具体来说,可以使用 DISCARD 命令来撤销事务中尚未执行的所有命令,或者使用相反的命令来撤销已经执行的命令。

3. Redis从入门到精通系列文章

《Redis从入门到精通【进阶篇】之对象机制详解》
《Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解》
《Redis从入门到精通【进阶篇】之持久化 AOF详解》
《Redis从入门到精通【进阶篇】之持久化RDB详解》
《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》
在这里插入图片描述
大家好,我是冰点,今天的Redis 从入门到精通【进阶篇】之Redis事务详解,全部内容就是这些。如果你有疑问或见解可以在评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/730808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TCP连接管理(三次握手,四次挥手)

目录 一、回顾一下TCP包头二、连接的建立——“三次握手”三、连接的建立——“四次挥手”保活计时器 一、回顾一下TCP包头 源端口号&#xff08;Source Port&#xff09;&#xff1a;16 位字段&#xff0c;表示发送方的端口号。 目的端口号&#xff08;Destination Port&…

【机器学习核心总结】什么是决策树

什么是决策树 在游戏中遇到敌人是选择攻击还是逃跑&#xff1f;如果选择攻击&#xff0c;是选择普通的物理攻击还是魔法攻击&#xff1f;为达到目标根据一定的条件进行选择的过程&#xff0c;就是决策树(DT Tree)。 决策树模型非常经典&#xff0c;在机器学习中常被用于分类&…

2.3 Web应用 -- 5. Web缓存/代理服务器技术

2.3 Web应用 -- 5. Web缓存/代理服务器技术 Web缓存/代理服务器技术条件性GET方法 Web缓存/代理服务器技术 功能 在不访问服务器的前提下满足客户端的HTTP请求。 为什么要发明这种技术&#xff1f; 缩短客户请求的响应时间减少机构/组织的流量在大范围内(Internet)实现有效的内…

【小沐学C++】libcurl实现HTTP/HTTPS请求

文章目录 1、简介2、下载和编译2.1 下载2.2 编译2.3 使用 3、命令行测试3.1 获取文件头Headers3.2 请求内容Request Content3.3 响应内容Response Content3.4 GET请求3.5 POST请求3.6 其他 4、代码测试3.1 simple.c3.2 url2file.c3.3 simplepost.c3.4 resolve.c3.5 progressfun…

Docker中部署Redis集群与部署微服务项目的详细过程

目录 一、使用Docker部署的好处二、Docker 与 Kubernetes 对比三、Redis集群部署实战四、Spring Boot项目 打包镜像?小结 一、使用Docker部署的好处 Docker的好处在于&#xff1a;在不同实例上运行相同的容器 Docker的五大优点&#xff1a; 持续部署与测试、多云服务平台支…

一、枚举类型——新特性(模式匹配-支配性)

switch 中 case 语句的顺序很重要。如果基类先出现&#xff0c;就会支配任何出现在后面的 case&#xff1a; Dominance.java JDK 17 sealed interface Base { }record Derived() implements Base { }public class Dominance {static String test(Base base) {return switch (ba…

稳扎稳打学爬虫09—chromedriver下载与安装方法

chromedriver下载与安装方法 1. 获取chromedriver.exe2. 将chromedriver.exe 应用程序复制到浏览器的安装目录下3. 将chromedriver.exe 应用程序复制到python安装目录下4.进行测试5.有可能的报错 1. 获取chromedriver.exe http://chromedriver.storage.googleapis.com/index.h…

SpringBoot整合shiro项目完成认证功能

springboot整合shiro完成认证功能 一、准备阶段&#x1f95d; 1.创建springboot工程&#x1f353; 2.引入依赖&#x1f353; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi&…

Visual Studio Code 编辑器实用插件简介

Visual Studio Code 编辑器插件 以下是一些常用的 Visual Studio Code 编辑器插件及其简短描述&#xff1a; 2gua.rainbow-brackets&#xff1a;在括号周围添加彩虹色的边框&#xff0c;以帮助区分不同层次的括号。adpyke.codesnap&#xff1a;将代码片段转换为漂亮的图片&am…

第66篇:顶级APT后门Sunburst通信流量全过程复盘分析(修正篇)

Part1 前言 由于先前文章存在部分错误&#xff0c;原文ABC_123已删除&#xff0c;上周末把文章修正&#xff0c;重新发布。 大家好&#xff0c;我是ABC_123。前面几周分享了Solarwinds供应链攻击事件的详细攻击流程及Sunburst后门的设计思路&#xff0c;但是多数朋友还是对Sun…

各种软件的启动界面(SplashScreen)修改汇总

最近装了新电脑&#xff0c;搞了一波萌化&#xff0c;顺便把我常用软件的启动界面也给换了&#xff0c;包括adobe全家桶、UE、3dsMax、Maya、JB家的几个&#xff0c;&#xff08;Office想换没换成功&#xff0c;找了很久没找到方法&#xff0c;不知道有没大佬知道&#xff09; …

在阿里云上部署Springboot项目

文章目录 环境准备1.安装jdk2.安装mysql3.开启端口 上传项目1.数据库上传2.项目上传 环境准备 1.安装jdk 查看系统中原来是否含有java环境 rpm -qa |grep java rpm -qa |grep jdk rpm -qa |grep gcj其中&#xff0c;gcj是一个轻巧的&#xff0c;性能优越的Java语言编译器。它…

SpringSecurityOAuth2 中 Miss grant type问题

SpringSecurityOAuth2 登录传值的时候会出现 Miss grant type问题 没有设置content-type问题 Content-Type: application/x-www-form-urlencoded 解决

timingPath/set_data_check和set_max_delay/set_multicycle_path

目录 0.timing path 1.set_data_check 1.1set_data_clk约束cdc path 1.2 set_data_check和set_max_delay区别和使用场景 2.set_mulicycle_path 0.timing path timing path的概念&#xff1a; start和end不只是reg的CP和REG的D pin 还可以是mem的D/Qpin和 port port--&…

大模型AI时代哪类人相对更「安全」

最近随着人工智能技术的发展&#xff0c;越来越多的工作被AI智能自动化取代&#xff0c;这导致了许多人失去了TA们的工作。这些人被视为首批因人工智能失业的人。虽然这些人的失业是非常令人遗憾的&#xff0c;但是我们可以从中获得一些启示&#xff0c;以应对未来可能出现的类…

颠覆你的认知,不用开通Plus会员也可以使用ChatGPT的插件功能(兼容3.5)

在看到这篇文章之前&#xff0c;你可能以为只有GPT4.0才能使用插件。但事实上&#xff0c;早就有人基于3.5开发了一套完整的插件体系了&#xff0c;不但可以使用插件&#xff0c;还可以自己开发插件。插件使用的就是JavaScript语法&#xff0c;开发起来可以说是相当简单了。 注…

excel中函数vlookup使用方法

1、VLOOKUP函数是Excel中的一个纵向查找函数&#xff0c;它与 LOOKUP函数和 HLOOKUP函数属于一类函数&#xff0c;在工作中都有广泛应用。VLOOKUP是按列查找&#xff0c;最终返回该列所需查询列序所对应的值&#xff1b;与之对应的HLOOKUP是按行查找的。接下来以5位同学成绩表为…

路径规划算法:基于吉萨金字塔建造优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于吉萨金字塔建造优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于吉萨金字塔建造优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍…

pytorch—实现各种注意力

1.什么是Attention 所谓Attention机制&#xff0c;便是聚焦于局部信息的机制&#xff0c;比如图像中的某一个图像区域。随着任务的变化&#xff0c;注意力区域往往会发生变化。 面对上面这样的一张图&#xff0c;如果你只是从整体来看&#xff0c;只看到了很多人头&#xff0c…

JAVA ORM Bee 2.1.6 更简单,更易用;一行代码,即可完成一个表的 Sharding 分片配置

在一个工程里&#xff0c;同时使用 Mongodb 和 MySQL, 可以吗&#xff1f; 不但可以&#xff0c;还可以使用一套 Dao 的代码。 Java ORM Bee 不但支持 JDBC 类型的数据库&#xff0c;还支持 Mongodb, 也支持 Android, 鸿蒙. 最新功能介绍: V2.1.6 (2023・父亲节版) 1. 添加…