当前位置: 首页 > news >正文

MySQL实战解析底层---事务隔离:为什么你改了我还看不见

目录

前言

隔离性与隔离级别

事务隔离的实现

事务的启动方式


  • 前言

  • 和数据库打交道的时候,总是会用到事务
  • 最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱
  • 转账过程具体到程序里会有一系列的操作,比如查询余额、做加减法、更新余额等
  • 这些操作必须保证是一体的,不然等程序查完之后,还没做减法之前,你这 100 块钱,完全可以借着这个时间差再查一次,然后再给另外一个朋友转账,如果银行这么整,不就乱了么?
  • 这时就要用到“事务”这个概念了
  • 简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败
  • 在 MySQL 中,事务支持是在引擎层实现的
  • MySQL 是一个支持多引擎的系统,但并不是所有的引擎都支持事务
  • 比如 MySQL 原生的 MyISAM 引擎就不支持事务,这也是 MyISAM 被 InnoDB 取代的重要原因之一
  • 下面将会以 InnoDB 为例,剖析 MySQL 在事务支持方面的特定实现,并基于原理给出相应的实践建议,希望这些案例能加深你对 MySQL 事务原理的理解
  • 隔离性与隔离级别

  • 提到事务,你肯定会想到 ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)
  • 今天就来说说其中 I,也就是“隔离性”
  • 当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念
  • 在谈隔离级别之前,你首先要知道,你隔离得越严实,效率就会越低
  • 因此很多时候,都要在二者之间寻找一个平衡点
  • SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )
  • 下面逐一解释:
    • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到
    • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到
    • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的;当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的
    • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”;当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
  • 其中“读提交”和“可重复读”比较难理解,所以用一个例子说明这几种隔离级别
  • 假设数据表 T 中只有一列,其中一行的值为 1
  • 下面是按照时间顺序执行两个事务的行为

  • 来看看在不同的隔离级别下,事务 A 会有哪些不同的返回结果,也就是图里面 V1、V2、V3 的返回值分别是什么
  • 若隔离级别是“读未提交”
    • 则 V1 的值就是 2
    • 这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了
    • 因此,V2、V3 也都是 2
  • 若隔离级别是“读提交”
    • 则 V1 是 1,V2 的值是 2
    • 事务 B 的更新在提交后才能被 A 看到
    • 所以, V3 的值也是 2
  • 若隔离级别是“可重复读”
    • 则 V1、V2 是 1,V3 是 2
    • 之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的
  • 若隔离级别是“串行化”
    • 则在事务 B 执行“将 1 改成 2”的时候,会被锁住
    • 直到事务 A 提交后,事务 B 才可以继续执行
    • 所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2
  • 在实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准
  • 在“可重复读”隔离级别下
    • 这个视图是在事务启动时创建的,整个事务存在期间都用这个视图
  • 在“读提交”隔离级别下
    • 这个视图是在每个 SQL 语句开始执行的时候创建的
    • 这里需要注意的是,“读未提交”隔离级别下直接返回记录上的最新值,没有视图概念
  • 而“串行化”隔离级别下
    • 直接用加锁的方式来避免并行访问
  • 可以看到在不同的隔离级别下,数据库行为是有所不同的
  • Oracle 数据库的默认隔离级别其实就是“读提交”
  • 因此对于一些从 Oracle 迁移到 MySQL 的应用,为保证数据库隔离级别的一致,你一定要记得将 MySQL 的隔离级别设置为“读提交”
  • 配置的方式是,将启动参数 transaction-isolation 的值设置成 READ-COMMITTED
  • 可以用 show variables 来查看当前的值
  • 总结来说,存在即合理,每种隔离级别都有自己的使用场景,要根据自己的业务情况来定
  • 那什么时候需要“可重复读”的场景呢?
  • 来看一个数据校对逻辑的案例
  • 假设你在管理一个个人银行账户表
  • 一个表存了账户余额,一个表存了账单明细
  • 到了月底你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致
  • 你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果
  • 这时候使用“可重复读”隔离级别就很方便
  • 事务启动时的视图可以认为是静态的,不受其他事务更新的影响
  • 事务隔离的实现

  • 理解了事务的隔离级别,再来看看事务隔离具体是怎么实现的
  • 这里展开说明“可重复读”
  • 在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作
  • 记录上的最新值,通过回滚操作,都可以得到前一个状态的值
  • 假设一个值从 1 被按顺序改成了 2、3、4
  • 当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view
  • 在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存在多个版本,就是数据库的多版本并发控制(MVCC)
  • 对于 read-view A,要得到 1,就必须将当前值依次执行图中所有的回滚操作得到
  • 同时你会发现,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C对应的事务是不会冲突的
  • 回滚日志总不能一直保留吧,什么时候删除呢?
  • 答案是,在不需要的时候才删除
  • 也就是说,系统会判断,当没有事务再需要用到这些回滚日志时,回滚日志会被删除
  • 什么时候才不需要了呢?
  • 就是当系统里没有比这个回滚日志更早的 read-view 的时候
  • 基于上面的说明,来讨论一下为什么建议你尽量不要使用长事务
  • 长事务意味着系统里面会存在很老的事务视图
  • 由于这些事务随时可能访问数据库里面的任何数据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间
  • 在 MySQL 5.5 及以前的版本,回滚日志是跟数据字典一起放在 ibdata 文件里的,即使长事务最终提交,回滚段被清理,文件也不会变小
  • 有数据只有 20GB,而回滚段有 200GB的库
  • 最终只好为了清理回滚段,重建整个库
  • 除了对回滚段的影响,长事务还占用锁资源,也可能拖垮整个库,这个会在后面讲锁的时候展开
  • 事务的启动方式

  • 如前面所述,长事务有这些潜在风险,建议尽量避免
  • 其实很多时候业务开发并不是有意使用长事务,通常是由于误用所致
  • MySQL 的事务启动方式有以下几种:
  • (1)显式启动事务语句, begin 或 start transaction;配套的提交语句是 commit,回滚语句是 rollback
  • (2)set autocommit=0,这个命令会将这个线程的自动提交关掉;意味着如果你只执行一个select 语句,这个事务就启动了,而且并不会自动提交;这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接
  • 有些客户端连接框架会默认连接成功后先执行一个 set autocommit=0 的命令
  • 这就导致接下来的查询都在事务中,如果是长连接,就导致了意外的长事务
  • 因此建议你总是使用 set autocommit=1, 通过显式语句的方式来启动事务
  • 但是有的开发会纠结“多一次交互”的问题
  • 对于一个需要频繁使用事务的业务,第二种方式每个事务在开始时都不需要主动执行一次 “begin”,减少了语句的交互次数
  • 如果你也有这个顾虑,建议你使用 commit work and chain 语法
  • 在 autocommit 为 1 的情况下,用 begin 显式启动的事务,如果执行 commit 则提交事务
  • 如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销
  • 同时带来的好处是从程序开发的角度明确地知道每个语句是否处于事务中
  • 可以在 information_schema 库的 innodb_trx 这个表中查询长事务

相关文章:

MySQL实战解析底层---事务隔离:为什么你改了我还看不见

目录 前言 隔离性与隔离级别 事务隔离的实现 事务的启动方式 前言 和数据库打交道的时候,总是会用到事务最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱转账过程具体到程序里会有一系列的操作&#xff0…...

变更数据捕获(CDC)

从广泛意义上说,全球许多企业每天都需要通过频繁的数据批量处理与加载,来定期将数据从一个数据库迁移到另一个数据库(或数据仓库)。这类定期批量加载的工作,往往既耗费时间,又会消耗原始系统的大量处理能力。因此,管理…...

【移动端表格组件】uniapp简单实现H5,小程序,APP多端兼容表格功能,复制即用,简单易懂【详细注释版本】

前言: 由于最近需要做移动端的项目 有个pc端的后台系统里面需要移一部分页面过来 而里面就有很多的表格,我就开始惯例网上先找前人栽的树,我好乘凉 然后找了一圈发现,不管是主流的移动端ui库或者网上自己写的帖子,或者…...

电子技术——CMOS 逻辑门电路

电子技术——CMOS 逻辑门电路 在本节我们介绍如何使用CMOS电路实现组合逻辑函数。在组合电路中,电路是瞬时发生的,也就是电路的输出之和当前的输入有关,并且电路是无记忆的也没有反馈。组合电路被大量的使用在当今的数字逻辑系统中。 晶体管…...

【C++】C++11 新特性

目录 1.列表初始化 1.1. C98中使用{}初始化的问题 1.2. 内置类型的列表初始化 1.3. 自定义类型的列表初始化 2. 变量类型推导 2.1. 为什么需要类型推导 2.2. decltype类型推导 2.2.1 为什么需要decltype 2.2.2. decltype 3. 对默认成员的控制(default、delete) 3.1. …...

JPA 相关注解说明

jpa相关注解 JPA(Java Persistence API)是一种Java规范,定义了一套标准的对象关系映射(ORM)API,用于将Java对象映射到关系型数据库中。JPA旨在统一各种ORM框架之间的差异,提供一种标准化的ORM解…...

SAP 生产订单/流程订单中日期的解释

SAP 生产订单/流程订单中日期的解释 基本开始日期:表示订单的开始日期 基本完成日期:表示订单的完成日期 我们在输入基本开始日期和基本完成日期时需要关注 调度 下面的“类型”,其中有向前、向后、当天日期等: 调度类型 为向前…...

Java设计模式笔记——七大设计原则

系列文章目录 第一章 Java 设计模式之七大设计原则 文章目录系列文章目录前言一、单一职责原则1.案例分析2.改进二、开闭原则1.案例分析2.改进三、里氏替换原则1.案例分析2.改进四、依赖倒转原则五、接口隔离原则1.案例分析2.改进六、合成复用原则1.案例分析2.改进七、迪米特原…...

记录第一次接口上线过程

新入职一家公司后,前三天一直在学习公司内部各种制度文化以及考试。 一直到第三天组长突然叫我过去,给了一个需求的思维导图,按照这个需求写这样一个接口, 其实还不错,不用自己去分析需求,按照这上面直接开…...

时序预测 | MATLAB实现Rmsprop算法优化LSTM长短期记忆神经网络时间序列多步预测(滚动预测未来,多指标,含验证Loss曲线)

时序预测 | MATLAB实现Rmsprop算法优化LSTM长短期记忆神经网络时间序列多步预测(滚动预测未来,多指标,含训练和验证Loss曲线) 目录 时序预测 | MATLAB实现Rmsprop算法优化LSTM长短期记忆神经网络时间序列多步预测(滚动预测未来,多指标,含训练和验证Loss曲线)效果一览基本描…...

如何利用Level2行情数据接口追板和交易股票?

十档行情看得更深的A股行情软件,我们在盘口数据中可以看到,买一到买五以及卖一到卖五,共10个价位的挂单情况,但基于上证所的level-2行情软件,视野则扩展到了买一到买十以及卖一到卖十数据,无疑比所有免费软…...

MySQL常用的聚合函数

聚合函数聚合函数对一组值进行运算,并返回单个值。也叫组合函数函数作用COUNT(*|列名) 统计查询结果的⾏数AVG(数值类型列名)求平均值,返回指定列数据的平均值SUM (数值类型列名)求和,返回指定列的总和MAX(列名)查询指定列的最⼤值MIN(列名)查…...

如何评估模糊测试工具-unibench的使用

unibench是一个用来评估模糊测试工具的benchmark。这个benchmark集成了20多个常用的测试程序,以及许多模糊测试工具。 这篇文章(https://zhuanlan.zhihu.com/p/421124258)对unibench进行了简单的介绍,本文就不再赘诉,…...

2023初级会计详细学习计划打卡表!自律逆袭,一次上岸!

2023年初级会计职称考试报名时间:2月7日-28日考试时间:5月13日—17日给大家整理了《经济法基础》和《初级会计实务》两科超实用的学习打卡表重要程度、难易度、易错点、要求掌握内容、章节估分等都全部总结在一起,一目了然!为什么…...

【Python】Python项目打包发布(四)(基于Nuitka打包PySide6项目)

Python项目打包发布汇总 【Python】Python项目打包发布(一)(基于Pyinstaller打包多目录项目) 【Python】Python项目打包发布(二)(基于Pyinstaller打包PyWebIO项目) 【Python】Pytho…...

一起Talk Android吧(第五百一十三回:Java中的byte数组与int变量相互转换)

文章目录整体思路示例代码各位看官们大家好,上一回中咱们说的例子是"自定义Dialog",这一回中咱们说的例子是" Java中的byte数组与int变量相互转换"。闲话休提,言归正转, 让我们一起Talk Android吧!在实际项目…...

22《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》中文分享

​《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》 本人能力有限,如果错误欢迎批评指正。 第五章:Folding and Aggregation Are Cooperative Transitions (折叠和聚合是同时进行的) -蛋白质折叠的协同作…...

vue2 @hook 的解析与妙用

目录前言几种用法用法一 将放在多个生命周期的逻辑,统一到一个生命周期中用法二 监听子组件生命周期运行的情况运用场景场景一 许多时候,我们不得不在不同的生命周期中执行某些逻辑,并且这些逻辑会用到一些通用的变量,这些通用变量…...

网络技术|网络地址转换与IPv6|路由设计基础|4

对应讲义——p6 p7NAT例题例1解1例2解2例3解3例4解4一、IPv6地址用二进制格式表示128位的一个IPv6地址,按每16位为一个位段,划分为8个位段。若某个IPv6地址中出现多个连续的二进制0,可以通过压缩某个位段中的前导0来简化IPv6地址的表示。例如…...

MySQL运维知识

1 日志1.1 错误日志1.2 二进制日志查看二进制日志:mysqlbinlog ./binlog.000007purge master logs to binlog.000006reset mastershow variables like %binlog_expire_logs_seconds%默认二进制文件只存放30天,30天后会自动删除。1.3 查询日志1.4 慢查询日…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性&#xf…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

【Veristand】Veristand环境安装教程-Linux RT / Windows

首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

Android屏幕刷新率与FPS(Frames Per Second) 120hz

Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数,单位是赫兹(Hz)。 60Hz 屏幕:每秒刷新 60 次,每次刷新间隔约 16.67ms 90Hz 屏幕:每秒刷新 90 次,…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

C++中vector类型的介绍和使用

文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...