详解MySQL事务原理
wptr33 2025-01-11 17:49 14 浏览
今天给大家分享的是大数据开发基础部分MySQL的事务,事务在MySQL知识点中非常重要的部分,很多伙伴只是知道MySQL的四大特性,但不知道其中的原理,老刘这次给大家详细的描述MySQL四大特性的原理,MySQL事务篇的大纲如下:
什么是事务?
在MySQL中的事务是由存储引擎实现的,而且支持事务的存储引擎不多,我们主要讲解InnoDB存储引擎中的事务。
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。
事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的。
事务的四大特性(ACID)
- Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部成功,要么全部失败。
- Consistency(一致性):数据库在事务执行前后状态都必须是稳定的或者是一致的,就是说事务开始和结束后,数据库的完整性不会被破坏。
- Isolation(隔离性):事务之间不会相互影响。由锁机制和MVCC机制来实现的,其中MVCC(多版本并发控制):优化读写性能(读不加锁、读写不冲突),四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。
- Durability(持久性):事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。
事务的使用
begin或start transaction:开启一个事务;
commit:提交一个事务,并使已对数据库进行的所有修改称为永久性的;
rollback:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
ACID实现原理
下面我们就来详细讲解一下上述示例涉及的事务的ACID特性的具体实现原理。总结来说,事务的隔离性由多版本控制机制和锁实现,而原子性、一致性和持久性通过InnoDB的redo log、undo log和ForceLog at Commit机制来实现。
重做日志Redo Log
如果要存储数据则先存储数据的日志,一旦内存崩了,则可以从日志找重做日志保证了数据的可靠性,InnoDB采用了Write Ahead Log(预写日志)策略,即当事务提交时,先写重做日志,然后再择时将脏页写入磁盘。如果发生宕机导致数据丢失,就通过重做日志进行数据恢复。
回滚日志Undo Log
数据库崩溃重启后需要从redo log中把未落盘的脏页数据恢复出来,重新写入磁盘,保证用户的数据不丢失。当然,在崩溃恢复中还需要回滚没有提交的事务。由于回滚操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志来保证,所以崩溃恢复先做redo恢复数据,然后做undo回滚。
所以,在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。
Force Log at Commit机制
它实现事务的持久性,即当事务提交时,必须先将该事务的所有日志写入到重做日志文件进行持久化,然后事务的提交操作完成才算完成。为了确保每次日志都写入到重做日志文件,在每次将重做日志缓冲写入重做日志后,必须调用一次fsync操作(操作系统),将缓冲文件从文件系统缓存中真正写入磁盘。
总结一下就是redo log用于在崩溃时恢复数据,undo log用于对事务的影响进行撤销,也可以用于多版本控制。而Force Log at Commit机制保证事务提交后redo log日志都已经持久化。
原子性
原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。例如银行转账要么成功,要么失败,是不存在中间的状态!
Undo Log是实现原子性的关键,靠的就是undo log。当事务对数据库进行修改时,InnoDB会生成对应的undo log。undo log它属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。
以update操作为例:当事务执行 update 时,其生成的 undo log 中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到 update 之前的状态。
持久性
持久性是指事务执行成功后必须全部写入磁盘,事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失。
InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中。
虽然Buffer Pool的使用大大提高了读写数据的效率,但是也有别的问题,当MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
于是,优秀的程序员们引入了redo log,当我们对数据进行修改时,除了修改Buffer Pool中的数据,还会在redo log中记录这次操作。当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。
还有一点必须知道就是redo log采用的是WAL策略,所有修改先写入日志,再更新到Buffer Pool,保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
隔离性
在MySQL隔离性中,一般有两种情况:
- 要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。
- 在进行读操作的时候,可能出现脏读、不可重复读、幻读的问题。
首先讲第一种情况,MySQL要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证这一点。
锁机制的基本原理可以理解为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。
至于锁机制中的锁,一般就是之前讲到的MySQL锁,大家可以去看看这篇MySQL锁的内容。
接着讲第二种情况,读操作可能出现脏读、不可重复读、幻读的问题。
隔离性追求的是并发情形下事务之间互不干扰,但是在事务的并发操作中可能会出现一些问题:
- 丢失更新:两个事务针对同一数据都发生修改操作时,会存在丢失更新问题。
- 脏读:对于两个事务 T1,T2,T1 读取了已经被 T2 更新但还没有被提交的字段。之后,若 T2 回滚,T1读取的内容就是临时且无效的。
- 不可重复读:对于两个事务T1,T2,T1 读取了一个字段,然后 T2 更新了该字段。之后,T1再次读取同一个字段,发现字段的内容不一样。要求,多次读取数据的时候,在一个事务中读出的都应该是一样的。一般是由于 update 操作引发,所以将来执行的时候要特别注意。
- 幻读:对于两个事务T1,T2,T1 从一个表中读取了一个字段,然后 T2 在该表中插入了一些新的行。之后。如果 T1 再次读取同一个表,就会多出几行。就是发现数据的数量不一样。要求,在一个事务中多次去读取数据的时候都应该是一样的。
虽然有上述这些问题,但MySQL数据库为我们提供的四种隔离级别(由低到高):
- Read uncommitted (读未提交):最低级别,任何情况都无法保证。
- Read committed (RC,读已提交):可避免脏读的发生。
- Repeatable read (RR,可重复读):可避免脏读、不可重复读的发生。(InnoDB默认级别为RR,它可以解决幻读,主要原因是Next-Key(Gap)锁,只有RR才能使用Next-Key锁)
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
解决脏读、不可重复读、幻读的问题使用的是MVCC,即多版本的并发控制协议。它说的就是在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。
MVCC最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB实现MVCC,多个版本的数据可以共存,主要是依靠数据的隐藏列( 也可以称之为标记位 )和undo log。其中数据的隐藏列包括了该行数据的版本号、删除时间、指向undo log的指针等等;当读取数据时,MySQL可以通过隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现MVCC。
MVCC如何解决脏读、不可重复读、幻读的问题
1、MVCC解决脏读
当事务T1在第三个时刻读取自己的余额时,会发现数据已被T2事务修改,并且T2的状态还没有提交。此时事务A读取最新数据后,根据数据的undo log执行回滚操作,得到事务T2修改前的数据,从而避免了脏读。
2、MVCC解决不可重复读
当事务T1在第二个时刻第一次读取数据时,会记录该数据的版本号(数据的版本号是以row为单位记录的),假设版本号为1;当事务T2对自己的余额进行修改并且提交时,该行记录的版本号增加,假设版本号为2;当事务T1在第五个时刻再一次读取数据时,发现数据的版本号2大于第一次读取时记录的版本号1,因此会根据undo log执行回滚操作,得到版本号为1时的数据,从而实现了可重复读。
3、MVCC解决幻读
InnoDB实现的RR通过next-key lock机制避免了幻读现象。
next-key lock是行锁的一种,实现相当于record lock(记录锁) + gap lock(间隙锁),它的特点是不仅会锁住记录本身(record lock的功能),还会锁定一个范围(gap lock的功能)。
当事务T1在第二个时刻第一次读取0<id<5数据时,会进行标记,标记内容包括数据的版本号等,并且标记的不只是id=1的数据,还将范围(0,5)进行了标记。我们接着在第三个时刻插入新的用户并且提交事务,最后第五个时刻再次读取0<id<5数据时,便可以发现id=2的数据比之前标记的版本号更高,此时再结合undo log执行回滚操作,避免了幻读。
稍微总结下,InnoDB通过锁机制、数据的隐藏列、undo log和类next-key lock,实现了一定程度的隔离性,可以满足大多数场景的需要。不过需要说明的是,RR虽然避免了幻读问题,但是毕竟不是Serializable,不能保证完全的隔离。
一致性
一致性是事物追求的最终目标,前面提到的原子性,隔离性,持久性都是为了保证数据库的一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性,数据库提供的手段。数据库必须要实现AID三大特性,才有可能实现一致性。
总结
本文作为大数据开发指南MySQL的第四篇详细介绍了MySQL事务的内容,尤其是MySQL四大特性的原理。希望大家能够跟着老刘的文章,好好捋捋思路,争取能够用自己的话把这些知识点讲述出来!
相关推荐
- 每天一个AI姬,AMD核显用户有福了,AI绘画打破 NVIDIA 显卡垄断
-
使用StableDiffusion进行AI绘画,并不一定只能使用NVIDIA英伟达显卡,甚至,也不一定只能使用独立显卡。今天我们使用AMD6800H核显,并安装了StableDif...
- NETworkManager:功能强大的网络管理与问题排除工具
-
关于NETworkManagerNETworkManager是一款功能强大的网络管理与问题排除工具,该工具完全开源,可以帮助广大研究人员轻松管理目标网络系统并排除网络疑难问题。该工具使用远程桌面、Po...
- AMD也能深度学习+免费AI绘画:StableDiffusion+ROCm部署教程!
-
某国政客扇扇嘴皮子,CN玩硬件和深度学习的圈子里就掀起了一场风暴,这就是著名的嘴皮子效应(误)。没了高性能计算的A100H100倒也能理解,但是美利坚这波把RTX4090禁售了就让人无语了,所以不少做...
- windows 下编译 python_rtmpstream
-
最近在研究数字人,看了大咖的项目(https://github.com/lipku/metahuman-stream),尝试编译此项目的依赖项目python_rtmpstream(https://gi...
- 如何使用 Python 操作 Git 代码?GitPython 入门介绍
-
花下猫语:今天,我在查阅如何用Python操作Gitlab的时候,看到这篇文章,觉得还不错,特分享给大家。文中还提到了其它几种操作Git的方法,后续有机会的话,再陆续分享之~~作者:匿蟒...
- 网上看了不少,终于把ZlmediaKit流媒体框架搭建起来啦
-
你都站在2023年代了,视频通话、视频直播、视频会议、视频监控就是风口浪尖上的猪师兄,只要你学那么一丁点,拿个高薪的工作不过分吧!我也是半瓶子晃荡的,所以路人呀,共学习,同进步!本篇开始,只讲在Lin...
- MacDown:一款 macOS 的强大 Markdown 编辑器
-
大家好,很高兴又见面了,我是"...
- ZLMediaKit安装配置和推拉流
-
一、ZLMediaKit库简介ZLMediaKit是一个基于...
- 大神赞过的:学习 WebAssembly 汇编语言程序设计
-
文/阿里淘系F(x)Team-旭伦随着前端页面变得越来越复杂,javascript的性能问题一再被诟病。而Javascript设计时就不是为了性能优化设计的,这使得浏览器上可以运行的本地语言一...
- 【Docker】部署WVP视频监控平台
-
回来Docker系列,今天将会跟大家分享一则关于开源WVP视频监控平台的搭建。先说结论吧,一开始按照网上说的一步一步搭建没有搭建成功,不知道是版本太旧还是我这边机器有问题,尝试了好几个不同方式的搭建都...
- MongoDB+GridFS存储文件方案
-
GridFS是MongoDB的一个内置功能,它提供一组文件操作的API以利用MongoDB存储文件,GridFS的基本原理是将文件保存在两个Collection中,一个保存文件索引,一个保存文...
- 【开源】强大、创新且直观的 EDA套件
-
今天分享的LibrePCB是...
- Ollama如何制作自己的大模型?
-
背景Llama3发布了,这次用了...
- Ollama使用指南【超全版】
-
一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...
- 基于区块链的价值共享互联网即时通讯应用平台源码免费分享
-
——————关注转发之后私信回复【源码】即可免费获取到本项目所有源码基于区块链的价值共享互联网即时通讯应用平台,是一个去中心化的任何人都可以使用的通讯网络,是一款基于区块链的价值共享互联网即时通讯AP...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git 执行pull错误如何撤销 git pull fail
-
面试官:git pull是哪两个指令的组合?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git pull 之后本地代码被覆盖 解决方案
-
git命令之pull git.pull
-
- 最近发表
-
- 每天一个AI姬,AMD核显用户有福了,AI绘画打破 NVIDIA 显卡垄断
- NETworkManager:功能强大的网络管理与问题排除工具
- AMD也能深度学习+免费AI绘画:StableDiffusion+ROCm部署教程!
- windows 下编译 python_rtmpstream
- 如何使用 Python 操作 Git 代码?GitPython 入门介绍
- 网上看了不少,终于把ZlmediaKit流媒体框架搭建起来啦
- MacDown:一款 macOS 的强大 Markdown 编辑器
- ZLMediaKit安装配置和推拉流
- 大神赞过的:学习 WebAssembly 汇编语言程序设计
- 【Docker】部署WVP视频监控平台
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)