Mysql 关键日志文件 #
redolog
和undolog
为 InnoDB 存储引擎独有,与事务操作息息相关,binlog
也与事务操作有一定的关系,同时对数据复制、恢复有很大帮助。
Redolog #
确保事务的持久性
防止在发生故障的时间点,尚有脏页未写入磁盘导致数据丢失问题。在重启 MySQL 服务的时候,会根据 redolog 进行重做,恢复崩溃时暂未写入磁盘的数据,从而达到事务的持久性这一特性。
redolog 是物理格式的日志,记录的是物理数据页的修改信息。
redolog 是顺序写入 redolog file 的物理文件中去的。
当更新一条数据时,InnoDB 会找到要更新的行数据,把做了什么修改写到 redolog 中,并把这行数据更新到内存中,整个过程就算完成了。
redolog file 是固定大小的(如下图),所以为了能够一直记录它只能采用循环写入方式,write pos
为当前记录的位置,checkpoint
为当前可以擦除的位置,代表更新的行已经完成数据库的磁盘更改,可以覆盖掉了。
事务开始之后就产生 redo log,redolog 的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,就开始逐步写入到 redolog文件中。
当对应事务的脏页写入到磁盘之后,redolog 的使命也就完成了(落盘后就不用担心数据丢失了),redolog 中对应事务占用的空间就可以重用(被覆盖)。
刷盘逻辑 #
所以为了保证事务的持久性必须将innodb_flush_log_at_trx_commit
的值设置为1,即每当事务提交时就必须保证事务都已经写入重做日志文件,设置为其他值都可能出现事务的丢失。
Binlog #
- 1)数据复制,比如在主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。
- 2)数据恢复,比如基于时间点的还原。
binlog 是逻辑格式的日志,binlog 中存储的内容称之为事件,每一个数据库更新操作(Insert、Update、Delete,不包括Select)等都对应一个事件(event)。 简单地说 binlog 中存储的是更新数据库的 SQL 语句,但又不完全是 SQL 语句这么简单, binlog 中同时包括了用户执行的SQL语句(增删改)的反向 SQL 语句。
比如:delete 操作在 binlog 会有 delete本身和其反向的 insert 这两条记录;update 则同时存储着update执行前后的版本的信息;insert则对应着delete和insert本身的信息。
事务提交的时候,会一次性将事务中的 SQL 语句(一个事务可能对应多个 SQL 语句)按照一定的格式记录到 binlog 中。
这里与 redolog 很明显的差异就是 redolog 并不一定是在事务提交的时候刷新到磁盘,redolog是在
事务开始
之后就逐步写入磁盘。因此对于事务的提交,即便是较大的事务,redolog 都是很快的。
但是对 binlog 来说,较大事务的提交可能会变得比较慢一些,这是因为 binlog 是在事务提交
的时候一次性写入的。
刷盘逻辑 #
事务提交时直接刷盘。
binlog 与 redolog 的区别 #
Binlog(二进制日志) #
-
用途:
- 主要用于复制(replication)和数据恢复(point-in-time recovery)。
- 记录所有对数据库进行更改的 SQL 语句(例如
INSERT
、UPDATE
、DELETE
)。
-
存储位置:
- 存储在磁盘上,通常位于 MySQL 数据目录中。
-
记录内容:
- 记录的是逻辑日志,即 SQL 语句或事件。
- 包含事务的开始和结束标记。
-
写入时机:
- 在事务提交时写入。
-
持久性:
- 可以配置为同步写入磁盘,也可以异步写入。
-
作用范围:
- 用于主从复制中的数据同步。
- 用于基于时间点的恢复(PITR)。
Redolog(重做日志) #
-
用途:
- 主要用于崩溃恢复(crash recovery)。
- 确保事务的持久性(即使在系统崩溃后,已提交的事务也不会丢失)。
-
存储位置:
- 存储在磁盘上,通常位于 MySQL 数据目录中的
ib_logfile
文件中。
- 存储在磁盘上,通常位于 MySQL 数据目录中的
-
记录内容:
- 记录的是物理日志,即数据页的更改。
- 包含每个事务的物理更改(例如,某个数据页的某个位置被修改)。
-
写入时机:
- 在事务进行过程中不断写入。
- 在事务提交时,确保所有相关的重做日志都已经写入磁盘。
-
持久性:
- 通常是同步写入磁盘,以确保数据的持久性。
-
作用范围:
- 用于崩溃恢复,确保数据库在崩溃后能够恢复到一致状态。
- 不用于复制或基于时间点的恢复。
总结 #
-
Binlog:
- binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
- 记录逻辑日志(SQL 语句)。
- 用于复制和基于时间点的恢复。
- 在事务提交时写入。
- binlog 大小无限制,是可以追加写入的,达到上限后会滚动更新。
-
Redolog:
- redolog 是 InnoDB 引擎特有的;
- 记录物理日志(数据页的更改)。
- 用于崩溃恢复,确保事务的持久性。
- redolog 空间是固定的,只能循环写。
- 在事务进行过程中不断写入,并在提交时确保写入磁盘。
undolog #
MySQL 中 undolog 是 MVCC 技术实现的重要组成部分,一致性读功能也需要用到 undolog。当然,更重要的一点就是用于回滚。如事务执行失败后,通过 undolog 进行回滚恢复到之前的状态。
undolog 是一个链表结构。,每次对数据进行 INSERT、UPDATE(DELETE) 时,都会将旧数据存储到 undolog 中。然后在数据行中修改字段DB_ROLL_PTR
指向对应的 undolog,以便在需要时查询到之前的数据。
可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
undo log也会产生 redo log,因为 undo log 也需要实现持久性保护。
对于 INSERT 操作的 undolog,在事务提交后就可以删除了。因为这是第一个版本所以并不需要存储旧数据。
而对于 UPDATE(DELETE) 操作则提交后也不能删除(可能其他事务的一致性读还在用这个 undolog),需要存储直到没有与之关联的快照事务时才能删除。
其中 DELETE 操作会先打上删除标记,然后由 purge 线程来删除。
MVCC #
Multi-Version Concurrency Control 多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。
通过存储多版本数据来减少并发控制中锁的使用,以提升性能,适合读多写少的场景。
MySQL 中的 MVCC 是 InnoDB 存储引擎实现的。通过 MVCC 技术,InnoDB 存储引擎可以同时执行对同一记录的读操作和写操作,而不需要等写操作释放锁,极大增加并发性。
实现 #
InnoDB 存储引擎在数据的每一行中都增加了 3 个隐藏字段用于存储必要数据。
- 1)
DB_TRX_ID
– 6 字节,记录最后插入
或更新
该行时的 事务 Id(删除
操作在内部被看做更新
,内部只是更新了该记录的删除标志位)。 - 2)
DB_ROLL_PTR
–7字节,回滚指针,指向对应的 undolog ,用于撤销操作 - 3)
DB_ROW_ID
–6字节,主键Id,如果建表时显式指定了主键则不会出现该行。
回滚 #
对应 InnoDB 存储引擎来说用户操作只有以下两种情况:
- 1)INSERT–插入一行数据,该行数据
DB_TRX_ID
的值就是插入这条数据的事务Id,DB_ROLL_PTR
此时还是空的。 - 2)UPDATE–修改(删除)该行数据,
DB_TRX_ID
则更新为当前事务Id,同时把修改之前的数据(只记录有更新的字段)写入到 undolog,DB_ROLL_PTR
则指向 undolog 对应位置。
回滚时通过字段DB_ROLL_PTR
找到 undolog 中记录的旧数据回滚即可。
一致性读 #
undolog 中记录了这么多个版本,查询时到底查哪个版本?
在 InnoDB 中,开启一个新事务的时候,会创建一个 read_view(读视图)
,由查询时所有活跃(即未提交)事务的Id数组(将其中最小的记作 min_id)和当前已创建的最大事务Id(记作max_id)组成。
需要注意的是:最大事务Id并不是活跃事务中Id最大的那个。
read_view是针对全表的,session级别的。例如在查询A表时会生成一个read_view,然后在同一个session中查询B表会使用前面生成的readview,不会针对B表生成新的。
假设当前事务Id为 trx_id ,版本链比较规则如下:
- 1)如果(trx_id<min_id)– 表示这个版本事务已提交,数据可见
- 2)如果(min_id<=trx_id<=max_id)
- 情况1:若 trx_id 不在数组中,表示这个版本是有已经提交的事务生成的(后续解释),数据可见。
- 情况2:如果trx_id不在id数组中,说明是由还未提交的事务生成的,数据不可见
- 3)如果(max_id<trx_id)– 表示这个版本是由将来(这里的将来是相对于当前事务来说的)启动的事务生成的,数据肯定不可见。
对于情况二解释如下:
MySQL 中的事务 Id 是自增的数字,所以数字大小可以判断事务的开始先后,但是不能判断提交先后。所以情况二中如果不在活跃事务列表说明这个事务虽然是后创建的但是已经先提交了。
所以查询时就根据这个规则,顺着版本链,把不可见的过滤掉,出现的第一个可见数据就是结果。
- 1)InnoDB 存储引擎中,每次修改记录都会在 undolog 中存储旧数据。
- 2)MySQL 真正数据表中只存最新版本记录,同时除了第一次插入的时候,其他情况 roll_ptr 都会有值。
- 3)历史版本数据都在 undolog 中,所以可以根据 roll_ptr 字段在 undolog 中找到多个版本数据。
- 4)当 undolog 不会被用到时会被移除。