【MySQL】MySQL是如何实现事务的

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 【MySQL】MySQL是如何实现事务的

事务定义


事务:一个最小的不可再分的工作单元;一个事务通常对应一个完整的业务,例如银行账户转账业务,该业务就是一个最小的工作单元


一个完整的业务需要一组的DML( insert、update、delete)语句共同联合完成

事务只和DML语句有关,或者说DML语句才有事务。这个和业务逻辑有关,业务逻辑不同,DML语句的个数不同


事务特性


一个事务都必须包含四条基本特性,这四条特性一般称为ACID


  • Atomicity)原子性: 事务是最小的执行单位,不允许分割。原子性确保动作要么全部成功,要么完全全失败;
  • (Consistency)一致性: 执行事务前后,数据保持一致;
  • (Isolation)隔离性: 并发访问数据库时,一个事务不被其他事务所干扰。
  • (Durability)持久性: 一个事务被提交之后。对数据库中数据的改变是持久的,即使数据库发生故障。


接下来就介绍下,MySQL在innoDB引擎下是事务特性的


隔离性


为什么要先说隔离性呢?这是因为隔离性是事务最基础的特性,会涉及隔离级别、锁、MVCC等多个概念。


image.png


不同的隔离级别是为了解决不同的问题。也就是脏读、幻读、不可重复读。


  1. 脏读: 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。


  1. 不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。


  1. 幻读:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样,幻读是数据行记录变多了或者少了。


MySQL不同的隔离级别,可能存在的问题如下表

image.png

那么不同的隔离级别是怎么保证隔离性呢?答案是 锁 和 MVCC。


MySQL中的锁从粒度上来说分为表锁、页锁、行锁。


表锁有意向共享锁(IS)、意向排他锁(IX)、自增锁等。


行锁的种类共享锁(S)、共享锁  (X),行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。


三种行锁算法


  1. Record Lock记录锁:单个行记录上的锁。


  1. Gap Lock间隙锁,间隙锁就会对记录之间的间隙加锁,防止数据插入。就是我们在使用实时读(SELECT FOR … UPDATE)或者更新,为了防止读的过程中有新的数据插入,会对我们读的数据的左右区间进行加锁,防止其他事务插入数据,所以间隙锁之间是不排斥的,间隙锁排斥的只是插入数据的操作。


3.Next-Key Lock临键锁,会锁记录以及记录之间的间隙,就是 record lock 和 gap lock的组合,就是会对索引记录加记录锁 + 索引记录前面间隙上的锁”,就是对要更新的数据的左右两个端点加间隙锁,


锁和MVCC


大致介绍了下锁,可以看到。有了锁,当某事务正在写数据时,其他事务获取不到写锁,就无法写数据,一定程度上保证了事务间的隔离。但前面说,加了写锁,为什么其他事务也能读数据呢,不是获取不到读锁吗?这就是依靠MVCC(Multi-Version Concurrency Control)多版本的并发控制实现的。


Innodb 在存储每一行数据有一些额外的字段:DATA_TRX_ID和DATA_ROLL_PTR。


  • DATA_TRX_ID:数据行版本号。用来标识最近对本行记录做修改的事务 id。
  • DATA_ROLL_PTR:指向该行回滚段的指针。该行记录上所有旧版本,在 undo log 中都通过链表的形式组织。


ReadView


在每一条 SQL 开始的时候被创建,有几个重要属性:


  • trx_ids: 当前系统活跃(未提交)事务版本号集合。
  • low_limit_id: 创建当前 read view 时“当前系统最大事务版本号+1”。
  • up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号”
  • creator_trx_id: 创建当前read view的事务版本号;


此时查询


  • DATA_TRX_ID <up_limit_id :说明数据在当前事务之前就存在了,显示。
  • DATA_TRX_ID >= low_limit_id:说明该数据是在当前read view 创建后才产生的,数据不显示。


  • 根据 DATA_ROLL_PTR 从 undo log 中找到历史版本,找不到就空。
  • up_limit_id <DATA_TRX_ID <low_limit_id :就要看隔离级别了。


RR 级别的幻读


有了锁和 MVCC , 事务的隔离性得到解决。这里要引申一下,默认的 RR 的级别,解决了幻读吗?幻读通常针对的是 INSERT, 不可重复度则针对 UPDATE 。


快照读


普通的SELECT语句都是普通读,也就是读取的数据都是事务开始时那个状态的数据,普通读的幻读问题主要是通过MVCC来解决的,具体可以看上面的MVCC中的查询操作。


实时读


SELECT *** FOR UPDATE 在查询时会先申请X锁


SELECT *** LOCK IN SHARE MODE 在查询时会先申请S锁


就是实时读,就是读取的是实时的数据,而不快照数据,读的时候会加Next-Key Lock锁住当前的记录,以及左右两个区间的间隙,这样在读的时候就不能往我们的查询范围插入数据了。


原子性


前面有提到 undo log 回滚日志。隔离性的MVCC其实就是依靠它来实现的,原子性也是。实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。


当事务对数据库进行修改时,InnoDB会生成对应的 undo log;如果事务执行失败或调用了 rollback,导致事务需要回滚,便可以利用 undo log 中的信息将数据回滚到修改之前。undo log 属于逻辑日志,记录的是sql执行相关的信息。当发生回滚时,InnoDB 会根据 undo log 的内容做与之前相反的工作:


  • 对于每个 insert,回滚时会执行 delete;
  • 对于每个 delete,回滚时会执行insert;
  • 对于每个 update,回滚时会执行一个相反的 update,把数据改回去。


以update操作为例:当事务执行update时,其生成的undo log中会包含被修改行的主键(以便知道修改了哪些行)、修改了哪些列、这些列在修改前后的值等信息,回滚时便可以使用这些信息将数据还原到update之前的状态。


持久性


持久性依靠的是 redo log。MySQL 里经常说到的 WAL(Write-Ahead Logging) 技术 ,它的关键点就是先写日志,再写磁盘。


在做数据更新操作时,先将对数据页的更改记录到redo log,然后再去更新内存中的数据页,在下次查询数据页或者空闲时间,将操作记录更新到磁盘。这样可以将随机I/O改为顺序I/O。


优点是减少磁盘I/O次数,即便发生故障也可以根据redo log来将数据恢复到最新状态。

缺点是会造成内存脏页,后台线程会自动对脏页刷盘,或者是淘汰数据页时刷盘,此时会暂时查询操作,影响查询。


redo log 有两个特点:


  • 大小固定,循环写
  • crash-safe


对于redo log 是有两阶段的:commit 和 prepare 如果不使用“两阶段提交”,数据库的状态就有可能和用它的日志恢复出来的库的状态不一致.


两段提交制是什么?


更新时,先改内存中的数据页,将更新操作写入redo log日志,此时redo log进入prepare状态,然后通知MySQL Server执行完了,随时可以提交,MySQL Server将更新的SQL写入bin log,然后调用innodb接口将redo log设置为提交状态,更新完成。


可能你会疑问还有个 binlog 也是写操作并用于数据的恢复,有啥区别呢。


  • 层次:redo log 是 innoDB 引擎特有的,server 层的叫 binlog(归档日志)
  • 内容:redolog 是物理日志,记录“在某个数据页上做了什么修改”;binlog 是逻辑日志,是语句的原始逻辑,如“给 ID=2 这一行的 c 字段加 1 ”
  • 写入:redolog 循环写且写入时机较多,binlog 追加且在事务提交时写入


对于语句 update T set c=c+1 where ID=2;


  1. [执行器先找引擎取 ID=2 这一行。ID 是主键,直接用树搜索找到。如果 ID = 2 这一行所在数据页就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,再返回。]


  1. [执行器拿到引擎给的行数据,把这个值加上 1,N+1,得到新的一行数据,再调用引擎接口写入这行新数据。]


  1. [引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。]


  1. [执行器生成这个操作的 binlog,并把 binlog 写入磁盘。]


  1. [执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成]


崩溃恢复时的判断规则(以redolog是否commit或者binlog是否完整来确定)


  1. 如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;


  1. 如果 redo log 里面的事务只有完整的 prepare,则判断对应的事务 binlog 是否存在并完整: a. 如果是,则提交事务; b. 否则,回滚事务。


一致性


一致性是事务追求的最终目标,前面提到的原子性、持久性和隔离性,其实都是为了保证数据库状态的一致性。当然,上文都是数据库层面的保障,一致性的实现也需要应用层面进行保障。


也就是你的业务,比如购买操作只扣除用户的余额,不减库存,肯定无法保证状态的一致。

image.png


总结


MySQL事务应该大家都知道,但是实现原理可能就不是那么清楚,希望本文能对事务的了解有所帮助。


今天多学一点知识,明天就少说一句求人的话

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
MySQL锁机制:并发控制与事务隔离
本文深入解析了MySQL的锁机制与事务隔离级别,涵盖锁类型、兼容性、死锁处理及性能优化策略,助你掌握高并发场景下的数据库并发控制核心技巧。
|
2月前
|
存储 监控 Oracle
MySQL事务
MySQL事务具有ACID特性,包括原子性、一致性、隔离性和持久性。其默认隔离级别为可重复读,通过MVCC和间隙锁解决幻读问题,确保事务间数据的一致性和并发性。
MySQL事务
|
3月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
17天前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
161 0
|
1月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
|
3月前
|
安全 关系型数据库 MySQL
mysql事务隔离级别
事务隔离级别用于解决脏读、不可重复读和幻读问题。不同级别在安全与性能间权衡,如SERIALIZABLE最安全但性能差,READ_UNCOMMITTED性能高但易导致数据不一致。了解各级别特性有助于合理选择以平衡并发性与数据一致性需求。
162 1
|
10月前
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
4450 56
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
9月前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
403 7
MySQL事务日志-Undo Log工作原理分析
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
279 43
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
2231 14
MySQL事务日志-Redo Log工作原理分析

推荐镜像

更多