MySQL 日志简述
# 零、三种基本的日志
- undo log(回滚日志):是 Innodb 存储引擎层生成的日志,实现了事务中的原子性,主要用于事务回滚和 MVCC。
- redo log(重做日志):是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于掉电等故障恢复;
- binlog (归档日志):是 Server 层生成的日志,主要用于数据备份和主从复制;
# 一、undo log
- 开启事务后,InnoDB 层更新记录前,首先要记录相应的 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面。
# 1,记录的信息
- 在插入一条记录时,要把这条记录的主键值记下来,回滚时只需要把这个主键值对应的记录删掉;
- 在删除一条记录时,要把这条记录中的内容都记下来,回滚时再把由这些内容组成的记录插入到表中;
- 在更新一条记录时,要把被更新的列的旧值记下来,回滚时再把这些列更新为旧值;
# 2,版本链
- 一条记录的每一次更新操作产生的 undo log 格式都有一个 roll_pointer 指针和一个 trx_id 事务id:
- 通过 trx_id 可以知道该记录是被哪个事务修改的;
- 通过 roll_pointer 指针可以将这些 undo log 串成一个链表,这个链表就被称为版本链;
# 3,两大作用:
- 实现事务回滚,保障事务的原子性
- 事务处理过程中,如果出现了错误或者用户执 行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据将数据恢复到事务开始之前的状态;
- 实现 MVCC(多版本并发控制)关键因素之一
- MVCC 是通过 ReadView + undo log 实现的。undo log 为每条记录保存多份历史数据,MySQL 在执行快照读(普通 select 语句)的时候,会根据事务的 Read View 里的信息,顺着 undo log 的版本链找到满足其可见性的记录;
# 二、Buffer Pool
# 1,作用
- 当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取;
- 当修改数据时,如果数据存在于 Buffer Pool 中,那直接修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,为了减少磁盘I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘;
# 2,内存划分
- InnoDB 会把存储的数据划分为若干个「页」,以页作为磁盘和内存交互的基本单位,一个页的默认大小为 16KB。因此,Buffer Pool 同样需要按「页」来划分。
- InnoDB 会在 MySQL 启动的时候为 Buffer Pool 申请一片连续的内存空间,然后按照默认的16KB的大小划分出一个个的页, Buffer Pool 中的页就叫做缓存页。此时这些缓存页都是空闲的,之后随着程序的运行,才会有磁盘上的页被缓存到 Buffer Pool 中。
# 3,持久化
- 为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来,这个时候更新就算完成了;
- InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里;
- Buffer Pool 除了缓存**「索引页」和「数据页」,还包括了 Undo 页,插入缓存、自适应哈希索引、锁信息**等等。
- undo log 和数据页的刷盘策略是一样的,都需要通过 redo log 保证持久化。
- buffer pool 中有 undo 页,对 undo 页的修改也都会记录到 redo log。
- redo log 会每秒刷盘,提交事务时也会刷盘,数据页和 undo 页都是靠这个机制保证持久化的。
# 三、redo log
# 1,为什么需要
- Buffer Pool 是基于内存的,而内存总是不可靠,万一断电重启,还没来得及落盘的脏页数据就会丢失;
# 2,简单介绍
- redo log 是物理日志,记录了某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新,每当执行一个事务就会产生这样的一条或者多条物理日志;
# 3,WAL 技术
- WAL 技术指的是, MySQL 的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上;
- 在事务提交时,只要先将 redo log 持久化到磁盘即可,不需要等到将缓存在 Buffer Pool 里的脏页数据持久化到磁盘,重启后可以根据 redo log,将所有数据恢复到最新的状态;
- redo log 和 WAL 技术,InnoDB 就可以保证即使数据库发生异常重启,之前已提交的记录都不会丢失,这个能力称为 crash-safe(崩溃恢复),redo log 保证了事务四大特性中的持久性;
- 写入 redo log 的方式使用了追加操作, 磁盘操作是顺序写,写入数据需要先找到写入位置,然后才写到磁盘,所以磁盘操作是随机写;
- MySQL 的写操作从磁盘的「随机写」变成了「顺序写」,提升语句的执行性能;
# 4,与undo log的区别
- redo log 和 undo log 这两种日志是属于 InnoDB 存储引擎的日志,它们的区别在于:
- redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值;
- undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值;
- 事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务;
- 事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务;
# 5,刷盘
# 5.1 非直接刷盘
- 执行一个事务的过程中,产生的 redo log 不是直接写入磁盘的,直接写入会产生大量的 I/O 操作;
- 每当产生一条 redo log 时,会先写入到 redo log buffer,后续在持久化到磁盘;
- redo log buffer 默认大小 16 MB,可以通过 innodb_log_Buffer_size 参数动态的调整大小;
# 5.2 刷盘时机
- MySQL 正常关闭时;
- 当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;
- InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘;
- 每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由 innodb_flush_log_at_trx_commit 参数控制)
# 5.3 innodb_flush_log_at_trx_commit
- 为 0 时,表示每次事务提交,还是将 redo log 留在 redo log buffer 中,在事务提交时不会主动触发写入磁盘的操作;
- 为 1 时,表示每次事务提交,都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘,可以保证 MySQL 异常重启之后数据不会丢失,默认值;
- 为 2 时,表示每次事务提交,都只是缓存在 redo log buffer 里的 redo log 写到 redo log 文件,注意写入到「 redo log 文件」并不意味着写入到了磁盘,因为操作系统的文件系统中有个 Page Cache,Page Cache 是专门用来缓存文件数据的,所以写入「 redo log文件」意味着写入到了操作系统的文件缓存;
# 5.4 后台线程
- 每隔1s执行一次;
- 针对参数 0 :调用 write() 把缓存在 redo log buffer 中的 redo log 写到操作系统的 Page Cache,然后调用 fsync() 持久化到磁盘,MySQL 进程的崩溃会导致上一秒钟所有事务数据的丢失;
- 针对参数 2 :调用 fsync,将缓存在操作系统中 Page Cache 里的 redo log 持久化到磁盘,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失;
# 5.5 场景比较
- 数据安全性:参数 1 > 参数 2 > 参数 0
- 写入性能:参数 0 > 参数 2> 参数 1
# 5.6 redo log group
- 默认情况下,InnoDB 存储引擎有 1 个重做日志文件组(redo log Group),由 2 个 redo log 文件组成,分别叫 :ib_logfile0 和 ib_logfile1;
- 重做日志文件组是以循环写的方式工作的,从头开始写,写到末尾就又回到开头,相当于一个环形;
- redo log 是为了防止 Buffer Pool 中的脏页丢失而设计的,随着系统运行,脏页刷新到了磁盘中,那么 redo log 对应的记录也就没用了,这时候擦除这些旧记录,以腾出空间记录新的更新操作;
- 循环写
- write pos 和 checkpoint 的移动都是顺时针方向;
- write pos ~ checkpoint 之间的部分(图中的红色部分),用来记录新的更新操作;
- check point ~ write pos 之间的部分(图中蓝色部分):待落盘的脏数据页记录;
- 如果 write pos 追上了 checkpoint,就意味着 redo log 文件满了,这时 MySQL 会被阻塞;
- 因此所以针对并发量大的系统,适当设置 redo log 的文件大小非常重要;
- 此时会停下来将 Buffer Pool 中的脏页刷新到磁盘中,然后标记 redo log 哪些记录可以被擦除并进行擦除,等擦除完旧记录腾出了空间,checkpoint 就会往后顺时针移动,然后 MySQL 恢复正常运行,继续执行新的更新操作;
- 所以,一次 checkpoint 的过程就是脏页刷新到磁盘中变成干净页,然后标记 redo log 哪些记录可以被覆盖的过程;
# 四、binlog
# 1,介绍
- MySQL 在完成一条更新操作后,Server 层还会生成一条 binlog,等之后事务提交的时候,会将该事物执行过程中产生的所有 binlog 统一写 入 binlog 文件;
- binlog 文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作,比如 SELECT 和 SHOW 操作;
# 2,与 redo log 区别
# 2.1 适用对象不同:
- binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用;
- redo log 是 Innodb 存储引擎实现的日志;
# 2.2 文件格式不同:
- binlog 有 3 种格式类型,分别是 STATEMENT(默认格式)、ROW、 MIXED,区别如下:
- STATEMENT:每一条修改数据的 SQL 都会被记录到 binlog 中(相当于记录了逻辑操作,也称为逻辑日志),主从复制中 slave 端再根据 SQL 语句重现。但 STATEMENT 有动态函数的问题,比如 uuid 或者 now 这些随时在变的函数会导致复制的数据不一致;
- ROW:记录行数据最终被修改成什么样,不会出现动态函数的问题。但每行数据的变化结果都会被记录,比如update 语句,更新多少行数据就会产生多少条记录,使 binlog 文件过大,而在 STATEMENT 格式下只会记录一个 update 语句而已;
- MIXED:包含了 STATEMENT 和 ROW 模式,它会根据不同的情况自动使用 ROW 模式和 STATEMENT 模式;
- redo log 是物理日志,记录的是在某个数据页做了什么修改,比如对 XXX 表空间中的 YYY 数据页 ZZZ 偏移量的地方做了AAA 更新;
# 2.3 写入方式不同:
- binlog 是追加写,写满一个文件,就创建一个新的文件继续写,不会覆盖以前的日志,保存的是全量的日志;
- redo log 是循环写,日志空间大小是固定,全部写满就从头开始,保存未被刷入磁盘的脏页日志;
# 2.4 用途不同:
- binlog 用于备份恢复、主从复制;
- redo log 用于掉电等故障恢复;
- redo log 文件是循环写,是会边写边擦除日志的,只记录未被刷入磁盘的数据的物理日志,已经刷入磁盘的数据都会从 redo log 文件里擦除;
- binlog 文件保存的是全量的日志,也就是保存了所有数据变更的情况,所以如果不小心整个数据库的数据被删除了,得用 binlog 文件恢复数据;
# 3,主从复制
- 记录 MySQL 上的所有变化并以二进制形式保存在磁盘上,复制的过程就是将 binlog 中的数据从主库传输到从库上;
- 这个过程一般是异步的,也就是主库上执行事务操作的线程不会等待复制 binlog 的线程同步完成;
编辑 (opens new window)
上次更新: 2024/08/12, 19:17:34