Mysql 日志

151 阅读9分钟

图片来源网络,侵删

日志

日志是Mysql中的重要组成部分,提供了Mysql的运行情况的信息、数据处理的信息以及运行的其他日志。当系统出现意外宕机的时候,可以使用日志进行恢复和重启,保证数据的一致性。

Mysql中的日志主要分为了以下几个部分:

  • 查询日志
  • slowlog(慢查询日志)
  • redolog(重做日志)
  • undolog(undo日志)
  • binlog(二进制日志)
  • relayLog(中继日志)
  • 错误日志

慢查询日志

慢查询日志主要是为查询是提供。当一个语句的执行时间,超过了设定的慢查询的时间,则会记录该查询语句,便于通过慢查询日志分析查询的sql,并且优化sql,提高性能。

查询日志

查询日志和慢查询日志的区别是:每一个查询的语句,都会生成一条查询日志,但是生成慢查询日志的条件是查询的时候需要超过设定的慢查询时间,只有当超过设定的时间的时候,Mysql会认为该查询是一个慢查询,然后会将查询的语句写入到慢查询日志。

redo日志

redo日志和undo日志,一起可以称为事务日志,主要是为了保证数据的一致性提供。在这里不得不先提一下两阶段提交。


  • 两阶段提交

指在执行一个事务的时候,为了保证数据的一致性,提供的一种数据落盘的策略。

  1. 读取原始数据,将数据加载进Buffer Pool
  2. 创建一个 undo 日志,记录修改数据的原始信息;
  3. 更新内存中的值
  4. 写入一个redo日志信息,将redo 的状态设置为prepare状态;
  5. 写入 binlog日志,并将binlog 日志落盘;
  6. 提交redo,将redo的状态设置为 commit。

第一阶段的提交是指创建redo日志,并设置redo的状态为 prepare;第二阶段的提交是指提交redo,将redo的状态设置为commit。


  • 问题1:为什么要两次?一次不行吗?

如果只有一次提交,如果说在第一次提交之后,还未写入到 binlog,如果机器宕机,再次重启的时候,会使用 redo 去恢复内存中的数据,但是不会修改 binlog,就会导致主从复制的数据不一致。

当使用两阶段提交,如果是宕机后发现redo log 和 binlog 的数据不一致。

如果先写 redolog,如果写完 redo log ,没有写binlog,会导致binlog 丢失数据,如果写了 binlog,没有写redolog,会导致 redolog 丢数据。

  • 问题2:宕机后怎么恢复数据?

如果发现redo log 不是 commit 状态,则说明在写入的时候,发生了异常,则会直接回滚操作。如果发现redolog 是commit状态,则说明数据均已提交,则会恢复数据。


从两阶段提交中能够看出,redolog 主要是为了在 服务器出现宕机的时候,用于恢复还未落盘的数据,保证数据的一致性。

InnoDB 的redolog 的大小是固定的,例如可以配置为 4个文件,每个文件1G大小。这几个文件可以想象成一个环形,当环用完的时候,会将未落盘的数据进行落盘,然后重复利用之前的空间。

8561045-94b94fcc685ce0f2.webp

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos 和 checkpoint 之间的是还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示redo log满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

undo日志

undo 日志是为了支持事务回滚操作。当一个事务对数据进行更改的时候,会先查询一下旧的数据,并将旧的数据写入到undo日志中,当事务回滚的时候,会将之前记录的数据写会到数据库,保证数据的一致性。

undo日志创建是在事务开启,并且开始更新数据的时候,会创建一个undo日志,用于保存前一个版本的数据。

undo日志的释放并不会在事务结束的时候删除,因为undo日志还需要继续为现有存在的事务服务(MVCC机制)。undo日志的删除会由一个后台线程不定时的去执行purge线程进行清理,清理也只会处理掉不会再使用的版本信息,还在继续使用的版本信息依旧会继续保存。


MVCC机制

MVCC机制,全称为多版本并发控制,用于在可重复读隔离级别下,解决事务的幻读情况。详情查看事务

数据库的隔离级别设置为可重复读,开启事务的时候,只会在第一次读取数据的时候,会生成一个ReadView视图,视图中保存了当前活跃的所有的事务ID和当前事务ID。在后续查找数据的时候,会根据生成的视图信息,去判断一个数据对当前事务的可见状态。

在修改数据的时候,每一条记录都会有一个隐形的列:roll_pointer指向修改的版本链信息(刚创建的数据没有,因为他是第一个版本)。每一次修改数据的时候,就会在该版本链中,使用头插法的方式,插入一个版本的信息,并带上事务Id。

当需要判断该版本是否可见的时候,就会使用roll_pointer指针指向的数据,判断事务ID和当前事务Id的关系,以确定该数据对当前事务是否可见。

ReadView信息

  • 活跃的事务Id
  • ReadView视图中保存了当前活跃的最小的事务Id
  • 系统下一次分配的事务Id 的大小
  • 当前事务的Id

判断数据是否可见的流程:

  1. 如果当前版本的事务ID等于当前事务的ID,说明是当前事务创建的,可见
  2. 如果当前版本的事务ID小于最小的事务ID,说明创建该视图的时候,事务已经提交,则可见
  3. 如果大于或者等于系统下一次分配的事务ID,则说明该版本是在该事务创建后提交的,不可见
  4. 如果在最大和最小之间,则判断当前事务Id是否在活跃事务Id中,在的话,说明还未提交,不可见,否则说明已经提交,可见。

?为什么读已提交会出现幻读?

因为读已提交是在每一次读的时候,去创建ReadView视图,每一次活跃的事务ID可能都会不一样,导致某些数据在其他事务提交或者回滚后再去读取,就会出现不一样的情况。

binlog日志

binlog日志主要是为了后续恢复数据,主从数据同步以及增量同步。binlog 中记录了每一个修改数据的操作,后续恢复数据的时候,可以通过回放binlog的方式,修复数据信息。

binLog 有两种格式:

  • Statement 模式:文件中存放的是sql语句

    缺点:准确性差

  • Row模式:记录每一个字段变化前后得到的值

    磁盘占用大,网络开销大

  • Mixed模式:statement 和 row 格式的混合

    主从同步可能会失败

插入语句的 binlog 的翻译

BEGIN
/*!*/;
# at 4515
#210426 20:52:45 server id 1  end_log_pos 4586 CRC32 0x5b63981b 	Table_map: `zcwdb`.`feed_counter_v1` mapped to number 108
# at 4586
#210426 20:52:45 server id 1  end_log_pos 4672 CRC32 0xbd119055 	Write_rows: table id 108 flags: STMT_END_F

BINLOG '
nbeGYBMBAAAARwAAAOoRAAAAAGwAAAAAAAEABXpjd2RiAA9mZWVkX2NvdW50ZXJfdjEACQMIAwMD
AxEFBQMACAgAABuYY1s=
nbeGYB4BAAAAVgAAAEASAAAAAGwAAAAAAAEAAgAJ//8A/iIAAABQuAUAAAAAAAEAAAAAAAAAAAAA
AAAAAABZBgoAAAAAAAAAAAA9CtejcLV6QFWQEb0=
'/*!*/;
### INSERT INTO `zcwdb`.`feed_counter_v1`
### SET
###   @1=34 /* INT meta=0 nullable=0 is_null=0 */
###   @2=374864 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @3=1 /* INT meta=0 nullable=0 is_null=0 */
###   @4=0 /* INT meta=0 nullable=0 is_null=0 */
###   @5=0 /* INT meta=0 nullable=0 is_null=0 */
###   @6=0 /* INT meta=0 nullable=0 is_null=0 */
###   @7=1493568000 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
###   @8=0 /* DOUBLE meta=8 nullable=0 is_null=0 */
###   @9=427.33999999999997499 /* DOUBLE meta=8 nullable=0 is_null=0 */
# at 4672
#210426 20:52:45 server id 1  end_log_pos 4703 CRC32 0xe65b5c53 	Xid = 28
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

更新的 binlog 转义

BEGIN
/*!*/;
# at 4841
#210426 21:04:26 server id 1  end_log_pos 4912 CRC32 0xcac5fbee 	Table_map: `zcwdb`.`feed_counter_v1` mapped to number 108
# at 4912
#210426 21:04:26 server id 1  end_log_pos 5050 CRC32 0xfc407f62 	Update_rows: table id 108 flags: STMT_END_F

BINLOG '
WrqGYBMBAAAARwAAADATAAAAAGwAAAAAAAEABXpjd2RiAA9mZWVkX2NvdW50ZXJfdjEACQMIAwMD
AxEFBQMACAgAAO77xco=
WrqGYB8BAAAAigAAALoTAAAAAGwAAAAAAAEAAgAJ/////wD+IgAAAFC4BQAAAAAAAQAAAAAAAAAA
AAAAAAAAAFkGCgAAAAAAAAAAAD0K16NwtXpAAP4iAAAAULgFAAAAAAABAAAAAQAAAAAAAAAAAAAA
WQYKAAAAAAAAAAAAPQrXo3C1ekBif0D8
'/*!*/;
### UPDATE `zcwdb`.`feed_counter_v1`
### WHERE
###   @1=34 /* INT meta=0 nullable=0 is_null=0 */
###   @2=374864 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @3=1 /* INT meta=0 nullable=0 is_null=0 */
###   @4=0 /* INT meta=0 nullable=0 is_null=0 */
###   @5=0 /* INT meta=0 nullable=0 is_null=0 */
###   @6=0 /* INT meta=0 nullable=0 is_null=0 */
###   @7=1493568000 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
###   @8=0 /* DOUBLE meta=8 nullable=0 is_null=0 */
###   @9=427.33999999999997499 /* DOUBLE meta=8 nullable=0 is_null=0 */
### SET
###   @1=34 /* INT meta=0 nullable=0 is_null=0 */
###   @2=374864 /* LONGINT meta=0 nullable=0 is_null=0 */
###   @3=1 /* INT meta=0 nullable=0 is_null=0 */
###   @4=1 /* INT meta=0 nullable=0 is_null=0 */
###   @5=0 /* INT meta=0 nullable=0 is_null=0 */
###   @6=0 /* INT meta=0 nullable=0 is_null=0 */
###   @7=1493568000 /* TIMESTAMP(0) meta=0 nullable=0 is_null=0 */
###   @8=0 /* DOUBLE meta=8 nullable=0 is_null=0 */
###   @9=427.33999999999997499 /* DOUBLE meta=8 nullable=0 is_null=0 */
# at 5050
#210426 21:04:26 server id 1  end_log_pos 5081 CRC32 0xee3bcea4 	Xid = 39
COMMIT/*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

中继日志

中继日志和binlog一起作用与MySQL的主从复制,binlog 是传输数据的记录,中继日志是Master和Slave连接的信息,IO线程将来自Master的时间存储到中继日志中。中继日志充当缓存,这样Master不必等待slave执行完成就可以发送下一个事件。

当使用statrt slave 的时候,就会创建中间日志,使用reset slave 的时候,就会删除中继日志。

错误日志

错误日志记录了Mysql运行或启动时,产生的错误的信息。