代码编织梦想

事务原理

原子性,一致性,持久性基于redo log 和 undo log

隔离性:锁+MVCC

redo log

重做日志,记录的是事务提交时数据页的物理修改,是用来实现事物的持久性.

该日志文件由两部分组成:重做缓冲区(read log buffer),以及重做日志文件(read log file),前者实在内存中,后者在磁盘中,当事务提交后,会把所有修改信息都存到该日志里没用与在刷新脏页到磁盘发生错误时,进行数据恢复使用.

undo log

回滚日志,用于记录数据被修改前的信息,作用包含两个:提供回滚和MVCC(多版本并发控制),undo log 和redo log记录物理日志不一样,他是逻辑日志,可以认为当delete一条记录时,undo log会记录一条对应的insert记录,反之亦然.

Undo log销毁:undo log在事务执行时产生,事务提交时不会立即删除undo log这些日志还可能用于MVCC

Undo log存储:undo log 采用段的方式进行管理和记录,存放在rollback segment回滚段中,内部包含1024个undo log segment

MVCC(多版本并发控制)

  • 当前读:读取的记录是最新的版本,读取时还要保证方其他并发事务不能修改当前记录,会对读取的记录进行加锁.

  • 快照读:简单的select语句(不加锁)就是快照读,读取的是记录数据的可见版本,有的是历史版本,不加锁,是非阻塞读.

    • Read Committed:每次select,都生成一个快照读.
    • Repeatable Read:开启事务后第一个select语句才是快照读的地方
    • Serializable:快照读会退化成当前读
  • 记录中的隐式字段

    1. DB_TRX_ID

      最近修改事务id,记录插入这条记录或最后一次修改该记录的事物ID.

    2. DB_ROLL_PTR

      回滚指针,指向这条记录的上一个版本,用于配合undo log使用,指向undo log

    3. DB_ROW_ID

      隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段

  • ReadView(读视图)

    可重复读隔离级是由 MVCC(多版本并发控制)实现的,实现的方式是开始事务后(执行 begin 语句后),在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好了避免幻读问题。ReadView是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务id,ReadView中包含了四个核心字段:

    1. m_ids:当前活跃的事物id集合,**“活跃事务”**指的就是,启动了但还没提交的事务。

    2. min_trx_id:最小活跃事物id,也就是 m_ids 的最小值。

    3. max_trx_id:这个并不是 m_ids 的最大值,而是创建 Read View 时当前数据库中应该给下一个事务的 id 值,也就是全局事务中最大的事务 id 值 + 1;

    4. creator_trx_id:指的是创建该 Read View 的事务的事务 id

      在创建 Read View 后,我们可以将记录中的 trx_id 划分这三种情况:

img

  • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 已经提交的事务生成的,所以该版本的记录对当前事务可见
  • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 才启动的事务生成的,所以该版本的记录对当前事务不可见
  • 如果记录的 trx_id 值在 Read View 的min_trx_id和max_trx_id之间,需要判断 trx_id 是否在 m_ids 列表中:
    • 如果记录的 trx_id m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见
    • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见

mysql日志

undo log(回滚日志)

是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC

img

为什么需要 undo log?

如果一个事务在执行过程中,在还没有提交事务之前MYSQL发生了崩溃,而undo log就会记录没次事务开始前的的数据,保证了事务的原子性,

一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:

  • 通过 trx_id 可以知道该记录是被哪个事务修改的;
  • 通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;

image-20240516150446115

TIP

很多人疑问 undo log 是如何刷盘(持久化到磁盘)的?

undo log 和数据页的刷盘策略是一样的,都需要通过 redo log 保证持久化。

buffer pool 中有 undo 页,对 undo 页的修改也都会记录到 redo log。redo log 会每秒刷盘,提交事务时也会刷盘,数据页和 undo 页都是靠这个机制保证持久化的。

为什么需要 Buffer Pool?

MySQL 的数据都是存在磁盘中的,那么我们要更新一条记录的时候,得先要从磁盘读取该记录,然后在内存中修改这条记录。那修改完这条记录是选择直接写回到磁盘,还是选择缓存起来呢?

当然是缓存起来好,这样下次有查询语句命中了这条记录,直接读取缓存中的记录,就不需要从磁盘获取数据了。为此,Innodb 存储引擎设计了一个缓冲池(Buffer Pool),来提高数据库的读写性能。

有了 Buffer Poo 后:

  • 当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。
  • 当修改数据时,如果数据存在于 Buffer Pool 中,那直接修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页(该页的内存数据和磁盘上的数据已经不一致),为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。

Buffer Pool 缓存什么?

InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool 同样需要按「页」来划分。

在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。

Buffer Pool 除了缓存「索引页」和「数据页」,还包括了 Undo 页,插入缓存、自适应哈希索引、锁信息等等。

img

Undo 页是记录什么?

开启事务后,InnoDB 层更新记录前,首先要记录相应的 undo log,如果是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面。

不是的。

当我们查询一条记录时,InnoDB 是会把整个页的数据加载到 Buffer Pool 中,将页加载到 Buffer Pool 后,再通过页里的「页目录」去定位到某条具体的记录。

为什么需要 redo log ?

Buffer Pool 是提高了读写效率没错,但是问题来了,Buffer Pool 是基于内存的,而内存总是不可靠,万一断电重启,还没来得及落盘的脏页数据就会丢失。

为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来,这个时候更新就算完成了

后续,InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里,这就是 WAL (Write-Ahead Logging)技术

什么是 redo log?

redo log 是物理日志,记录了某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志。

在事务提交时,只要先将 redo log 持久化到磁盘即可,可以不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘。

当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,接着 MySQL 重启后,可以根据 redo log 的内容,将所有数据恢复到最新的状态。

redo log 和 undo log 区别在哪?

  • redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值;
  • undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值;

事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务,事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务

  • 实现事务的持久性,让 MySQL 有 crash-safe 的能力,能够保证 MySQL 在任何时间段突然崩溃,重启后之前已提交的记录都不会丢失;
  • 将写操作从「随机写」变成了「顺序写」,提升 MySQL 写入磁盘的性能。

产生的 redo log 是直接写入磁盘的吗?

不是的。

实际上, 执行一个事务的过程中,产生的 redo log 也不是直接写入磁盘的,因为这样会产生大量的 I/O 操作,而且磁盘的运行速度远慢于内存。

所以,redo log 也有自己的缓存—— redo log buffer,每当产生一条 redo log 时,会先写入到 redo log

为什么需要 binlog ?

前面介绍的 undo log 和 redo log 这两个日志都是 Innodb 存储引擎生成的。

MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件。

binlog 文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作,比如 SELECT 和 SHOW 操作。

redo log 和 binlog 有什么区别?

1、适用对象不同:

  • binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;
  • redo log 是 Innodb 存储引擎实现的日志;

2、文件格式不同:

  • binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:
    • STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,所以针对这种格式, binlog 可以称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如你用了 uuid 或者 now 这些函数,你在主库上执行的结果并不是你在从库执行的结果,这种随时在变的函数会导致复制的数据不一致;
    • ROW:记录行数据最终被修改成什么样了(这种格式的日志,就不能称为逻辑日志了),不会出现 STATEMENT 下动态函数的问题。但 ROW 的缺点是每行数据的变化结果都会被记录,比如执行批量 update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
    • MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
  • redo log 是物理日志,记录的是在某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新;

3、写入方式不同:

  • binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志。
  • redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志。

4、用途不同:

  • binlog 用于备份恢复、主从复制;
  • redo log 用于掉电等故障恢复。

主从复制是怎么实现?

MySQL 的主从复制依赖于 binlog ,也就是记录 MySQL 上的所有变化并以二进制形式保存在磁盘上。复制的过程就是将 binlog 中的数据从主库传输到从库上。

这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成。

MySQL 主从复制过程

MySQL 集群的主从复制过程梳理成 3 个阶段:

  • 写入 Binlog:主库写 binlog 日志,提交事务,并更新本地存储数据。
  • 同步 Binlog:把 binlog 复制到所有从库上,每个从库把 binlog 写到暂存日志中。
  • 回放 Binlog:回放 binlog,并更新存储引擎中的数据。

为什么需要两阶段提交?

事务提交后,redo log 和 binlog 都要持久化到磁盘,但是这两个是独立的逻辑,可能出现半成功的状态,这样就造成两份日志之间的逻辑不一致。

  • 如果在将 redo log 刷入到磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入。MySQL 重启后,通过 redo log 能将 Buffer Pool 中 id = 1 这行数据的 name 字段恢复到新值 xiaolin,但是 binlog 里面没有记录这条更新语句,在主从架构中,binlog 会被复制到从库,由于 binlog 丢失了这条更新语句,从库的这一行 name 字段是旧值 jay,与主库的值不一致性;
  • 如果在将 binlog 刷入到磁盘之后, MySQL 突然宕机了,而 redo log 还没有来得及写入。由于 redo log 还没写,崩溃恢复以后这个事务无效,所以 id = 1 这行数据的 name 字段还是旧值 jay,而 binlog 里面记录了这条更新语句,在主从架构中,binlog 会被复制到从库,从库执行了这条更新语句,那么这一行 name 字段是新值 xiaolin,与主库的值不一致性;

可以看到,在持久化 redo log 和 binlog 这两份日志的时候,如果出现半成功的状态,就会造成主从环境的数据不一致性。这是因为 redo log 影响主库的数据,binlog 影响从库的数据,所以 redo log 和 binlog 必须保持一致才能保证主从数据一致。

MySQL 为了避免出现两份日志之间的逻辑不一致的问题,使用了「两阶段提交」来解决,两阶段提交其实是分布式事务一致性协议,它可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功的状态

两阶段提交把单个事务的提交拆分成了 2 个阶段,分别是「准备(Prepare)阶段」和「提交(Commit)阶段」,每个阶段都由协调者(Coordinator)和参与者(Participant)共同完成。注意,不要把提交(Commit)阶段和 commit 语句混淆了,commit 语句执行的时候,会包含提交(Commit)阶段

两阶段提交有什么问题?

两阶段提交虽然保证了两个日志文件的数据一致性,但是性能很差,主要有两个方面的影响:

  • 磁盘 I/O 次数高:对于“双1”配置,每个事务提交都会进行两次 fsync(刷盘),一次是 redo log 刷盘,另一次是 binlog 刷盘。
  • 锁竞争激烈:两阶段提交虽然能够保证「单事务」两个日志的内容一致,但在「多事务」的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_74347016/article/details/139045254

mysql事务原理浅析_寻筝的博客-爱代码爱编程

前言 ​ 因为自己对数据的可靠性,可用性方面特别感兴趣,所以在MySQL事务方面看了很多资料,也看了很多博客,所以想到自己也写一篇博客整理整理自己所学内容,尽量用自己的语言解释得通俗易懂。 事务经典场景 ​ 在很多介绍

详解MySQL事务原理-爱代码爱编程

什么是事务? 在MySQL中的事务是由存储引擎实现的,而且支持事务的存储引擎不多,我们主要讲解InnoDB存储引擎中的事务。 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。 事务用来管理 DDL、DML、DCL 操作,比如 insert,update,delete 语句,默认是自动提交的。 事务的四大特性

MySQL事务实现原理-爱代码爱编程

文章目录 一、事务的概念二、如何实现原子性——Undo Log2.1 插入操作对应的undo log2.2 删除操作对应的undo log2.3 更新操作对应的undo log三、如何实现隔离性——MVCC和锁四、如何实现持久性——Redo Log和Binlog4.1 Redo Log4.2 Binary Log4.3 数据恢复五、总结 一、事

Mysql事务原理拆解-爱代码爱编程

1.mysql事务基本概念 事务特性 ACID A 原子性 I 隔离性 D 持久性 C 一致性 并发问题 脏读 读取到未提交的数据。 不可重复读 两次读取结果不同 幻读 select操作得到的结果所表征的数据状态无法支撑后续的业务操作。 隔离级别 2.mysql事务实现原理 MVCC ●多版本并发控制 ●解决读写冲突 ●隐藏列

MySQL事务原理-爱代码爱编程

实际上,我们研究事务的原理,就是研究MySQL的InnoDB引擎是如何保证事务的四大特性的。 而对于这四大特性,实际上分为两个部分。 其中的原子性、一致性、持久性,实际上是由InnoDB中的两份日志来保证的,一份是redo log日志,一份是undo log日志。 而隔离性是通过数据库的锁, 加上MVCC来保证的。 一、redo log

Mysql事务原理详解-爱代码爱编程

事物 目的 事务将数据库从一种一致性状态转换为另一种一致性状态; 组成 事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成; Innodb支持事务,Myisam是不支持事务的。这个是Myisam和Innodb一个主要的区别。 对于一条SQL语句,Innodb默认是加上事务的。 Myisam为什么不支持事务? 因为Myis

mysql 事务原理详解_用户昵称23的博客-爱代码爱编程

前言 事务是mysql Innodb引擎的一大特点,可以说,在日常开发中,对于mysql事务的使用无处不在,因此深入了解并掌握mysql的事务原理很有必要。 一、mysql事务简介 事务 是一组操作集合,一个不可分割的

mysql事务的实现原理-爱代码爱编程

核心日志 1、Redo Log 2、Undo Log 3、BinLog redoLog,保持继续执行恢复没有写入的功能日志,物理日志,innoDB独有。 undoLog,回滚操作,生成与执行相反的逻辑日志,且提前会有快照放在undoLog里面,有备份。 BinLog,二进制日志,mysql自身日志,主要是主从复制,数据恢复(使用mysq

js遇到需要正则匹配来修改img标签+清除行内样式-爱代码爱编程

方法一 var regex0 = new RegExp("(i?)(\<img)([^\>]+\>)", "gmi") //正则匹配表达式 this.newcontent = this.content.replace(regex0,"$2 style='display:block;margin: auto;width:120px;'

python学习-爱代码爱编程

核心代码 # 导入pandas库 import pandas as pd # 导入正则表达式包 import re # 指定Excel文件的路径,这个data.xlsx表为原始表,表内有40个sheet子表 file_p

mysql事务实现原理_mysql事务的实现原理-爱代码爱编程

MySQL事务实现原理主要基于ACID(原子性、一致性、隔离性、持久性)原则。MySQL使用InnoDB存储引擎来支持事务,并采用多版本并发控制(MVCC)来实现事务的隔离性。 1.事务的特性  原子性(Atomicity): 事务是一个原子操作,要么全部执行成功,要么全部失败回滚。MySQL通过事务日志(undo log)来实现原子性,它记录了事务

mysql-爱代码爱编程

1、适合创建索引的情况 1.1、字段数值具有唯一性的限制 索引本身是具有约束作用的,比如创建唯一索引、主键索引在数据表中如果某个字段是唯一性的,可以创建唯一性索引或主键索引 在业务场景中具有唯一特性的字段,即使是