Redis 事务 总结

前言


 相关系列

  • 《Redis & 目录》(持续更新)
  • 《Redis & 事务 & 源码》(学习过程/多有漏误/仅作参考/不再更新)
  • 《Redis & 事务 & 总结》(学习总结/最新最准/持续更新)
  • 《Redis & 事务 & 问题》(学习解答/持续更新)
     

 参考文献

  • 《Redis事务详解》
     
     

概述


    Redis事务并不严格具备ACID属性。在我们对数据库的常规理解中,事务必然会严格遵守原子性/一致性/隔离性/持久性。但由于Redis基于内存设计/实现的原因,其对事务ACID属性的实现并不存在/彻底。因此与其称之为事务,将之称为批处理其实更加贴切…该知识点会在下文讲解ACID时详述。

    Redis事务的意义是为了防止插队。Redis事务中的指令会被打包一次执行,因此可以避免多客户端指令交互执行的情况发生。
 
 

使用


 开启/执行/取消

    事务的开启/执行/取消指令具体如下:

  • MULTI:开启事务;
  • EXEC:执行事务;
// ---- 设置一个键/值为num/1的键值对。
> SET num 1
OK
// ---- 开启事务,并依次递增/递减/递增。每个指令输入后都会返回QUEUED,意味着指令已被成功加入
// 事务队列中,等待最终被执行。
> MULTI
OK
> INCR num
QUEUED
> DECR num
QUEUED
> INCR num
QUEUED
// ---- 执行事务,并返回每条指令的执行结果。
> EXEC
2
1
2
  • DISCARD:在执行前取消事务。
> SET num 1
OK
// ---- 开启事务,并添加递增/递减/递增指令。
> MULTI
OK
> INCR num
QUEUED
> DECR num
QUEUED
> INCR num
QUEUED
// ---- 取消事务。
> DISCARD
OK
// ---- 取消事务后再执行会抛出错误。
> EXEC
(error) ERR EXEC without MULTI

 

 阶段

    Redis事务分为组队/执行两个阶段。组队阶段是Redis事务的第一阶段,由指令“MULTI”在开启事务时同步开启。在组队阶段中Redis会创建相应的事务队列用于顺序接收客户端发送的各项指令,因此事务中的指令其实是多次发送的。而由于Redis不可能在客户端发送全部指令/发送“EXEC”指令/发送“DISCARD”前无限等待,因为整个事务指令发送过程的时长并不可控,因此Redis实际上可以并发接受多个客户端指令。这种并发接收可能是单线程在多客户端之间相互切换实现的,也可能真的有多个线程在各自为政,这具体与Redis的版本有关。随着Redis的不断发展,早期(6.0之前)版本的“单线程实现”描述其实已经不太准确了,就比如现今绝大多数版本的Redis都会启用额外线程来实现数据持久化。但一直以来始终不变的是指令必然只会由唯一的主线程执行,因此在该设计被破坏前,无论Redis如何引入多线程,其都可以被称为单线程实现。

在这里插入图片描述

    Redis事务可能无法进入执行阶段。执行阶段顾名思义,即是将已完成组队的事务指令整体交由主线程依次执行。而由于执行阶段会由“EXEC”负责开启,因此如果客户端事先发送了“DISCARD”指令,那么事务/事务队列就会直接结束/丢弃而无法进入执行阶段。而除此之外如果客户端发送的指令出现语法错误,那事务也会因为原子性而直接结束…该知识点会在下文讲解原子性时详述。
 
 

实现


 原子性

    Redis事务具备“伪”原子性。原子性通俗的说法就是“要么一起成功,要么一起失败”,关于Redis事务的原子性一直是相当具有争议的点,原因就是其未曾“完全”遵循原子性的定义,即其允许在某些情况下部分成功。因此Redis具备原子性虽然属于官方说法,但仍有部分开发者坚持认为其并不具备原子性。而对于作者本人而言我认为Redis是具有原子性的,只是基于其功能定位而难以/无法实现的足够彻底而已,因此我将这种原子性称为“伪”原子性。

    Redis事务在组队阶段具备“真”原子性。对于在组队阶段的事务而言,如果客户端输入的指令在语法上存在错误,那么其整个事务/事务队列都会直接结束/丢弃。该行为符合原子性的标准定义,因此Redis事务在组队阶段具备“真”原子性…具体图示/案例如下:

在这里插入图片描述

> SET num 1
OK
> MULTI
OK
> INCR num
QUEUED
// ---- 错误指令,不同版本的Redis/图形客户端在此会有不同的表现,我这里显示排队成功,但有些版本会直接报错。
> INCR1 num
QUEUED
> INCR num
QUEUED
// ---- 执行时由于存在错误指令会抛出异常,整体不执行。
> EXEC
ReplyError: EXECABORT Transaction discarded because of previous errors.

    Redis事务在执行阶段具备“伪”原子性。对于在执行阶段的事务而言,即使其在过程中出现某指令执行异常的情况,Redis也只会记录该异常并继续执行后续指令,因此Redis事务并不具备数据回滚能力。该行为明显违背了原子性的标准定义,因此Redis事务在执行阶段只具备“伪”原子性…具体图示/案例如下:

在这里插入图片描述

> SET name aaa
OK
> MULTI
OK
> SET name bbb
QUEUED
// ---- 该指令没有语法错误,但字符串类型是无法递增的。
> INCR name
QUEUED
> SET name ccc
QUEUED
// ---- 执行后只有两个结果,因为第二条指令会执行失败。可能是因为Redis版本/图形客户端
// 的原因,我这里没有抛出异常。
> EXEC
OK
OK

 

 一致性/持久性

    Redis事务具备结构一致性,但不具备内容一致性。Redis并不像Mysql一样对数据存在为空/大小/长度/范围/唯一/外键等结构性约束,故而事务数据只要计算成功其结构就必然合理,因此Redis事务具备结构一致性。但与此同时Redis事务却不具备内容一致性,因为其“伪”原子性无法保证所有指令全都执行成功。例如Redis事务就无法保证账号A/B在收/支上的一致性,因为两者的指令都存在失败可能,而任意一方的失败都将导致收支上的不平衡,因为Redis事务不具备内容一致性。

    Redis事务不(直接)具备持久性。所谓持久性是指数据被永久保存的特性,Redis存在RDB/AOF两种持久化机制,在两者被打开至少一个的情况下,Redis可以将内存中的数据持久化至磁盘中永久保存,从而“尽可能”避免在宕机/关机/重启/断电时丢失数据。该知识点会在Redis持久化的专项文章中详述,此处我们只需知道的是:虽然Redis具备全局性的数据持久化能力,但遗憾的是该机制的运行与事务是完全独立的,即事务的执行并不会强制触发该机制以实现对事务数据的持久化。因此Redis事务是不具备持久性的,虽然全局性的持久化机制可以在一定程度上间接达成这个效果。
 

 隔离性/WATCH

    Redis事务具备“读已递交”级别的隔离性。所谓隔离性是指事务并发期间的相互影响程度,这种相互影响在数据库的标准定义中被从低到高的被分为四个级别,其具体名称/问题/效果如下所示。而Redis没有设计任何专项机制去提升事务的隔离性,因此Redis事务仅天然具有“读已递交”级别的隔离性。为什么不是“读未递交”级别的隔离性呢?这是因为Redis事务压根没有统一递交的说法…其指令的每次执行都可以直接视作数据的一次递交。

名称问题效果
读未递交脏读/不可重复度/幻读会读取到其它事务未递交的数据,并且即无法保证数据的内容不变化,也无法保证数据的数量不变化
读已递交不可重复度/幻读不会读取到其它事务未递交的数据,但即无法保证数据的内容不变化,也无法保证数据的数量不变化
可重复读幻读不会读取到其它事务未递交的数据,并且可以保证数据的内容不变化,但无法保证数据的数量不变化
序列化/事务串行执行,相互之间完全不影响

    Redis事务在执行阶段具备“序列化”等级的隔离级别。由于单线程实现的原因,Redis事务在执行阶段其实具备“序列化”等级的隔离级别。因为在此期间其它事务最多只能处于组队阶段而无法处于执行阶段,甚至在早期版本中都可能不存在其它事务,因此对于当前正在执行的事务来说是绝对不可能出现目标数据被并发修改的情况的。那为什么说Redis事务只具备“读已递交”级别的隔离性呢?这是因为如果反过来看正处于组队阶段的当前事务,那么其目标数据就完全可能被处于执行阶段的其它事务所并发修改,从而导致出现执行前提被破坏的“竞态条件”情况…具体示例时间表如下:

时间事务1事务2
T1商品总量为5000,开启事务商品总量为5000,开启事务
T2组队指令 – 设置商品总量为4000(-1000)组队指令 – 设置商品总量为3000(-2000)
T3单线程执行指令 – 商品总量被设置为3000T4单线程执行指令 – 商品总量被设置为4000但正确总量应该为2000

    Redis提供了乐观锁机制的{WTACH}指令用于监控事务的目标数据。上述问题的本质还是线程安全问题,其核心在于指令的执行依据被并发修改,因此只要能够控制这一点就能为事务的正确执行提供基础可能。对此Redis基于乐观锁机制提供了{WTACH}指令用于监控目标数据,其本质是记录目标数据的快照。而当事务进入执行阶段时其会将最新的目标数据与快照进行对比,并在发现目标数据已变化的情况下拒绝执行事务并返回null…具体示例如下:

  • WATCH key [key …]:监控目标数据集,并在目标数据发生变化时拒绝执行事务。
        ------------------------- 入参 -------------------------
        key [key …] @ 键集:需监控目标数据集的键集,多个键之间使用" "分割。
// ---- 设置/监控目标数据,并开启事务。
> SET goal aaaa
OK
> WATCH goal
OK
> MULTI
OK
// ---- 更新目标数据。
> SET goal bbbb
QUEUED
// ---- 如果目标数据在事务组队期间被其它线程/事务修改,当前事务将不会执行并返回null。
> EXEC
null

 
 

管道


    管道可大幅提升多指令的执行效率。管道相当于将事务的组队阶段从服务端转移到了客户端执行,因此其不会像事务一样多次发送指令,而是会直接将已完成打包/组队的命令集一次性发送至服务端执行。而又因为Redis执行的高效性会使得指令/数据的RRT @ Round Trip Time @ 往返时间/网络传输时间远远多于指令的执行时间,因此多指令/数据的一次性发送/返回能够有效提升批量操作的执行效率,也因此管道被广泛用于数据大批量导入/导出的场景中。

在这里插入图片描述

    Redis管道无法通过原生指令开启…下文展示的是通过Jedis使用管道的模板:

public static void testPipeLineAndNormal(Jedis jedis) throws InterruptedException {
    // ---- 开启管道。
    Pipeline pipeline = jedis.pipelined();
    // ---- 在管道中添加多个SET指令。
    for (int i = 0; i < 10000; i++) {
        pipe.set(String.valueOf(i), String.valueOf(i));
    }
    // ---- 一次性发送/执行管道指令。
    pipe.sync();
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基础数据结构及算法——AVL树【自平衡二叉搜索树】解决失衡

历史 AVL 树是一种自平衡二叉搜索树&#xff0c;由托尔哈斯特罗姆在 1960 年提出并在 1962 年发表。它的名字来源于发明者的名字&#xff1a;Adelson-Velsky 和 Landis&#xff0c;他们是苏联数学家&#xff0c;于 1962 年发表了一篇论文&#xff0c;详细介绍了 AVL 树的概念和…

VoLTE 微信令:SBC 功能篇之 超长呼叫释放信令流程

目录 1. SBC 的位置及超长呼叫释放功能简介 2. VoLTE 超长通话,SBC 释放呼叫流程 博主wx:yuanlai45_csdn 博主qq:2777137742 想要 深入学习 5GC IMS 等通信知识(加入 51学通信),或者想要 cpp 方向修改简历,模拟面试,学习指导都可以添加博主低价指导哈。 1. SBC 的位置及…

使用 Python 的 BeautifulSoup(bs4)解析复杂 HTML

使用 Python 的 BeautifulSoup&#xff08;bs4&#xff09;解析复杂 HTML&#xff1a;详解与示例 在 Web 开发和数据分析中&#xff0c;解析 HTML 是一个常见的任务&#xff0c;尤其是当你需要从网页中提取数据时。Python 提供了多个库来处理 HTML&#xff0c;其中最受欢迎的就…

华尚实业集团家居产业园总部中心项目奠基仪式成功举办

金秋风景如画&#xff0c;十月天高云淡。良辰阳光灿烂&#xff0c;吉时热闹非凡。2024年10月23日上午&#xff0c;华尚实业集团家居产业园总部中心项目奠基仪式在增城经济技术开发区宁西园区项目现场隆重举行&#xff0c;标志着华尚实业集团家居产业园总部中心建设正式拉开帷幕…

基于Java语言的充电桩管理系统

介绍 云快充协议云快充1.5协议云快充协议开源代码云快充底层协议云快充桩直连桩直连协议充电桩系统桩直连协议 软件架构 1、提供云快充底层桩直连协议&#xff0c;版本为云快充1.5&#xff0c;对于没有对接过充电桩系统的开发者尤为合适&#xff1b; 2、包含&#xff1a;启…

安卓项目复制修改包名称打包失败处理——android studio

处理方法 将资源包名称直接替换为新的包名称&#xff0c;不管错误直接生成。

skynet的cluster集群

集群的使用 现在的游戏服务器框架中&#xff0c;分布式是一种常见的需求。一个游戏服务器组通常可以分成网关服务器、登录服务器、逻辑服务器、跨服服务器等等。 在skynet中&#xff0c;我们可以通过cluster来组建一个集群&#xff0c;实现分布式的部署。 示例 我们先来看一…

Win11安装基于WSL2的Ubuntu

1. 概述 趁着还没有完全忘记&#xff0c;详细记录一下在Win11下安装基于WSL2的Ubuntu的详细过程。不得不说WSL2现在被微软开发的比较强大了&#xff0c;还是很值得安装和使用的&#xff0c;笔者就通过WSL2安装的Ubuntu成功搭建了ROS环境。 2. 详论 2.1 子系统安装 在Win11搜…

在Debian上安装向日葵

说明&#xff1a; 因为之前服务器上安装了 PVE (Proxmox VE)&#xff0c;之前是用 Proxmox VE 进行服务器资源管理的。出于某些原因&#xff0c;现在不再通过 PVE构建的虚拟机来使用计算资源&#xff0c;而是通过 PVE 自带的 Debian 系统直接使用虚拟机资源&#xff08;因为积…

NVR接入录像回放平台EasyCVR视频融合平台语音对讲配置

国标GB28181视频平台EasyCVR视频融合平台可拓展性强、视频能力灵活&#xff0c;平台可提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、云台控制、语音对讲、智能分析接入等功能。其中&#xff0c;在语音对讲方面&#xff0c;NVR接入录像回放平台目前…

JavaEE初阶---多线程(三)---内存可见性/单例模式/wait,notify的使用解决线程饿死问题

文章目录 1.volatile关键字1.1保证内存的可见性--引入1.2保证内存的可见性--分析1.3保证内存的可见性--解决1.4内存可见性-JMM内存模型 2.notify和wait介绍2.1作用一&#xff1a;控制调度顺序2.2作用二&#xff1a;避免线程饿死2.3notify和notifyAll区分 3.单例模式--经典设计模…

数据库编程 SQLITE3 Linux环境

永久存储程序数据有两种方式&#xff1a; 用文件存储用数据库存储 对于多条记录的存储而言&#xff0c;采用文件时&#xff0c;插入、删除、查找的效率都会很差&#xff0c;为了提高这些操作的效率&#xff0c;有计算机科学家设计出了数据库存储方式 一、数据库 用来管理数据…

【Android】多渠道打包配置

目录 简介打包配置签名配置渠道配置配置打包出来的App名称正式包与测试包配置 打包方式开发工具打包命令行打包 优缺点 简介 多渠道打包 是指在打包一个 Android 应用时&#xff0c;一次编译生成多个 APK 文件&#xff0c;每个 APK 文件针对一个特定的渠道。不同的渠道可能代表…

Prompt提示词设计:如何让你的AI对话更智能?

Prompt设计&#xff1a;如何让你的AI对话更智能&#xff1f; 在人工智能的世界里&#xff0c;Prompt&#xff08;提示词&#xff09;就像是一把钥匙&#xff0c;能够解锁AI的潜力&#xff0c;让它更好地理解和响应你的需求。今天&#xff0c;我们就来聊聊如何通过精心设计的Pr…

厂房区域人员进出人数统计-实施方案

1.1 现状分析 传统的人流量统计方法往往依赖于人工计数或简单的视频监控系统&#xff0c;这些方法不仅效率低下&#xff0c;而且容易出错&#xff0c;无法满足现代仓库管理的需求。因此&#xff0c;我厂区决定引入先进的智能监控系统&#xff0c;通过集成高清摄像头、GPU服务器…

【Unity】仓库逻辑:拾取物体进仓库和扔掉物品

需求说明 目标&#xff1a;实现玩家移动过程中&#xff0c;拾取物体&#xff0c;物体被放入仓库&#xff1b;点击仓库中物体&#xff0c;重新扔回3D场景中逻辑。 逻辑分析&#xff1a; 需要玩家可以移动&#xff1b;需要检测玩家和物体的碰撞&#xff0c;并摧毁物体&#xf…

css知识点梳理2

1. 选择器拓展 在 CSS 中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基本选择器进行组合形成的。 ​ 复合选择器是由两个或多个基础选择器&#xff0c;通过不同的方式组合而成的&#xf…

【Flask】一、安装与第一个测试程序

目录 Flask简介 安装Flask 安装pip&#xff08;Python包管理器&#xff09; 使用pip安装Flask 验证安装 创建Flask程序 创建应用 运行 访问测试 Flask简介 Flask是一个用Python编写的轻量级Web应用框架。它被设计为易于使用和扩展&#xff0c;使其成为构建简单网站或复…

[项目][boost搜索引擎#4] cpp-httplib使用 | log.hpp | 前端 | 测试及总结

目录 编写http_server模块 1. 引入cpp-httplib到项目中 2. cpp-httplib的使用介绍 3. 正式编写http_server 九、添加日志到项目中 十、编写前端模块 十一. 详解传 gitee 十二、项目总结 项目的扩展 写在前面 项目 gitee 已经上传啦 &#xff08;还是决定将学校和个人…

网络编程基础-Reactor线程模型-原理剖析

1、Reactor基本概念 Reactor线程模型其实是一种设计模式&#xff0c;其核心思想就是将输入多路复用和事件派发相结合&#xff0c;从而减少系统中活跃线程的数量。 像我们之前讲到的文章网络编程基础-IO模型深入理解_网络io-CSDN博客提到了其中网络IO模型&#xff08;BIO、NIO…