想要深入 Mysql,其日志系统的底层原理必不可少,在面试中也经常遇到此类问题
今天做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
先简单了解一下MySQL大概的架构:
第1层 - 连接层:
连接层对来自客户端的连接进行权限验证并将相关的连接信息维护到连接池中,以便于下次连接。
第2层 - 服务层:
提供NoSQL,SQL的API、SQL解析、SQL语句优化、SQL语句缓存等相关组件。
第3层 - 存储引擎层:
提供了一系列可插拔的存储引擎,我们可以通过存储引擎来进行数据的写入与读取,通过存储引擎,我们可以真正的与硬盘中的数据和日志进行交互,我们可以根据需求来选择合适的存储引擎进行使用。
第4层 - 文件系统层:
该层包含了具体的日志文件和表数据文件以及MySQL相关的程序。
上面的四个部分,MySQL 的架构核心两个部分:服务层(Server Layer)和 存储引擎层(Storage Engine Layer)。
其中,服务层负责 MySQL 的核心逻辑处理,而存储引擎层负责实际的数据存取。
主要负责以下功能:
将客户端发送的 SQL 查询转化为可理解的内部结构,检查 SQL 语句是否符合 MySQL 语法规则。查询重写:服务层会将一些查询优化为等效的、执行效率更高的形式,比如将子查询改写为 JOIN,或简化复杂的表达式。查询优化:MySQL 服务层有一个查询优化器,会基于数据统计信息,选择最优的执行计划。包括表的连接顺序、索引的选择等。其优化方式包括基于成本的优化器(CBO)。
查询缓存MySQL 服务器层可以将一些查询的结果缓存起来,尤其是频繁执行的查询。如果客户端请求的查询已经存在于缓存中,MySQL 可以直接从缓存中返回结果,而无需重新执行查询。
SQL 执行在查询解析和优化之后,服务层负责将 SQL 语句执行。服务层会将解析后的查询计划传递给存储引擎层,存储引擎负责实际的数据操作。
服务层中包含了三大组件
解析器是 SQL 查询执行的第一步,它的职责是将用户发送的 SQL 语句解析为数据库能够理解的内部结构。
解析完成后,生成一个中间表示结构,交由下一步进行处理。
优化器负责选择最优的执行计划,使查询能够以最高效的方式运行。
经过优化后,优化器会生成一个查询执行计划,并交给执行器处理。
执行器的任务是按照优化器生成的执行计划,逐步执行查询,访问数据并返回结果。
这三个组件相互协作,完成从接收到 SQL 查询到返回结果的整个过程。
Mysql数据更新语句的流程, 大致如下图所示:
具体更新一条记录 update table set name=“李四” where id = 1000;
的流程如下:
- 如果 id=1000 这一行所在的数据页本来就在 buffer pool 中,就直接返回给执行器更新;
- 如果记录不在 buffer pool,将数据页从磁盘读入到 buffer pool,返回记录给执行器。
- 如果一样的话就不进行后续更新流程;
- 如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作;
InnoDB 层更新记录前,InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要旧值记下来,也就是要生成一条 undo log,undo log 不是之间写磁盘,而是会写入Buffer Pool , 只是会写入 Buffer Pool 中的 Undo 页面。
不过,这里也有WAL思想,在修改该 Undo 页面前,需要先记录对应的 redo log,所以,这里的流程是:先记录修改 Undo 页面的 redo log ,然后再真正的修改 Undo 页面。
提示
Undo Log 的第一个目标就是为了保证事务的原子性(Atomicity)。事务的原子性意味着事务中的所有操作要么全部成功,要么全部回滚到事务开始之前的状态,不会出现部分执行的情况。如果事务在执行过程中发生错误,或者用户明确发起了回滚操作(ROLLBACK),数据库会通过读取 Undo Log 中记录的旧值,恢复被修改的数据到事务开始前的状态。这种恢复操作确保了事务的原子性,即便事务中途失败,数据库也能够恢复到一致的状态。
当然,Undo Log 还用于实现 MVCC(Multi-Version Concurrency Control)。当不同事务同时读取数据时,Undo Log 提供了“旧版本”的数据视图,使得即使某个事务在修改数据,其他事务仍然能够读取到事务开始时的数据版本。这不仅提升了并发性能,也间接支持了原子性和隔离性的实现。所以,Undo Log 实现了对 事务的 原子性、隔离性的支持。
面试的时候,一般人都知道 undo log 实现了事务的原子性, 如果把undo log的隔离性说出来,一定会惊讶到 面试官的。
InnoDB 层开始更新数据记录之前,根据 WAL 思想,先记录修改数据页面的 redo log ,然后再真正的修改数据页面。
修改数据页面的过程是修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,而不是之间写磁盘。为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。至此,一条记录更新完了。
提示
Redo Log
是实现事务 持久性(Durability) 的关键机制之一。事务的持久性意味着一旦事务提交成功,即使数据库系统发生崩溃,系统也能够在重启后保证数据不会丢失。Redo Log 通过记录事务的修改并在崩溃后进行恢复,确保数据库的一致性和数据的持久性。
在一条更新语句执行完成后,然后开始记录该语句对应的 binlog。此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件。在事务提交时,才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。
提示
Binlog(Binary Log)
在 MySQL 中主要用于 主从复制 和 数据恢复,它通过协调 redo log 和 binlog 的写入,确保事务的一致性( 包括两个一致性:崩溃一致性&主从一致性),特别是在分布式场景下的 主从一致性。
- prepare 阶段:将 redo log 对应的事务状态设置为 prepare,然后将 redo log 刷新到硬盘;
- commit 阶段:将 binlog 刷新到磁盘,接着调用引擎的提交事务接口,将 redo log 状态设置为 commit(将事务设置为 commit 状态后,刷入到磁盘 redo log 文件);
提示
原始的两阶段提交 的性能比较低,更高性能的提交方式是 组提交,说白了就是批量提交。但是原始的两阶段提交比较好理解,为了方便说明,这里不说组提交的过程,只说两阶段提交.至此,一条更新语句执行完成。
以下面一条简单的 SQL 语句为例,我们来解释下执行器和 InnoDB 存储引擎在更新时做了哪些事情
sqlupdate table set name=“李四” where id = 1000;
注意不要把这里的提交事务和我们 sql 语句中的提交事务 commit 命令搞混了哈,我们这里说的提交事务,指的是事务提交过程中的一个小步骤,也是最后一步。当这个步骤执行完成后,commit 命令就执行成功了。
可以看到,所谓两阶段提交,其实就是 把 redo log 的写入拆分成了两个步骤:prepare 和 commit。
所以,为什么要这样设计呢?这样设计怎么就能够实现崩溃恢复呢?
根据两阶段提交,崩溃恢复时的判断规则是这样的:
如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交
如果 redo log 里面的事务处于 prepare 状态,则判断对应的事务 binlog 是否存在并完整
如果 binlog 存在并完整,则提交事务;否则,回滚事务。
结合上面的两阶段提交, 实现了事务的持久性 和一致性。实际的场景中,上图片中的写入 redo log 和写入 bin log,并不等同于写入磁盘文件,可能仅仅写入内存了
undolog一般叫回滚日志。
事务回滚rollback功能就是通过 undolog实现的,通过undolog保证了为事务的原子性,undolog主要功能如下:
当开启一段事务还未提交时,事务中的操作可能会出现错误异常,这时候就可以通过undo log将事务中的操作进行回滚(rollback),意思是回到事务开启前那个状态。
例如:开启事务后,对表中某条记录进行修改(将该记录字段值由value1 ——> value2 ——> value3 ),如果从整个修改过程中出现异常,事务就会回滚,字段的值就回到最初的起点(值为value1 )。
事务如何通过undo log进行回滚操作呢?
这个很好理解,我们只需要在undo log日志中记录事务中的反向操作即可,发生回滚时直接通过undolog中记录的反向操作进行恢复,例如:
undo log保存的是一个版本链,也就是使用 DB_ROLL_PTR
这个字段来连接的。多个事务的 undo-log 日志副本 (数据快照),组成了一个 副本链
undo log版本链,数据页中的每行数据都会分配两个字段:trx_id
和 roll_pointer
。
为了 提升 Undo Log 读写性能, Undo Log 也在内存中进行了缓存,所处的位置在 Buffer Pool 中,如下图所示:
Buffer Pool 中的 Undo Log 页 是 InnoDB 存储引擎中用于缓存 Undo Log 记录的内存区域。
Buffer Pool 是一个重要的内存结构,用于提高数据库的性能,通过在内存中存储频繁访问的数据页和日志记录,减少对磁盘的 I/O 操作。
以下是有关 Buffer Pool 中 Undo Log 页的详细信息:
buffer pool 中有数据页也有 undo 页,基于wal思想,不仅对buffer pool 中数据页修改操作会记录到redo log buffer,对 undo 页修改操作也会记录到 redo log buffer,这样就通过redo log保证了事务持久性。
当事务Commit之后,undo 页本身就没有利用价值了,此时通过后台线程中的Master Thread或Purge Thread进行 undo 页 的回收工作。
那么,如果多个事务并行的读写操作,每一个事务应该使用那个版本呢?
MVCC 实现了自己 Copy-On-Write思想提升并发能力的时候, 也需要数据的副本,这里既然undo-log 有了那么多副本,MVCC 就借鸡生蛋, 复用这些数据副本。
所以,undo log 中的副本,可以用于实现多版本并发控制(MVCC),提升事务的并发性能,同时每一个事务操作自己的副本,实现事务的隔离性。
MVCC机制主要通过三个组件实现:
nnoDB中的缓存区叫innodb_buffer_pool,当读取数据时,就会先从缓存中查看是否数据的页(page)存在,不存在的话去磁盘上检索,查到后缓存到innodb_buffer_pool中。
同理,插入、修改、删除也是先操作缓存里数据,之后再以一定频率更新到磁盘上,这个刷盘机制叫做Checkpoint。
InnoDB中的数据主要有数据页、索引页、插入缓存、自适应哈希索引、锁信息和数据字典信息。
提示
RedoLog不在缓存区中,有个专门的 redo log_buffer 重做日志缓冲池。
Buffer Pool是很经典的缓存池,其中又以Page为单位
下图是mysql官网原图,其展示了Buffer Pool在innodb引擎架构的组成
Buffer Pool(缓冲池)是MySQL数据库中InnoDB存储引擎的一个重要组成部分,它位于内存中,用于缓存数据库中经常被访问的数据页和索引页,以减少对磁盘的I/O操作,从而提高数据库的读写性能
Buffer Pool的工作原理基于“时间局部性”和“空间局部性”原则,即最近访问过的数据在未来很可能再次被访问,且一个数据项被访问时,与其相邻的数据项也很可能被访问。
Buffer Pool通过缓存热点数据和索引,减少了磁盘I/O操作,大大提高了数据库的性能。
在Buffer Pool中,数据的加载和淘汰机制是维护和优化其性能的关键。
当数据库服务器接收到一个查询请求时,它首先会在Buffer Pool中查找对应的数据页。如果数据页已经在内存中,服务器可以直接返回数据,而不需要等待磁盘IO操作。如果数据页不在内存中,服务器会从磁盘读取数据,并将其存储在Buffer Pool中,以便后续查询可以直接从内存中获取数据。
所以,Buffer Pool的大小对数据库性能有很大的影响。
因此需要找到一个平衡点根据服务器的整体性能和数据库的特性来合理配置Buffer Pool的大小。
Buffer Pool中的核心组件包括索引页、数据页、Undo页等。索引页存储了InnoDB表的索引结构,数据页存储了InnoDB表的实际数据行,Undo页存储了旧版本的数据,用于支持事务的ACID属性中的原子性、隔离性(Isolation)。
在配置Buffer Pool时,核心的参数如下:
监控Buffer Pool的使用情况对于性能调优至关重要。
可以使用SHOW STATUS命令查看Buffer Pool的使用情况,如Innodb_buffer_pool_read_requests表示从Buffer Pool中读取的次数,Innodb_buffer_pool_reads表示直接从磁盘读取的次数等。
通过这些监控信息,可以了解Buffer Pool的使用情况,从而进行针对性的优化。
总结来说,Buffer Pool是MySQL中一个重要的组件,通过合理地加载和淘汰数据页,Buffer Pool能够有效地减少磁盘IO操作,提高查询效率。同时,监控和维护Buffer Pool的运行情况也是优化数据库性能的关键步骤。
redo log 的意思是 重做日志,redo log 是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。
redo log 保证了事务的持久性,当我们对缓冲池中的数据页进行了修改(修改后变成脏页),但是脏页数据是存在于Buffer Pool缓冲池,缓冲池占用的就是操作系统内存空间,所以数据页本质也是存在内存中的,内存有个特点就是断电即失。
所以当脏页数据还没有刷入磁盘,此时数据库服务发生宕机,那么脏页数据就会因为宕机而丢失,如何恢复这些没刷盘得脏页数据呢?
这时候redo log就派上了用场.以一个更新事务为例, redo log 流转过程,如下图所示:
redo log通过WAL(Write-Ahead Logging)来进行故障恢复(crash-safe),所谓WAL简单来说就是先写日志,后写磁盘。
当缓存页被修改后(变成脏页),我们就将本次操作写入到redo log buffer中,当事务Commit时,就先将redo log buffer中记录通过后台线程刷到磁盘中(事务提交是redo log默认刷盘时机),此时脏页还没有刷入磁盘,但是,只要redo log成功刷盘,就可以认为本次的修改操作完成了,因为就算发生了故障导致脏页数据丢失,也可以通过磁盘redo log恢复,所以,redo log 和 undo log 配合起来的作用就是:
redo log记录先写入到redo log buffer中,什么时候刷盘?也就是说, 最后还是从 redo log buffer 同步到硬盘中,那么redo log buffer何时进行刷盘操作呢?
主要是以下几种情况,默认情况下,redo log在事务提交时,就会进行一次redo log刷盘:
Master Thread每秒刷盘一次
redo log buffer 剩余空间 < 1/2, 刷盘一次
通过innodb_flush_log_at_trx_commit参数控制
0:有事务提交的情况下,每秒刷盘一次 1:每次提交事务,刷盘一次(默认值,性能差) 2:每次提交事务,把日志记录放到OS内核缓冲区,刷盘时机交给OS(性能好,低可靠)
再啰嗦一下 这个核心的innodb_flush_log_at_trx_commit 参数,这个超参数有3个值,支持三种策略:
也就是说, 一个没有提交事务的redo log 记录, 也可能会刷盘.因为在事务执行过程 redo log 记录是会写入redo log buffer 中, 这些redo log 记录会被后台线程刷盘
因为在事务执行过程 redo log 记录是会写入redo log buffer 中, 这些redo log 记录会被后台线程刷盘
innodb_flush_log_at_trx_commit=1(默认值):
注意,这个不仅仅写入page cache,而是写入磁盘的 redo.file
所以,为1时, 只要事务提交成功, redo log 记录就一定在硬盘里, 不会有任何数据丢失。数据绝对不会丢失, 但是效率最差的
如果事务执行期间MySQL 挂了或宕机, 这部分日志刷盘了, 但是事务并没有提交, 可以使用redo log 进行重做,所以日志丢了也不会有损失. 可以保证ACID的D(持久性),
设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache,刷盘时机交给OS(性能好,低可靠)
innodb_flush_log_at_trx_commit=2:
性能:性能较高,因为事务提交时只需要将日志写入内存中的文件系统缓存,而不需要立即进行磁盘 I/O。
如果仅仅只是MySQL挂了不会有任何数据丢失, 但是操作系统宕机可能会有5秒数据的丢失, 这种情况下无法满足ACID中的D.但数值2 肯定是效率更高的
Page Cache 刷盘的默认周期
在操作系统中,Page Cache 是用于缓存文件系统数据的内存区域,用来减少频繁的磁盘 I/O 操作。当数据写入文件时,实际上首先被写入到 Page Cache,而不立即写入到磁盘。
刷盘(即将 Page Cache 中的数据写入磁盘)的时机受多种因素影响,操作系统会定期将 Page Cache 中的数据刷新到磁盘。
Page Cache 刷盘周期由操作系统的默认行为和配置参数控制,一般来说:
设置为 0 的时候,表示每次事务提交时不进行刷盘操作。有事务提交的情况下,每秒刷盘一次
innodb_flush_log_at_trx_commit=0:
innodb_flush_log_at_trx_commit
设置为不同值时,分别是什么时候将 redo log 写入磁盘?
参数0:
事务提交时不刷盘。靠后台线程每秒刷盘一次。
后台线程把缓存在 redo log buffer 中的 redo log ,通过调用 write() 写到操作系统的 Page Cache,然后调用 fsync() 持久化到磁盘。
所以参数为 0 的策略,MySQL 进程的崩溃会导致上一秒钟所有事务数据的丢失
参数1:
只要事务提交成功,redo log记录就一定在硬盘里,不会有任何数据丢失。
如果事务执行期间MySQL挂了或宕机,日志丢了,但是事务也不可能成功提交,所以日志丢了也不会有损失!!
参数2:
事务提交成功,redo log buffer中的内容都会写入文件系统缓存(page cache),此时没有刷盘。
操作系统负责把 Page Cache 里的 redo log 持久化到磁盘。如果仅仅只是MySQL挂了,但是服务器没挂,不会有任何数据丢失。只有服务器崩溃的情况下,上5秒钟所有事务数据才可能丢失。
对比如下:
参数 1 最安全,在一些对数据安全性要求比较高的场景中,显然 innodb_flush_log_at_trx_commit 参数需要设置为 1。
在一些可以容忍数据库崩溃时丢失 1s 数据的场景中,我们可以将该值设置为 0,这样可以明显地减少日志同步到磁盘的 I/O 操作。
安全性和性能折中的方案就是参数 2,虽然参数 2 没有参数 0 的性能高,但是数据安全性方面比参数 0 强,因为参数 2 只要操作系统不宕机,即使数据库崩溃了,也不会丢失数据,同时性能方便比参数 1 高。
InnoDB的 Checkpoint 机制是数据库的一种自动保存技术,用于定期将内存中的脏数据(修改过的数据)同步到磁盘,确保数据一致性和持久性。
它的核心作用包括:
Checkpoint 触发时机包括:日志文件写满、系统空闲时或后台线程定期执行。它类似于游戏中的“自动存档”,既保证数据安全,又避免实时刷盘的开销。
Crash-safe 是指数据库系统在发生崩溃(例如服务器宕机、硬件故障、操作系统崩溃等)后,能够通过适当的恢复机制,确保数据的一致性和持久性,并保证已提交的事务不会丢失,未提交的事务会被回滚。这种机制的核心是事务的原子性、一致性、隔离性和持久性(ACID)特性,尤其是持久性。
Innodb 引擎有 crash-safe 能力,即事务提交过程中任何阶段,MySQL 宕机重启后都能保证事务的完整性,已提交的数据不会丢失。
这种能力是通过redo log保证的,MySQL 宕机重启,系统将自动检查 redo log,将修改还未写入磁盘的数据从 redo log 恢复到 MySQL 中。
binlog是binary log的缩写,即二进制日志。
binlog 是作为mysql操作记录归档的日志,这个日志记录了所有对数据库的数据、表结构、索引等等变更的操作。也就是说只要是对数据库有变更的操作都会记录到binlog里面来, 可以把数据库的数据当成我们银行账户里的余额,而binlog就相当于我们银行卡的流水。账户余额只是一个结果,至于这个结果怎么来的,那就必须得看流水了。而同样在mysql里我们就是通过binlog来归档、验证、恢复、同步数据。
bin log该日志主要有两个功能:
每次事务进行提交时,都会将增、删、改操作以追加的方式记录到bin log文件中。为什么该日志具备备份恢复功能,就是根据bin log日志中记录的二进制操作记录恢复即可;主从复制常用于MySQL主从集群搭建,MySQL从节点通过监听主节点bin log日志进行同步即可。
事务提交,以两阶段提交 为例:
binlog应该说是Mysql里最核心的日志, 它记录了除了查询语句(select、show)之外的所有的 DDL 和 DML 语句,也就意味着我们基本上所有对数据库的操作变更都会记录到binlog里面。
实际上,binlog以事件形式记录,不仅记录了操作的语句,同时还记录了语句所执行的消耗的时间。
关于bin log的二进制日志格式,有以下三种类型:
msql 5.6默认 statement 类型的bin log,语句模式原封不动的记录当前DML,可读性高,用户能看懂。msql 5.7默认 ROW类型的bin log,记录数据行的变化(用户看不懂,需要工具分析)。
statement 与ROW 模式的对比
事务执行过程中,先把日志写到 binlog cache(Server 层的 cache),事务提交的时候,再把 binlog cache 写到 binlog 文件中。
因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
提示
参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。
如果超过了这个参数规定的大小,就要暂存到磁盘(Swap)。
什么时候 binlog cache 会写到 binlog 文件?
在事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 文件中,并清空 binlog cache。
虽然每个线程有自己 binlog cache,但是最终都写到同一个 binlog 文件:
MySQL提供一个 sync_binlog 参数来控制数据库的 binlog 刷到磁盘上的频率:
在MySQL中系统默认的设置是 sync_binlog = 0,也就是不做任何强制性的磁盘刷新指令,这时候的性能是最好的,但是风险也是最大的。因为一旦主机【指的是操作系统】发生异常重启,还没持久化到磁盘的数据就会丢失。
而当 sync_binlog 设置为 1 的时候,是最安全但是性能损耗最大的设置。因为当设置为 1 的时候,即使主机发生异常重启,最多丢失一个事务的 binlog,而已经持久化到磁盘的数据就不会有影响,不过就是对写入性能影响太大。
如果能容许少量事务的 binlog 日志丢失的风险,为了提高写入的性能,一般会 sync_binlog 设置为 100~1000 中的某个数值。
在MySQL中,binlog(二进制日志)是在事务的提交阶段被刷入磁盘的。当一个事务执行并准备提交时,MySQL先生成binlog日志,这些日志记录了事务所做的所有修改。
注意
在事务的两阶段提交过程中,首先会将binlog日志写入到磁盘,然后才提交事务,确保了数据的一致性和持久性。
bin log属于MySQL体系架构的Server层,事务操作进行过程中,会把日志信息先记录到bin log cache中,等到事务提交后会将bin log cache中记录刷盘到bin log 文件中。
这里需要注意,无论一个事务中包含了多少个增删改操作,都要一次性写入,不可拆分。
总的来说,binlog在事务的提交阶段被刷入磁盘,这是MySQL保证数据一致性和支持数据恢复的重要机制。通过合理配置sync_binlog参数,可以在数据安全性和性能之间取得平衡。
总的来说,binlog在事务的提交阶段被刷入磁盘,这是MySQL保证数据一致性和支持数据恢复的重要机制。通过合理配置sync_binlog参数,可以在数据安全性和性能之间取得平衡。
说到这里大概知道两阶段提交其实就是为了防止这两个日志不一致,它将事务Commit操作分为两个阶段:
这个两阶段提交机制保证了 binlog 和 redo log 的协调一致。
总结起来说就是如果一个事务在prepare阶段中落盘成功,并在MySQL Server层中的binlog也写入成功,那这个事务必定commit成功。
通过两阶段提交协议,MySQL 在事务提交过程中可以确保在发生崩溃时:
数据恢复:当 MySQL 恢复时,系统可以根据 redo log 和 binlog 的状态,确保事务的一致性,避免部分提交或丢失。
日志文件是以.NNNNNN结尾的。索引文件以.index结尾。所有的文件有个相同的前缀。默认的binlog文件的前缀名是“HOSTNAME-bin”。
text... HOSTNAME-bin.0000101 HOSTNAME-bin.0000102 HOSTNAME-bin.0000103 ... HOSTNAME-bin.index ...
主从复制的一致性是 MySQL 等数据库系统在主从复制架构中的核心概念。
relay-log 的作用是实现主从复制的一致性,对于确保高可用性和数据同步至关重要。
在 MySQL 主从复制中,从库(slave)会从主库(master)读取其执行的二进制日志(Binlog),并将这些日志写入自己的中继日志(Relay Log)。
Relay Log 是从库用于保存从主库传输过来的数据变更操作的日志。
mysql主从复制流程的过程大概是这个样子:
relay-log中继日志是连接master和slave的核心,我们来深入了解一下它的结构和使用。
relay 日志文件的命名类似,只不过文件的前缀是“HOSTNAME-relay”。
textHOSTNAME-relay.0000101 HOSTNAME-relay.0000102 HOSTNAME-relay.0000103 ... HOSTNAME-relay.index
relay-log的结构和binlog非常相似,只不过他多了一个master.info和relay-log.info的文件。
知道binlog和relay-log的结构之后,我们重新梳理一下整个链路的流程,这里我们假定master.info和relay-log.info都是存在的情况:
但是在这里IO和SQL线程有会产生重复事件的问题,举一个场景:
既然会有这个问题还为什么要这样做呢?假设反过来,先更新master.info再记录中继日志,这样带来的问题就是丢失数据了。而mysql认为丢失比重复更严重,所以要先刷新日志,保大还是保小mysql帮你做了决定。
1. 崩溃一致性(Crash Consistency)
Binlog 记录事务提交的 SQL 操作,通常与 Redo Log 协同工作,实现崩溃一致性(Crash Consistency)
MySQL 的 InnoDB 存储引擎 和 MySQL Server 层的 binlog 通过两阶段提交来保证事务的一致性,确保在系统崩溃或主从复制时,事务的逻辑和物理数据保持一致。具体流程如下:
第一阶段(Prepare 阶段):
第二阶段(Commit 阶段):
这个两阶段提交机制保证了 binlog 和 redo log 的协调一致。
总结起来说就是如果一个事务在prepare阶段中落盘成功,并在MySQL Server层中的binlog也写入成功,那这个事务必定commit成功。
通过两阶段提交协议,MySQL 在事务提交过程中可以确保在发生崩溃时:
2. 主从复制的一致性
在主从复制的场景下,binlog 的一致性尤为重要。
MySQL 确保:
通过这种机制,MySQL 保证了主从复制环境中的一致性,不会出现主库和从库不同步的情况。
根据两阶段提交,崩溃恢复时的判断规则是这样的:
如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交
如果 redo log 里面的事务处于 prepare 状态,则判断对应的事务 binlog 是否存在并完整
a. 如果 binlog 存在并完整,则提交事务; b. 否则,回滚事务。
commit阶段写入 binog 之前发生了崩溃,事务回滚 - commit阶段写入 binlog 之后发生了崩溃,事务恢复
Mysql 逻辑架构
Mysql 整体架构上分为四层,分别是:连接层、服务层、存储引擎层、文件系统层。其中最核心的就是服务层和存储引擎层。
当一个SQL执行时,到服务层回显经过 解释器 对执行的SQL进行语法的解析验证,通过后会交给 优化器 对SQL进行优化,例如SQL语句的重写或者是索引的命中优化,最终会生成一个最优的执行计划,交给 执行器,执行器会按照优化器生成的执行计划,逐步执行查询,访问数据并返回结果。
Buffer Pool
Buffer Pool 是 InnoDB 中的缓存池,内部包含数据页、索引页、undo页、锁信息等等。
Buffer Pool(缓冲池)是MySQL数据库中InnoDB存储引擎的一个重要组成部分,它位于内存中,用于缓存数据库中经常被访问的数据页和索引页,以减少对磁盘的I/O操作,从而提高数据库的读写性能。
Buffer Pool通过缓存热点数据和索引,减少了磁盘I/O操作,大大提高了数据库的性能。所以,Buffer Pool的大小对数据库性能有很大的影响。
既然是缓存,就避免不了内存的淘汰,Buffer Pool 采用最近最少使用的 LRU 算法进行内存淘汰。
undo log
回滚日志,核心就是回滚事务与MVCC中的快照版本控制,保证了原子性与一致性。
在事务执行的过程中操作任何数据之前先将数据备份到undolog中。事务失败时可根据undo log进行回滚。用来保证事务的一致性。还可以用来实现多版本并发控制MVCC。
多个事务的 undo-log 日志副本 (数据快照),组成了一个 副本链,由回滚指针指向了上一个版本的事务数据,其事务数据的可见性由 ReadView 来保证。
redo log
重做日志,在事务执行的过程中不断记录事务操作的变化,记录的是数据页的物理修改。恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。用来保证事务的持久性。
当我们对缓冲池中的数据页进行了修改,会将修改后的数据页变成脏页,生成一条重做日志并写入 redo log buffer,当事务 commit 时,将 redo log buffer 中的内容刷新到 redo log file ,对 redo log file 采用追加写的方式,定期将内存中修改的数据将脏页数据刷入磁盘。如果此时数据库服务发生宕机,那么脏页数据就会因为宕机而丢失,但是 redo log 默认刷盘时机是在事务提交,只要redo log成功刷盘,就可以认为本次的修改操作完成了,这就是 redo log 能恢复这些没刷盘得脏页数据原因。
binlog
二进制日志,在事务提交后进行记录。用来备份, 通过主从复制来实现数据同步、读写分离和数据库的崩溃恢复,保证了数据的一致性。
事务的 Prepare、Commit 两阶段提交:
本文作者:柳始恭
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!