Mysql基础课三:日志

数据库的crash-safe

  1. 保证:如果客户端收到事务成功的消息,事务就一定持久化了;

  2. 保证:如果客户端收到事务失败(比如主键冲突、回滚等)的消息,事务就一定失败了;

  3. 保证:如果客户端收到,执行异常的消息,应该通过查询当前状态来继续后续的逻辑,此时数据库只需要保证内部一致(数据和日志之间,主库和备库之间);

  4. 通俗的来说,crash-safe 保证了,数据库可以实现恢复到完整日志范围内,任意一秒的状态;

Mysql 日志

  1. Mysql 是通过 完整的 redo log 和 bin log 来保证 crash-safe的,所以需要保证完整的日志,且磁盘保存;

  2. bin log 是Server层的日志,而redo log 是引擎层的日志,并且是 InnoDB 引擎特有的;

  3. bin log 是逻辑日志,记录的是SQL语句的原始逻辑,比如“ 给 ID=2 的这一行的 c 字段加 1 ”,而 redo log 是物理日志,记录的是 ”在某个数据页上做了什么修改”;

  4. binlog 是追加写入的,是指写到一定大小后,会切换到下一个,不会覆盖以前的日志;而 redo log 是循环写的,空间固定会用完,用完后需要将内存页刷新到磁盘(注意不是 redo log 持久化,已经持久化过了),然后擦除部分 redo log 来空出空间;

bin log 日志操作

  1. 事务执行过程中,先把日志写到 bin log cache,事务提交的时候,再把 bin log cache 写到 bin log 文件中,最后通过 fsync 写入磁盘中,注意,每个线程都有自己 bin log cache,但是共用同一份 bin log 文件;
    Mysql基础课三:日志_第1张图片

  2. write 操作,写入 bin log 文件,并不是持久化到磁盘,而 fsync 才是持久化操作;这两个操作的时机,是由参数 sync_binlog 控制的;

  3. sync_binlog =0 ,表示提交事务只 write,不 fsync;sync_binlog = 1,表示提交事务只 fsync;sync_binlog = N,表示提交事务 write,累积 N 个事务后执行 fsync;设置为N,如果发生异常重启,会丢失最近 N 个事务的 bin log 日志;

redo log 日志操作

  1. Mysql 执行修改操作,会首先检测内存中是否存在,如果不存在,需要从磁盘中读入到内存中,然后写内存,写 redo log,此时修改操作正常返回,最后在数据库空闲时,将内存数据写入到磁盘,这个过程被称为 WAL,随让 redo log 持久化也需要 IO 操作,但这种顺序 IO 比 随机 IO 速度快很多;

Mysql基础课三:日志_第2张图片

  1. redo log 的持久化策略,是通过设置 innodb_flush_log_at_trx_commit 参数,设置为 0 的时,表示事务提交时,只把 redo log 留在 redo log buffer 的内存空间中 ;设置为 1 的时,表示事务提交时,将 redo log 直接持久化到磁盘;设置为 2 的时候,表示事务提交时,只把 redo log 写到 page cache;

  2. InnoDB 有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。

  3. redo log 是通过二阶段提交来写入的,会和 bin log 一起,保证同时提交;

  4. 建议常见 TB 级别的磁盘,将 redo log 大小设置为 4 个文件,每个文件 1 GB,避免 redo log 频繁写满,导致刷脏页;

  5. redo log 是在 Mysql 崩溃后恢复时使用,正常刷脏页的过程,只需要将内存数据页,merge 到磁盘数据页即可;

双一策略

  1. redo log 的参数 innodb_flush_log_at_trx_commit 设置为 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘,建议设置成 1,保证 MySQL 异常重启之后数据不丢失;

  2. 同样 bin log 的参数 sync_binlog 设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘,建议设置成 1,保证 MySQL 异常重启之后 binlog 不丢失;

二阶段提交

  1. 分析更新语句 update T set c = c + 1 where id = 2 的执行流程,SQL语句到达执行器,调用存储引擎接口,获取 id = 2 这一行;

  2. 存储引擎,判断id是主键,调用索引树搜索,找到这一行,如果在内存中就直接返回,否则从磁盘中读入内存,再返回;

  3. 存储引擎,首先会将更新操作 c + 1 写入内存,同时记录到 redo log,此时该条 redo log 处于 prepare 状态,最后告知执行器,完成更新可以提交事务;

  4. 执行器会生成该操作的 bin log,然后调用存储引擎的提交事务接口,此时存储引擎就会将,该条 redo log 修改为 commit 状态,操作完成;

  5. 这里使用了两阶段提交,只有 redo log 为 commit 状态的,才会真正执行到磁盘中,这样保证了 bin log 和 redo log 的一致性;

  6. 考虑下,如果 redo log 处于 prepare 状态,bin log 完成写操作,此时 Mysql 异常重启后,对于 redo log 的处理会判断,如果 redo log 是 prepare,没有 commit,但 bin log 是完整的,就会提交该事务;
    Mysql基础课三:日志_第3张图片

刷脏页和造成的SQL执行停顿

  1. InnoDB 更新时,会写内存和写 redo log,此时内存数据页和磁盘数据页是不一致的,InnoDB 查询时,如果内存中有结果,就直接返回,如果内存中没有,就会去磁盘查询,并将该数据页,加载到内存中;其中内存和磁盘不一致的,将内存中的这部分数据页称为脏页;

  2. 注意脏页,并不是指内存中数据不对,而是磁盘数据落后,需要将内存数据页 flush 到磁盘;

  3. flush 操作触发有几种情况,一种是 redo log 写满了,另一种是 系统内存不足,需要 flush 脏页到磁盘来空出内存,注意不能因为 redo log 有记录就直接丢弃脏页,因为这种落磁盘,能保证下次从磁盘加载到内存中的数据一定是正确的;第三种是,Mysql 空闲和 Mysql 正常关闭时;

  4. 如果平时执行很快的SQL语句,偶尔执行慢了,可能是脏页 flush 到磁盘的过程消耗了时间;

  5. InnoDB flush 脏页,会用到 innodb_io_capacity 参数,建议设置成 磁盘的 IOPS;参数 innodb_max_dirty_pages_pct 是脏页比例上限,默认是75%,多关注脏页比例,不用使其经常接近75%;

  6. 参数 innodb_flush_neighbors 可以控制 flush 脏页的 “连坐” 机制,在SSD设置,建议设置为0;

数据恢复

  1. 假设,想将数据恢复到前一天某个时间点,首先 bin log 会定期备份,找到最近的一次全量备份,然后从该备份来恢复数据;

  2. 将该备份恢复到临时库,然后从该时间点开始,将 bin log 依次取出,取到想要恢复的时间点,然后依次恢复,最后将想要恢复的数据从临时库取出,恢复到线上库中;

你可能感兴趣的:(Mysql)