Mysql的事务是什么?

简单来说,MySQL 实现事务的核心就像是给你的数据库操作加了一套“保险和存档”机制。它确保了你的操作要么全部成功,要么全部失败,并且在面对多人同时操作、系统突然崩溃等情况时,数据依然可靠、准确。

为什么需要事务呢?想象一下银行转账:你从A账户取出100元,然后存入B账户。这其实是两个步骤:

  1. A账户余额 - 100元。
  2. B账户余额 + 100元。

如果第一个步骤成功了,第二个步骤因为系统崩溃失败了,那这100元就凭空消失了!这显然不能接受。事务就是为了解决这类问题,它把这些操作“打包”成一个不可分割的整体。

MySQL(特别是其默认且最常用的存储引擎InnoDB)通过以下“四大法宝”来确保事务的可靠性:

  1. 原子性 (Atomicity):保证操作要么全成功,要么全失败。
  2. 一致性 (Consistency):保证事务前后数据都符合规则。
  3. 隔离性 (Isolation):保证多个事务互不干扰,像独立运行一样。
  4. 持久性 (Durability):保证事务一旦提交,数据就永远不会丢失。

接下来,我们就一步一步揭开这些法宝的神秘面纱。


一、 原子性 (Atomicity):要么全生,要么全死

核心思想: 事务里的所有操作,就像一个“生死与共”的团队。要么这个团队所有成员都成功完成任务,要么任务失败,所有成员的状态都回到任务开始前,就好像他们从来没做过一样。

MySQL 如何实现?

MySQL 主要依靠 Undo Log(回滚日志) 来实现原子性。

想象一下你是一个艺术家,正在画布上创作一幅画。每次你画一笔(执行一个DML操作,如UPDATE、DELETE、INSERT),在动笔之前,你都会把这一笔之前画布的样子用拍立得拍下来,并贴在你的“回溯日记本”里。

  • 如果你画得很顺利,最终完成了,你就可以把这些拍立得扔掉了(事务提交,Undo Log就不需要了)。
  • 如果你画到一半发现画错了,或者突然不想画了(事务回滚或系统崩溃),你就可以翻开“回溯日记本”,找到最近一次拍的照片,把画布恢复成那张照片的样子,就好像你从未画错一样。

Undo Log 的作用:

  • 数据回滚: 当事务回滚时,InnoDB会读取Undo Log,将数据恢复到事务开始前的状态。
  • MVCC(多版本并发控制): 这是后面隔离性中会讲到的一个重要概念。Undo Log也保存了数据的历史版本,供其他事务进行“快照读”。

事务提交/回滚的流程(简化版):


(例如: 错误/回滚)
事务开始: T1
执行SQL操作1: UPDATE 账户A
记录原始值到Undo Log
(例如: 账户A原值=1000)
执行SQL操作2: INSERT 交易记录
记录原始值到Undo Log
(例如: 交易记录无,回滚则删除)
所有操作完成?
事务提交
清空Undo Log
操作生效并持久化
(通过Redo Log等)
事务回滚
根据Undo Log
恢复数据到事务开始前状态

专家视角: 原子性是事务的基石。没有原子性,一致性、隔离性和持久性都无从谈起。Undo Log不仅是实现原子性的关键,它还是MVCC实现隔离性的重要支撑,因为它存储了数据的历史版本。


二、 一致性 (Consistency):万变不离其宗

核心思想: 事务执行前后,数据库的数据状态必须从一个一致性状态转换到另一个一致性状态。这意味着所有预设的规则(比如账户余额不能为负数、订单号不能重复、外键关系不能被破坏等)都必须得到遵守。

MySQL 如何实现?

一致性不是由某个单一的机制直接实现的,它是原子性、隔离性、持久性共同作用的结果,加上数据库本身的约束和应用层的业务逻辑来保证的。

  • 原子性 确保了要么所有操作成功,要么都回滚,避免了中间状态的暴露。
  • 隔离性 确保了并发操作时,事务不会看到其他事务的中间状态,从而避免了脏数据。
  • 持久性 确保了提交后的数据不会丢失,也就不会导致数据不一致。
  • 数据库约束: PRIMARY KEY (主键)、UNIQUE KEY (唯一键)、FOREIGN KEY (外键)、NOT NULL (非空) 等。这些约束是数据库层面强制执行的规则。
  • 业务逻辑: 应用程序代码中实现的复杂业务规则(例如,转账时检查账户余额是否充足)。

一致性就像是建筑的蓝图。无论你对建筑进行什么操作(加盖、拆除),最终的结构都必须符合蓝图的规定(比如承重墙不能拆,安全门必须保留)。如果操作导致不符合蓝图,就必须撤销(回滚)。

一致性更多的是一个目标,是数据库系统和应用系统共同维护的一种状态。它确保了数据的合法性和有效性。当原子性、隔离性、持久性都得到保障时,数据自然倾向于保持一致性。


三、 隔离性 (Isolation):井水不犯河水

核心思想: 当多个事务同时操作数据库时,每个事务都感觉自己是唯一在操作的。一个事务的中间状态对其他事务是不可见的,就像它们被“隔离”在一个个独立的房间里。

MySQL 如何实现?

隔离性是事务中最复杂的部分,因为它需要在并发性和数据正确性之间找到平衡。MySQL (InnoDB) 主要通过两种机制来实现:

  1. 锁 (Locks):最直接的隔离手段,通过“抢占资源”来避免冲突。

    • 共享锁 (S Lock):多个事务可以同时持有,用于读操作。
    • 排他锁 (X Lock):只有一个事务可以持有,用于写操作,会阻塞其他读写操作。
    • 粒度: 表锁(锁住整张表)、行锁(锁住特定行)。InnoDB 默认使用行锁,粒度更细,并发性更高。
  2. 多版本并发控制 (MVCC - Multi-Version Concurrency Control)

    • 核心思想: 允许多个事务同时读取数据的不同“版本”,而不是直接阻塞。就像每次修改数据时都创建一个新版本,旧版本仍然保留着,供正在读取的事务使用。这大大提高了并发性能。
    • MVCC 依赖:
      • Undo Log: 前面提到了,存储着数据的历史版本。
      • 隐藏字段: InnoDB 每行数据都会增加几个隐藏字段:
        • DB_TRX_ID:记录最近一次修改该行的事务ID。
        • DB_ROLL_PTR:回滚指针,指向该行上一个版本的Undo Log记录。
        • DB_ROW_ID:行ID(如果表没有主键,InnoDB会生成一个隐藏的主键)。
      • Read View (读视图): 一个在事务开始时生成的“快照”,决定当前事务能看到哪些版本的数据。它包含当前活跃的事务ID列表。

MVCC 的工作原理(快照读):
当一个事务进行“快照读”(普通SELECT语句)时,它不是直接读取最新的数据,而是根据自己的 Read View 和数据的 DB_TRX_IDDB_ROLL_PTR,沿着Undo Log链条找到一个它“应该”看到的数据版本。

隔离级别: 数据库标准定义了四种隔离级别,隔离性从弱到强,并发性从强到弱:

  • 读未提交 (Read Uncommitted):可以看到其他事务未提交的数据(脏读)。基本不用。
  • 读已提交 (Read Committed):只能看到其他事务已提交的数据。但同一个事务内,多次读取可能看到不同结果(不可重复读)。Oracle 默认级别。
  • 可重复读 (Repeatable Read):同一个事务内,多次读取同一数据会看到相同结果,避免了不可重复读。但可能出现幻读(行数变化)。MySQL (InnoDB) 默认级别。
    • 实现: 事务开始时生成 Read View,整个事务期间都用这个 Read View
  • 串行化 (Serializable):最高级别,强制事务串行执行,完全避免脏读、不可重复读、幻读。并发性能最低。
    • 实现: 对所有读操作加共享锁,写操作加排他锁。

MVCC 如何避免“不可重复读”和“幻读”?

  • 不可重复读: 在“可重复读”隔离级别下,MVCC通过 Read View 的固定来解决。事务T1开始时生成一个 Read View,即使事务T2修改了数据并提交,T1仍然通过自己的 Read View 看到T2修改前的数据版本,从而实现了可重复读。
  • 幻读: MVCC只能解决快照读的幻读问题。对于当前读SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE)仍然会存在。MySQL 在“可重复读”隔离级别下,通过间隙锁 (Gap Lock)Next-Key Lock (行锁+间隙锁) 来彻底解决幻读问题。

MVCC 读取数据的流程图:

T1可见
T1不可见
T1可见
T1不可见
事务T1开始
生成Read View(RV)
T1执行SELECT
(快照读)
读取数据行D的最新版本
比较D.DB_TRX_ID
与RV活跃事务列表
返回数据D给T1
沿着D.DB_ROLL_PTR
追溯到Undo Log中的上一版本
比较上一版本D'的DB_TRX_ID
与RV活跃事务列表
返回数据D'给T1
继续追溯到更早版本
直到找到可见版本或无版本
找到可见版本?
数据对T1不可见

专家视角: 隔离性是并发控制的核心挑战。MVCC的引入是数据库发展的重要里程碑,它极大地提高了数据库的并发处理能力,让读写操作可以在大部分情况下互不阻塞。锁和MVCC是互补的,锁用于需要严格同步的“当前读”和写操作,MVCC用于高性能的“快照读”。


四、 持久性 (Durability):言出法随,一字千金

核心思想: 事务一旦提交,它对数据库的所有修改就是永久性的。即使系统崩溃、断电,这些修改也必须能够恢复,不会丢失。

MySQL 如何实现?

MySQL (InnoDB) 主要通过 Redo Log(重做日志)WAL (Write-Ahead Logging) 机制来确保持久性。

想象一下你是一个重要的会议记录员。会议上做出的所有决定(数据修改),你不是等会议全部结束才整理成正式文件,而是每当有一个新决定拍板时,你立即用最快的速度记在一本“快速笔记”(Redo Log)上。这本笔记会定期同步到正式的“会议记录本”(数据文件)。

即使会议进行到一半突然停电,你也可以根据这本“快速笔记”恢复到停电前所有的已批准决定,而不会丢失任何内容。

Redo Log 的作用:

  • 崩溃恢复: 当数据库在事务提交后、数据页还没来得及从内存刷写到磁盘时崩溃,重启后可以通过Redo Log将这些已提交但未持久化的修改重新应用到数据文件中,确保数据不丢。
  • 提高性能: Redo Log是顺序写入的,速度非常快。它允许事务提交时只将Redo Log刷盘(相对数据文件随机写磁盘更快),而数据页的刷盘可以延迟进行。

Redo Log 刷盘策略 (InnoDB_flush_log_at_trx_commit):

  • 0:事务提交时,Redo Log 写入日志缓冲区,并每秒刷盘一次。性能好,但可能丢失1秒数据。
  • 1:事务提交时,Redo Log 写入日志缓冲区,并立即刷盘。安全性最高,但性能最差。
  • 2:事务提交时,Redo Log 写入日志缓冲区,然后写到OS Cache,OS每秒刷盘一次。折中方案。

WAL (Write-Ahead Logging) 原则:
先写日志,再写数据。即 Redo Log 必须先于对应的数据页刷盘。这是确保持久性的关键原则。

Double Write Buffer(双写缓冲区):
这是一个额外的保护机制,防止“部分写失效”问题。当数据页从Buffer Pool刷写到磁盘时,不是直接写到数据文件,而是先写到Double Write Buffer(一个连续的区域),然后再写到真正的数据文件。这样即使在数据页写入过程中发生崩溃,也可以从Double Write Buffer中恢复完整的数据页。

持久化流程图:

已提交
未刷盘数据页
未提交事务
客户端提交事务
数据修改
(在内存Buffer Pool中)
将修改内容写入Redo Log Buffer
(内存中)
事务提交
Redo Log Buffer内容刷写到磁盘Redo Log文件
(遵循WAL原则)
事务提交成功,返回客户端
后台线程异步将Buffer Pool中修改过的数据页
刷写到磁盘数据文件
(可能通过Double Write Buffer)
数据库崩溃/断电
数据库重启
检查Redo Log文件
发现未完全刷盘的已提交事务
或未完成的事务?
通过Redo Log重做这些操作
恢复数据一致性
通过Undo Log回滚这些操作
数据库恢复完成,对外服务

持久性是数据库系统最重要的承诺之一。Redo Log和WAL机制是实现这一承诺的核心,它们在保证数据不丢失的同时,也通过日志的顺序写入特性提升了性能。数据库的恢复能力是其健壮性的重要体现。


MySQL (InnoDB) 实现事务的四大特性,是一套精妙且协同工作的复杂系统。它不是依靠单一技术,而是通过多种机制的组合与协作:

  • Undo Log:是原子性(回滚)和隔离性(MVCC 多版本)的基石。
  • Redo Log:是持久性(崩溃恢复)和高性能写入的保障。
  • 锁机制:是隔离性的直接手段,解决并发冲突,特别是“当前读”的隔离。
  • MVCC:是隔离性的高级实现,通过多版本数据和 Read View 提升并发性能。
  • WAL原则、Double Write Buffer、事务隔离级别、数据库约束等,都是这套系统的重要组成部分。

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

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

相关文章

测试开发:Python+Django实现接口测试工具

【测试开发天花板】DjangoVuePyTest打造企业级自动化平台|能写进简历的硬核项目最近被几个公司实习生整自闭了,没有基础,想学自动化又不知道怎么去学,没有方向没有头绪,说白了其实就是学习过程中没有成就感&#xff0c…

TFS-2022《A Novel Data-Driven Approach to Autonomous Fuzzy Clustering》

核心思想 这篇论文的核心思想是提出一种全新的、数据驱动的自主模糊聚类(Autonomous Fuzzy Clustering, AFC)算法。其核心创新在于,它巧妙地结合了模糊聚类的灵活性和基于中位数(medoids)聚类的可解释性,并…

ELK是什么

ELK 是一个广受欢迎的开源技术栈,用于实时采集、处理、存储、搜索、分析和可视化海量的日志数据(log)和机器生成的数据(machine data),尤其是在 IT 系统监控、应用故障排查、安全分析和业务智能等领域应用广…

[硬件电路-123]:模拟电路 - 信号处理电路 - 常见的高速运放芯片、典型电路、电路实施注意事项

一、高速运放常见芯片型号及特性高速运放(高速运算放大器)通常指带宽(GBW)超过10MHz、压摆率(SR)高于10V/μs的器件,适用于视频处理、通信系统、高速数据采集等场景。以下是典型芯片及其特性&am…

关于解决WinRiver项目动态XmlElement的序列化与反序列化的问题

关于解决WinRiver项目动态XmlElement的序列化与反序列化的问题 一、WinRiver项目流量汇总XML内容 1.1、索引可变,索引下 XmlElement 元素内容固定 1.2、如何将对象 BottomTrack 的动态内容序列化为 XML ? 1.3、如何将 XML 动态内容反序列化为对象 BottomTrack ? 二、XML 动态…

【力扣 Hot100】 刷题日记

D3 128.最长连续序列 错解 class Solution {public int longestConsecutive(int[] nums) {Arrays.sort(nums);int maxCnt 0;int cnt 0;for (int i 0; i < nums.length - 1; i) {if(nums[i] ! nums[i 1] - 1){//如果不连续&#xff0c;取cnt与maxCnt较大值&#xff0c…

飞算JavaAI编程插件:以AI之力赋能Java开发,让编码效率再升级

你是否希望自己敲代码的时候总有一位大佬在你背后帮你保驾护航。想象一下&#xff0c;当你对着Java编辑器敲代码时&#xff0c;身后站了位“隐形大神”——你刚敲出for&#xff0c;它就预判到你要遍历集合&#xff0c;自动补全带泛型的循环逻辑&#xff1b;你手滑把equals写成&…

机器学习通关秘籍|Day 03:决策树、随机森林与线性回归

目录 一、决策树 1、概念 2、基于信息增益的决策树的建立 &#xff08;1&#xff09;信息熵 &#xff08;2&#xff09;信息增益 &#xff08;3&#xff09;信息增益决策树建立步骤 3、基于基尼指数的决策树的建立 4、API 二、随机森林 1、算法原理 2、API 三、线性…

C++进阶—C++的类型转换

第一章&#xff1a;C语言中的类型转换在C语言中&#xff0c;如果赋值运算符左右两侧类型不同&#xff0c;或者形参与实参类型不匹配&#xff0c;或者返回值类型与接收返回值类型不一致时&#xff0c;就需要发生类型转化&#xff0c;C语言中总共有两种形式的类型转换&#xff1a…

基于Flask的微博话题多标签情感分析系统设计

基于Flask的微博话题情感分析系统设计与实现 一、项目概述 本项目是一个轻量化的微博话题情感分析系统&#xff0c;通过Flask框架整合情感分析模型&#xff0c;实现对微博话题及评论的情感标签识别与结果展示。系统面向普通用户和研究者&#xff0c;提供简单易用的操作界面&…

TDengine 中 TDgpt 的模型评估工具

模型评估工具 TDgpt 在企业版中提供预测分析模型和异常检测模型有效性评估工具 analytics_compare&#xff0c;该工具能够使用 TDengine 中的时序数据作为 回测依据&#xff0c;评估不同预测模型或训练模型的有效性。该工具在开源版本中不可用使用评估工具&#xff0c;需要在其…

【DL学习笔记】DataLoader类功能和参数说明

文章目录一、Dataset 与 DataLoader 功能介绍抽象类Dataset的作用DataLoader 作用两者关系二、torch.utils.data.DataLoader代码示例常用参数图示num_workers设置多少合适数据加载子进程如何并行的pin_memorysampler两种sampler顺序采样 SequentialSampler随机采样 RandomSampl…

JVM中年轻代、老年代、永久代(或元空间)、Eden区和Survivor区概念介绍

在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;内存管理是自动化的&#xff0c;这主要通过垃圾回收机制实现。JVM将堆内存划分为不同的区域&#xff0c;以便更高效地管理和回收对象。以下是关于年轻代、老年代、永久代&#xff08;或元空间&#xff09;、Eden区和Surv…

译 | BBC Studios团队:贝叶斯合成控制方法SCM的应用案例

来自上传文件中的文章《Using Causal Inference for Measuring Marketing Impact: How BBC Studios Utilises Geo Holdouts and CausalPy》 本篇介绍了在传统A/B测试不适用时&#xff0c;如何利用贝叶斯合成控制方法和地理区域保留来评估营销活动效果。其亮点在于通过构建“反事…

Web开发-PHP应用TP框架MVC模型路由访问模版渲染安全写法版本漏洞

我们先使用/index.php/index/index/test&#xff0c;就是图中的test()方法 /index.php/index/index/index&#xff0c;这个回显就是111 http://127.0.0.1:83/index.php/index/index/test2?x123456 public function test2() {$x$_GET[x];return $x; } 这里再做一个案例更详细一…

FreeRTOS列表系统深度解析

FreeRTOS列表系统深度解析 一、核心数据结构 1. 列表控制块 (List_t) typedef struct xLIST {volatile UBaseType_t uxNumberOfItems; // 当前列表项数量ListItem_t * pxIndex; // 遍历指针&#xff08;用于轮询调度&#xff09;MiniListItem_t xListEnd; …

《Linux编译器:gcc/g++食用指南》

坚持用 清晰易懂的图解 代码语言&#xff0c;让每个知识点变得简单&#xff01; &#x1f680;呆头个人主页详情 &#x1f331; 呆头个人Gitee代码仓库 &#x1f4cc; 呆头详细专栏系列 座右铭&#xff1a; “不患无位&#xff0c;患所以立。” 《Linux编译器&#xff1a;GCC…

SparkKV转换算子实战解析

目录 KV算子 parallelizePairs mapToPair mapValues groupByKey reduceByKey sortByKey 算子应用理解 reduceByKey和groupByKey的区别 groupByKeymapValues实现KV数据的V的操作 改进用reduceByKey groupby通过K和通过V分组的模板代码 问题集锦 宝贵的经验 这里会…

深度解析 TCP 三次握手与四次挥手:从原理到 HTTP/HTTPS 的应用

TCP 的三次握手和四次挥手是网络通信的基石&#xff0c;无论是 HTTP 还是 HTTPS&#xff0c;它们都依赖 TCP 提供可靠的传输层服务。本文将用万字篇幅&#xff0c;结合 Mermaid 图表和代码示例&#xff0c;深入讲解 TCP 三次握手、四次挥手的原理、过程、状态变化&#xff0c;以…

Hyper-V + Centos stream 9 搭建K8s集群(一)

一、创建虚拟机一台32G内存&#xff0c;16核心的Win11&#xff0c;已经安装了Hyper-V 管理器。然后也下载了CentOS-Stream-9-latest-x86_64-dvd1.iso的镜像文件。这里Hyper-V创建虚拟机的过程就不赘述了&#xff0c;如果出现虚拟机加载不到镜像的问题&#xff0c;先把这个使用安…