2023,兔飞猛进,从了解MySQL日志开始。
简述
MySQL提供了很多种日志文件,通过这些日志,我们可以知道数据库的运行情况、SQL语句的执行情况及各种错误信息等。
-
错误日志(error log):记录MySQL在启动、运行和关闭过程的错误日志信息。
-
慢查询日志(slow query log):当某个查询sql执行时间超过了系统参数long_query_time 设置的阈值,慢查询日志便会对这条sql语句进行记录。这个也是我们开发过程中常用的日志,通过慢查询日志,可以监控哪些语句的执行效率较慢,以便告警优化。
-
查询日志(general log):查询日志会记录所有对MySQL数据库请求的信息,因为避免记录过多的无用信息,所以默认情况下查询日志是关闭的。
-
二进制日志(bin log):binlog记录了数据库所有执行的DDL和除select外的DML语句,日志通过三种模式保存在文件中,分别为Row、Statement、Mixed。Row模式会记录每一行数据修改的细节,所以日志量会很大。Statement模式只记录执行的 SQL,不记录每一行数据的变化,之前在工作中调研使用过pt-table-checksum主从数据比对工具,该工具只支持Statement模式。Mixed模式,结合了其他两种模式的优点,一般语句修改使用 Statement 格式保存;对于一些 Statement 无法准确完成主从复制的操作,则采用 Row 格式保存。
-
redo log:这是InnoDB存储引擎的事务日志,它的存在使数据库具有crash-safe能力,即如果Mysql进程宕机重启后,系统自动检查redo log文件,将未写入到Mysql的数据从redo log恢复到Mysql中。
-
undo log:回滚日志同样也是InnoDB所提供,是实现事务原子性的关键。事务提交过程中记录redo log的同时也会生成undo log,当事务需要回滚时提供撤销所有已经成功执行的sql语句的能力。
Update语句执行过程中关于binlog和redo log的写入
关于一条update语句的执行过程
- 首先MySQL会通过InnoDB存储引擎查询到要更新的数据记录。存储引擎如果在内存中命中数据,则直接返回,否则会从磁盘中读入内存再返回。
- MySQL执行器修改数据,通过InnoDB存储引擎将结果更新到内存中,存储引擎同时将操作记录写入到redo log,并置为prepare状态。
- 随后MySQL执行器会生成binlog日志文件,写入磁盘。
- 最后存储引擎提交事务,并把redo log置为commit状态,此时一条update语句执行完成。
关于redo log的写入流程
在内存中有个重做日志缓冲池,redo log会不断写入到这个缓冲区,然后通过刷盘机制,刷入到磁盘。关于刷盘机制,在以下的情况下,会把redo log缓冲池的内容刷新到磁盘。
- 缓冲区空间不足,当redo log日志量占据缓冲区总容量的大约一半左右,会把这些日志刷新到磁盘上。
- 事务提交时,会把缓冲区日志刷到磁盘,这也是事务持久性的一个保证。
- MySQL中会有后台线程,大约每秒都会将缓冲区日志刷入磁盘。
- redo log是固定大小的一组文件,采用"循环写"的方式,其中有两个位置,分别为write pos和 checkpoint。写完一个事务,write pos会向前移动,redo log中的记录更新到磁盘,checkpoint会往前移动。当write pos与checkpoint重合,表示redo log日志已经写满,此时会将缓冲区日志都刷到磁盘,然后write pos会回到最开始的位置,重新开始写。
两阶段提交
在此过程中对于redo log和binlog的写入,采用了两阶段提交,那么为什么需要两阶段提交,如果不采用两阶段提交的话,会有什么问题?
如果不采用两阶段提交,两种方案,要么先写redo log,后写binlog;要么先写binlog,后写redo log。
第一种情况,如果在redo log写入之后,binlog写入之前,系统发生宕机。此时binlog没有记录,如果使用binlog进行数据库备份或崩溃恢复,就会缺少对应的执行语句,导致数据没有更新成功。
第二种情况,如果在binlog写入之后,redo log写入之前,系统发生宕机。此时binlog已经记录了执行语句,通过binlog进行数据库备份或恢复,数据库中记录会被成功更新。但是redo log没有记录这个事务,导致实际数据库中的数据没有被更新。
MySQL通过一个两阶段的提交让binlog和redo log保持一种逻辑上的一致。