百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

一文学会Mysql(三)Mysql事务

mhr18 2025-03-02 18:06 40 浏览 0 评论

Mysql事务

1. ACID

在关系型数据库管理系统中,一个逻辑工作单元要成为事务,必须满足这 4 个特性,即所谓的 ACID: 原子性(Atomicity)、一致性( Consistency)、隔离性( Isolation )和持久性( Durability)。

1.1 原子性

原子性:事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。

修改---》 Bu?er Pool修改---》刷盘。可能会有下面两种情况:

  • 事务提交了,如果此时Bu?er Pool的脏页没有刷盘,这时候宕机了,如何保证修改的数据生效? Redo

  • 如果事务没提交,但是Bu?er Pool的脏页刷盘了,如何保证不该存在的数据撤销? Undo

每一个写事务,都会修改Bu?erPool ,从而产生相应的Redo/Undo日志,在Bu?er Pool 中的页被刷到 磁盘之前,这些日志信息都会先写入到日志文件中,如果 Bu?er Pool 中的脏页没有刷成功,此时数据 库挂了,那在数据库再次启动之后,可以通过 Redo 日志将其恢复出来,以保证脏页写的数据不会丢失。如果脏页刷新成功,此时数据库挂了,就需要通过Undo来实现了。

1.2 持久性

持久性:指的是一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,后续的操作或故障不 应该对其有任何影响,不会丢失。

如下图所示,一个“提交”动作触发的操作有: binlog落地、发送binlog、存储引擎提交、 ?ush_logs, check_point、事务提交标记等。这些都是数据库保证其数据完整性、持久性的手段。

MySQL的持久性也与WAL技术相关, redo log在系统Crash重启之类的情况时,可以修复数据,从而保 障事务的持久性。通过原子性可以保证逻辑上的持久性,通过存储引擎的数据刷盘可以保证物理上的持 久性。

1.3 隔离性

隔离性:指的是一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对其他的并 发事务是隔离的。

InnoDB 支持的隔离性有 4 种,隔离性从低到高分别为:读未提交、读提交、可重复读、串行化。锁和多版本并发控制( MVCC )技术就是用于保障隔离性的(后面课程详解)。

1.4 一致性

一致性:指的是事务开始之前和事务结束之后,数据库的完整性限制未被破坏。一致性包括两方面的内 容,分别是约束一致性和数据一致性。

约束一致性:创建表结构时所指定的外键、 Check、唯一索引等约束,可惜在 MySQL 中不支持 Check 。

数据一致性:是一个综合性的规定,因为它是由原子性、持久性、隔离性共同保证的结果,而不是 单单依赖于某一种技术。

一致性也可以理解为数据的完整性。数据的完整性是通过原子性、隔离性、持久性来保证的,而这3个 特性又是通过 Redo/Undo 来保证的。逻辑上的一致性,包括唯一索引、外键约束、 check 约束,这属 于业务逻辑范畴。

ACID 及它们之间的关系如下图所示, 4个特性中有3个与 WAL 有关系,都需要通过 Redo、 Undo 日志 来保证等。

WAL的全称为Write-Ahead Logging ,先写日志,再写磁盘。

2.并发事务控制

2.1 并发事务

事务并发处理可能会带来一些问题,比如:更新丢失、脏读、不可重复读、幻读等。

  • 更新丢失

当两个或多个事务更新同一行记录,会产生更新丢失现象。可以分为回滚覆盖和提交覆盖。

  • 回滚覆盖:一个事务回滚操作,把其他事务已提交的数据给覆盖了。

  • 提交覆盖:一个事务提交操作,把其他事务已提交的数据给覆盖了。

脏读

一个事务读取到了另一个事务修改但未提交的数据。

不可重复读

一个事务中多次读取同一行记录不一致,后面读取的跟前面读取的不一致。

幻读

一个事务中多次按相同条件查询,结果数量不一致。后续查询的结果和面前查询结果不同,多了或少了几行记录。

2.3 排队

最简单的方法,就是完全顺序执行所有事务的数据库操作,不需要加锁,简单的说就是全局排队。序列 化执行所有的事务单元,数据库某个时刻只处理一个事务操作,特点是强一致性,处理性能低。

2.2 排他锁

引入锁之后就可以支持并发处理事务,如果事务之间涉及到相同的数据项时,会使用排他锁,或叫互斥锁,先进入的事务独占数据项以后,其他事务被阻塞,等待前面的事务释放锁。

注意,在整个事务1结束之前,锁是不会被释放的,所以,事务2必须等到事务1结束之后开始。

2.3 读写锁

读和写操作:读读、写写、读写、写读。

读写锁就是进一步细化锁的颗粒度,区分读操作和写操作,让读和读之间不加锁,这样下面的两个事务 就可以同时被执行了。

读写锁,可以让读和读并行,而读和写、写和读、写和写这几种之间还是要加排他锁。

3.隔离级别

3.1 隔离级别类型

前面提到的“更新丢失”、”脏读”、“不可重复读”和“幻读”等并发事务问题,其实都是数据库一致性问题, 为了解决这些问题, MySQL数据库是通过事务隔离级别来解决的,数据库系统提供了以下 4 种事务隔 离级别供用户选择。

  • 读未提交

Read Uncommitted 读未提交:解决了回滚覆盖类型的更新丢失,但可能发生脏读现象,也就是 可能读取到其他会话中未提交事务修改的数据。

  • 已提交读

Read Committed 读已提交:只能读取到其他会话中已经提交的数据,解决了脏读。但可能发生 不可重复读现象,也就是可能在一个事务中两次查询结果不一致。

  • 可重复度

Repeatable Read 可重复读:解决了不可重复读,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上会出现幻读,简单的说幻读指的的当用户读取某一范围的数 据行时,另一个事务又在该范围插入了新行,当用户在读取该范围的数据时会发现有新的幻影行。

  • 可串行化

Serializable 串行化:所有的增删改查串行执行。它通过强制事务排序,解决相互冲突,从而解决 幻度的问题。这个级别可能导致大量的超时现象的和锁竞争,效率低下。

数据库的事务隔离级别越高,并发问题就越小,但是并发处理能力越差(代价)。读未提交隔离级别最 低,并发问题多,但是并发处理能力好。以后使用时,可以根据系统特点来选择一个合适的隔离级别, 比如对不可重复读和幻读并不敏感,更多关心数据库并发处理能力,此时可以使用Read Commited隔 离级别。

事务隔离级别,针对Innodb引擎,支持事务的功能。像MyISAM引擎没有关系。

事务隔离级别和锁的关系

1 )事务隔离级别是SQL92定制的标准,相当于事务并发控制的整体解决方案,本质上是对锁和MVCC使 用的封装,隐藏了底层细节。

2)锁是数据库实现并发控制的基础,事务隔离性是采用锁来实现,对相应操作加不同的锁,就可以防 止其他事务同时对数据进行读写操作。

3 )对用户来讲,首先选择使用隔离级别,当选用的隔离级别不能解决并发问题或需求时,才有必要在 开发中手动的设置锁。

MySQL默认隔离级别:可重复读

Oracle、SQLServer默认隔离级别:读已提交

一般使用时,建议采用默认隔离级别,然后存在的一些并发问题,可以通过悲观锁、乐观锁等实现处 理。

3.2 MySQL隔离级别控制

MySQL默认的事务隔离级别是Repeatable Read ,查看MySQL当前数据库的事务隔离级别命令如下:

show variables like 'tx_isolation';

select @@tx_isolation;

设置事务隔离级别可以如下命令:

set tx_isolation='READ-UNCOMMITTED';

set tx_isolation='READ-COMMITTED';

set tx_isolation='REPEATABLE-READ';

set tx_isolation='SERIALIZABLE';

4.MVCC(重点)

简介

什么是 MVCC ?

MVCC(Multi-Version Concurrency Control)即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。MVCC使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能。

MVCCMySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

什么是当前读和快照读?

在学习 MVCC 多版本并发控制之前,我们必须先了解一下,什么是 MySQL InnoDB 下的当前读和快照读?

  • 当前读像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读。

    为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

  • 快照读像不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的实现是基于多版本并发控制,而有可能是之前的历史版本。

MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是 快照读 , 而非当前读。

当前读实际上是一种加锁的操作,是悲观锁的实现

当前读,快照读和MVCC的关系

  • MVCC 多版本并发控制是 「维持一个数据的多个版本,使得读写操作没有冲突」 的概念,只是一个抽象概念,并非实现
  • 因为 MVCC 只是一个抽象概念,要实现这么一个概念,MySQL 就需要提供具体的功能去实现它,「快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能」。而相对而言,当前读就是悲观锁的具体功能实现
  • 要说的再细致一些,快照读本身也是一个抽象概念,再深入研究。MVCC 模型在 MySQL 中的具体实现则是由 3 个隐式字段undo 日志Read View 等去完成的,具体可以看下面的 MVCC 实现原理

MVCC 能解决什么问题,好处是?

数据库并发场景有三种,分别为:

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
  • 写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

MVCC 带来的好处是?多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题

  • 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
  • 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

如何使用MVCC解决并发问题?有了 MVCC,所以我们可以形成两个组合:

  • MVCC + 悲观锁MVCC解决读写冲突,悲观锁解决写写冲突
  • MVCC + 乐观锁MVCC 解决读写冲突,乐观锁解决写写冲突

这种组合的方式就可以最大程度的提高数据库并发性能,并解决读写冲突,和写写冲突导致的问题

MVCC 的实现原理

MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段undo日志Read View 来实现的。所以我们先来看看这个三个 point 的概念

隐式字段

每行记录除了我们自定义的字段外,还有数据库隐式定义的 DB_TRX_ID, DB_ROLL_PTR, DB_ROW_ID等字段

  • DB_TRX_ID6 byte,最近修改(修改/插入)事务 ID:记录创建这条记录/最后一次修改该记录的事务 ID
  • DB_ROLL_PTR7 byte,回滚指针,指向这条记录的上一个版本的undo log
  • DB_ROW_ID6 byte,隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID产生一个聚簇索引

undo日志

undo log 主要分为两种:

  • insert undo log代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
  • update undo log事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除

对 MVCC 有帮助的实质是 update undo log ,undo log 实际上就是存在 rollback segment 中旧记录链,

执行流程:

一、 比如一个有个事务插入 persion 表插入了一条新记录

记录如下, name 为 Jerry , age 为 24 岁, 隐式主键 是 1, 事务 ID 回滚指针 ,我们假设为 NULL

二、 现在来了一个 事务 1 对该记录的 name 做出了修改,改为 Tom

  • 在事务 1修改该行(记录)数据时,数据库会先对该行加排他锁
  • 然后把该行数据拷贝到 undo log 中,作为旧记录,既在 undo log 中有当前行的拷贝副本
  • 拷贝完毕后,修改该行name为Tom,并且修改隐藏字段的事务 ID 为当前事务 1的 ID, 我们默认从 1 开始,之后递增,回滚指针指向拷贝到 undo log 的副本记录,既表示我的上一个版本就是它
  • 事务提交后,释放锁

三、 又来了个事务 2 修改person 表 的同一个记录,将age 修改为 30 岁

  • 在事务2修改该行数据时,数据库也先为该行加锁
  • 然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log 了,那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面
  • 修改该行 age 为 30 岁,并且修改隐藏字段的事务 ID 为当前事务 2的 ID, 那就是 2 ,回滚指针指向刚刚拷贝到 undo log 的副本记录
  • 事务提交,释放锁

不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线链表,undo log 的链首就是最新的旧记录,链尾就是最早的旧记录

Read View 读视图

什么是 Read View?

**Read View 就是事务进行快照读操作的时候生产的读视图 (Read View)**,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID

我们可以把 Read View 简单的理解成有三个全局属性

  • 活跃事务列表

    • 一个数值列表
    • 用于维护 Read View 生成时刻系统 正活跃的事务 ID 列表
  • up_limit_id

    • lower water mark
    • 是 trx_list 列表中事务 ID 最小的 ID
  • low_limit_id

    • hight water mark
    • ReadView 生成时刻系统尚未分配的下一个事务 ID ,也就是 目前已出现过的事务 ID 的最大值 + 1
    • 为什么是 low_limit ? 因为它也是系统此刻可分配的事务 ID 的最小值

这两个 ID 其实就可以从当前执行的事务的视角,将所有的事务分为三个部分

  • 小于低水位的部分一定是当前事务开始前就提交了的部分

  • 大于等于高水位的则一定是还未提交的事务,我们一定不可见

  • 处于中间的部分就要分类讨论了:

    • 如果在视图数组中,说明当前事务开始时,这些事务仍在活跃,所以应该是不可见的;
    • 如果不在数组中,说明在仍活跃着的事务范围内,但其中有一些事务虽然不是开始最早的,但是结束的却比活跃数组中的事务早,以至于当前事务开始时,这些事务已经结束,所以就应该是可见的。

可见事务id总结

要么比低水位更早,要么比高水位的 id 小但是不能出现在活跃事物数组中。

整体流程

流程模拟

  • 当事务 2对某行数据执行了快照读,此时还有事务1和事务3在活跃中,事务 4在事务 2快照读前一刻提交更新了。

事务 1事务 2事务 3事务 4事务开始事务开始事务开始事务开始………修改且已提交进行中快照读进行中………

  • up_limit_id 就是1,low_limit_id 就是 4 + 1 = 5,活跃事务列表值是 1, 3,Read View 如下图

  • 我们的例子中,只有事务 4 修改过该行记录,并在事务 2 执行快照读前,就提交了事务。所以当前DB_TRX_ID 为4

  • 拿该记录 DB_TRX_ID 字段记录的事务 ID 4 去跟 Read View 的 up_limit_id 比较

    • DB_TRX_ID(4)大于 up_limit_id(1),所以不符合可见条件
    • 继续判断 DB_TRX_ID(4) 是否大于等于 low_limit_id(5),不符和不可见条件
    • 最后判断 DB_TRX_ID(4) 是否处于 trx_list 中的活跃事务, 最后发现事务 ID 为 4 的事务不在当前活跃事务列表中, 符合可见性条件
    • 如果上述三点判断后不符合可见性,则去undolog链中找上一条记录的DB_TRX_ID,重复123步查找

    所以事务 4修改后提交的最新结果对事务 2 快照读时是可见的,获取字段的值为'A'

MVCC 相关问题

RR 是如何在 RC 级的基础上解决不可重复读的?

Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同

当前读和快照读在 RR 级别下的区别:

表1:

事务A事务B开启事务开启事务快照读(无影响)查询金额为500快照读查询金额为500更新金额为400提交事务select 快照读金额为500select lock in share mode当前读金额为400

在上表的顺序下,事务 B 的在事务 A 提交修改后的快照读是旧版本数据,而当前读是实时新数据 400

表2:

事务A事务B开启事务开启事务快照读(无影响)查询金额为500更新金额为400提交事务select 快照读金额为400select lock in share mode当前读金额为400

而在表 2这里的顺序中,事务 B 在事务 A 提交后的快照读和当前读都是实时的新数据 400,这是为什么呢?

表2的第一次快照读在事务A更新金额为400而且commit之后发生

RC , RR 级别下的 InnoDB 快照读有什么不同?

正是 Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同

在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;

而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View。

相关推荐

Redis合集-使用benchmark性能测试

采用开源Redis的redis-benchmark工具进行压测,它是Redis官方的性能测试工具,可以有效地测试Redis服务的性能。本次测试使用Redis官方最新的代码进行编译,详情请参见Redis...

Java简历总被已读不回?面试挂到怀疑人生?这几点你可能真没做好

最近看了几十份简历,发现大部分人不是技术差,而是不会“卖自己”——一、简历死穴:你写的不是经验,是岗位说明书!反面教材:ד使用SpringBoot开发项目”ד负责用户模块功能实现”救命写法:...

redission YYDS(redission官网)

每天分享一个架构知识Redission是一个基于Redis的分布式Java锁框架,它提供了各种锁实现,包括可重入锁、公平锁、读写锁等。使用Redission可以方便地实现分布式锁。red...

从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道

2023年6月18日我们维护的电商平台在零点刚过3秒就遭遇了严重事故。监控大屏显示某爆款手机SKU_IPHONE13_PRO_MAX在库存仅剩500台时,订单系统却产生了1200笔有效订单。事故复盘发...

SpringBoot系列——实战11:接口幂等性的形而上思...

欢迎关注、点赞、收藏。幂等性不仅是一种技术需求,更是数字文明对确定性追求的体现。在充满不确定性的网络世界中,它为我们建立起可依赖的存在秩序,这或许正是技术哲学最深刻的价值所在。幂等性的本质困境在支付系...

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享在高流量场景下。首先,我需要回忆一下常见的优化策略,比如负载均衡、缓存、数据库优化、微服务拆分这些。不过,可能还需要考虑用户的具体情况,比...

Java面试题: 项目开发中的有哪些成长?该如何回答

在Java面试中,当被问到“项目中的成长点”时,面试官不仅想了解你的技术能力,更希望看到你的问题解决能力、学习迭代意识以及对项目的深度思考。以下是回答的策略和示例,帮助你清晰、有说服力地展示成长点:一...

互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?

你有没有遇到过这样的情况?在电商大促时,系统上线了抢券活动,结果活动刚一开始,服务器就不堪重负,出现超卖、系统崩溃等问题。又或者用户疯狂点击抢券按钮,最后却被告知无券可抢,体验极差。作为互联网大厂的后...

每日一题 |10W QPS高并发限流方案设计(含真实代码)

面试场景还原面试官:“如果系统要承载10WQPS的高并发流量,你会如何设计限流方案?”你:“(稳住,我要从限流算法到分布式架构全盘分析)…”一、为什么需要限流?核心矛盾:系统资源(CPU/内存/数据...

Java面试题:服务雪崩如何解决?90%人栽了

服务雪崩是指微服务架构中,由于某个服务出现故障,导致故障在服务之间不断传递和扩散,最终造成整个系统崩溃的现象。以下是一些解决服务雪崩问题的常见方法:限流限制请求速率:通过限流算法(如令牌桶算法、漏桶算...

面试题官:高并发经验有吗,并发量多少,如何回复?

一、有实际高并发经验(建议结构)直接量化"在XX项目中,系统日活用户约XX万,核心接口峰值QPS达到XX,TPS处理能力为XX/秒。通过压力测试验证过XX并发线程下的稳定性。"技术方案...

瞬时流量高并发“保命指南”:这样做系统稳如泰山,老板跪求加薪

“系统崩了,用户骂了,年终奖飞了!”——这是多少程序员在瞬时大流量下的真实噩梦?双11秒杀、春运抢票、直播带货……每秒百万请求的冲击,你的代码扛得住吗?2025年了,为什么你的系统一遇高并发就“躺平”...

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。比如上周有个小伙伴找我,五年经验但简历全是'参与系统设计''优化接口性能'这种空话。我就问他:你做的秒杀...

PHP技能评测(php等级考试)

公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...

你的简历在HR眼里是青铜还是王者?

你的简历在HR眼里是青铜还是王者?兄弟,简历投了100份没反应?面试总在第三轮被刷?别急着怀疑人生,你可能只是踩了这些"隐形求职雷"。帮3630+程序员改简历+面试指导和处理空窗期时间...

取消回复欢迎 发表评论: