首页 > 数据库 > 数据库开发技术 > MySQL的日志类型篇





MySQL中常见有以下7种日志,如下:
重做日志(redo log)
回滚日志(undo log)
二进制日志(binlog)
错误日志(errorlog)
慢查询日志(slow query log)
通用日志(general log)
中继日志(relay log)
不同的日志具有不同的功能,先总体了解下吧。




在MySQL中,如果每次更新操作都直接写进磁盘,写磁盘过程中,需要先找到那条记录,然后再更新。会导致整个IO成本,查找成本很高,因此,催生了WAL技术,WAL全称Write-ahead Logging,其核心思想是先写日志,再写磁盘。
当进行记录更新时,InnoDB先将记录写入redo log里,然后更新内存,此时更新操作就算完成了,然后InnoDB引擎会在系统空闲或者达到刷写条件的时候将redo log中的操作记录更新到磁盘中。
通过redo log,Innodb可以保证数据库即使发生异常重启,之前提交的记录都不会丢失,该能力称为crash-safe。
对于redo log,从以下几个点来进行总结:
redo log功能:
redo log是innodb引擎特有的日志,功能是为了保证事务的持久性。
redo log内容:
redo log是物理格式的日志,记录的是物理数据页面的修改的信息。
redo log生命周期:
redo log产生于事务开始,结束于对应事务的脏页写入到磁盘后,然后redo log占用的空间就可以被覆盖以实现重用。
redo log刷盘方式:
master线程周期性每秒一次刷新redo log buffer到redo log文件。
每个事务提交时刷新redo log buffer中数据到redo log文件。
当redo log buffer空间少于一般时,将会将redo log buffer中数据刷新到redo log文件。
redo log对应文件:
redo log对应ib_logfileN文件,相关参数有:
innodb_log_group_home_dir:日志文件路径。
innodb_log_files_in_group:重做日志文件组中文件个数,默认为2。
innodb_log_file_size:重做日志文件的大小。
innodb_mirrored_log_groups:日志镜像文件组的数量,默认为1。
redo log写入流程:
事务运行过程中,会不断地产生redo log,redo log不会直接写入磁盘中,而是先写入redo log buffer中,经过转换,在触发刷新条件时,顺序地写入到磁盘中。另外,redo log写入磁盘时,写入单位为512B,不需要double write机制。


binlog是MySQL中Server层面的日志,主要用于主从复制和定点恢复,binlog与redo写入方式不同,redo log是循环写的,而binlog是追加写的。binlog有三种模式,row,statement,mixed模式。目前不少工具的使用前提条件都是日志格式基于row格式的,因此生产上建议使用row格式。
row日志的三种格式:
statement格式:
优点:
1).技术成熟
2).写入日志文件数据较少,当更新或删除涉及多行时,可有效减少存储空间,可以快速根据二级制日志备份和恢复。
3).记录所有数据变更的原始语句,可用于数据库审计。
缺点:
1).执行结果不确定的DML语句,UDF和存储过程,以及不带order by的limit子句,可能会造成主备数据不一致。
2).一些内置函数,如uuid(),user(),sysdate()等时无法正确复制。
3).insert into ...select语句在基于statement的复制时需要的行级锁比基于row的复制多。
4).未使用索引的update语句需要进行表扫描,基于statement的复制时需要的行级锁比基于row的复制多。
5).对于复制语句,需要评估数据一致性风险。
row格式:
优点:
1).可以正确地复制所有数据的变更,是最安全的复制格式。
2).对于Insert into ...select,update或delete语句中where条件未使用索引等场景性能更好。
缺点:
1).生成更多的二进制日志数据,每行数据的变更都会写入二进制日志。
2).利用二进制日志进行备份和恢复是时间更长,另外,二进制日志持有的文件锁时间更长,可能会对数据块并发造成影响。
3).生成大字段的BLOB值会比基于statement的复制消耗更长的时间。
4).无法直接看到从库中执行的语句,需要使用mysqlbinlog工具的--base64-output=decode-rows和--verbose选项查看。
5).对于非事务性表,insert操作多行数据时需要更多的表级锁。
mixed格式:
对于mixed格式的日志,被视作不安全的语句在记录到二进制日志时会被自动转换为row格式,安全的语句会使用statement格式。

回滚日志保存了事务发生之前的数据的版本。
undo log功能:
1).保证数据的原子性。
2).用于回滚。
3).应用于MVCC版本下的读。
undo log内容:
逻辑格式的日志,执行undo时,将数据从逻辑上恢复至事务之前的状态。
undo log生命周期
事务开始之前,将当前事务的版本生成undo log,undo还会产生redo log保证undo log的可靠性。当事务提交之后,undo log不会马上被删除,而是放入带清理的链表,由purge线程判断是否由其他事务使用undo段中表的上一个事务之前的版本信息,来决定是否清理。
undo log物理文件:
MySQL5.6前,undo log表空间位于共享表空间的回滚段中,共享表空间的默认名称为ibdata,MySQL5.6之后,undo表空间可以配置成独立文件。完成初始化后undo log文件个数无法改变。
undo log配置:
innodb_undo_directory:独立表空间的存放目录。
innodb_undo_logs:undo log在独立表空间中的大小。
innodb_undo_tablespaces:undo log文件个数。
innodb_data_file_path:共享表空间文件目录。


其他四种日志相较于前三种重要性略低,因此在一起进行总结。
errorlog(错误日志):
errorlog记录着mysqld启动和停止,以及服务器在运行过程中发生的错误的相关错误,在服务器运行出现错误时,有助于相关人员进行问题分析,定位问题。
可通过以下参数配置:
log-error:错误日志路径。
slow query log(慢查询日志):
slow query log记录执行时间过长和没有使用索引的查询语句,不会记录执行报错的语句,只会记录执行成功的语句。慢日志可以通过mysqldumpslow,pt-query-digest等工具进行分析。为了便于分析,可对慢日志文件按天进行切割。
可通过以下参数配置:
slow_query_log:是否开启慢日志,on或1为开启。
slow_query_log_file:慢日志路径。
log_query_time:慢查询时间。
通用日志(general log)
general log记录了服务器接收到的每一个查询或者命令,无论这些查询或命令是否正确。默认是关闭的,因为开启general log会对服务器产生不小的系统开销。
中继日志(relay log):
relay log的功能是实现主从同步数据复制。在主从复制中,从库的io线程读取主库的binlog日志,将其转换为relay日志,然后sql线程读取relay log。进行操作回放。
两阶段提交从以下几个方面进行理解:
两阶段提交的功能:
简单来说是为了实现两份日志的逻辑一致。再次温习下redo log与binlog日志的差别:
1).redo log是InnoDB引擎独有的,binlog是MySQL的Server层实现的。
2).redo log是物理日志,记录在某个数据页上做了啥修改,binlog是逻辑日志,记录语句的原始逻辑。
3).redo log是循环写,binlog是追加写。
两阶段提交流程:
两阶段提交分为两个阶段,prepare阶段,commit阶段。
1).一条更新记录操作通过连接器,分析器,优化器等,通过调用操作引擎,将新数据更新到内存中,同时将该更新操作记录在redo log中,然后redo log处于prepare状态。
2).执行器生成该操作的binlog日志,并将binlog写入磁盘。
3).执行器调用引擎的提交事务接口,将刚刚写入的redo log状态更新为commit,完成更新操作。
Crash recovery的三种场景:
1).binlog有记录,redo log状态为commit:该场景下事务正常完成,无需恢复。
2).binlog有记录,redo log状态为prepare:该场景下binlog写完后提交事务之前发生了crash,恢复后会提交该事务。
3).binlog中没记录,redo log状态为prepare:该场景下在binlog写完之前发生了crash,恢复后会回滚事务。

重做日志写入磁盘,不一定是事务提交后才会写入重做日志文件的,而是随着事务的开始,逐步开始的,即使某个事务还没提交,InnoDB引擎仍然可以每秒将redo log buffer刷新到redo log中,这点从即使很大的事务提交时间也很短暂可以看出。

当undo文件使用共享表空间时,如果数据库中发生大事务操作时,需要生成大量的undo信息,这些信息保存在共享表空间中,可能会导致共享表空间变得很大,默认情况下,共享表空间不会也不能自动收缩。

从两种场景分析:
1).先写redo log再写binlog:此时如果MySQL在一条语句操作redo log后崩溃了,binlog中没有记录该语句操作,系统在crash recovery时重新执行时binlog中便会少了这一次修改,恢复的数据库中少了这条更新。
2).先写binlog再写redo log:此时如果MySQL在一条语句操作binlog后崩溃了,redo log中没有记录该语句操作,系统在crash recovery时重新执行时binlog便会多一次修改,恢复的数据库便会多一条更新。
因此,如果不使用两阶段提交,数据库状态就可能与用日志恢复出来的状态不一致。

今天系统学习总结了MySQL的各种日志,之前虽然零零散散接触过,但是没有详细的认知,通过总结,对MySQL的日志结构整体有了更深的认识。

END




原文作者:DBA修行之路;如有侵权,请联系管理员删除。