MySQL 中的日志

117 阅读6分钟

mysql 中的各种日志

bin log

概念

bin log 是 binary log 的缩写,即二进制日志,又名归档日志。bin log 记载数据库中发生的变化,例如新建了一个数据库,新建一张表,更新一条数据等。

  • mysql 服务启动时,设置 --log-bin 属性启用 bin log
  • 改动 log-bin 属性需要重启 mysql 服务器
  • bin log 并不是写在某一个文件中,而是写在一组同名的文件中
  • mysqlbinlog 命令可用于查看 bin log

作用

  • 用于复制
    • 主从结构中,leader 服务器接收变更请求,follower 服务器通过同步 leader 的 bin log,将发生的变化记录并执行,保证和 leader 数据库的数据一致
  • 用于恢复
    • 当一份数据丢失时,可以找到最近的一份备份数据并恢复到备份时的模样,且读取并执行备份时间之后的 bin log,直到数据丢失前一刻起

三种格式

  • 基于 statement 语句的 binlog
    • --binlog-format=STATEMENT
    • 记录完整的 sql 语句
    • 有可能导致主从服务器数据不一致
  • 基于 row 的 binlog
    • --binlog-format=ROW
    • 记录一条语句所改动记录的全部信息(例如某一条记录更新前后的所有内容)
    • 内容较为详细,所以能够保证主从数据一致
  • 混合模式
    • --binlog-format=MIXED
    • 通常情况下记录基于语句的日志,特殊情况下记录基于行的日志

redo log

概念

redo log 属于 InnoDB 存储引擎独有的日志格式;记录在数据变更过程中所产生的更改细节,如第0号表空间的100号页面的偏移量为 1000 处的值更新为 2,这样 mysql 就可以根据这些细节,将数据从某个异常状态恢复到当前正确的状态,所以,该种日志被成为 redo 日志

  • redo log 用于保证 crash-safe 能力
  • innodb_flush_log_at_trx_commit 参数
    • 取 0,启动定时任务,每秒刷写日志到磁盘
    • 取 1,表示每次事务结束,强制将日志刷写到磁盘(默认兼推荐值)
    • 取 2,每次事务提交后,将 log buffer 同步给 os buffer,同时启用定时任务,每秒将 os buffer 刷写到磁盘

作用 & 优点

  • 持久化某个事务对于数据库所做的修改
  • 完整的 redo log 可以用于恢复一个事务
  • 相对于 sql 语句和变动前后的数据而言,bin log 占用空间更小
  • redo log 可以顺序写入文件,保证持久化的速度

与 bin log 对比

  • redo log 属于 InnoDB 存储引擎;bin log 属于 MySQL server层通用日志,所有引擎都可以使用
  • redo log 属于物理日志,记录的是数据页上某个位置数据做了什么修改;bin log 属于逻辑日志,记录被执行 sql 原始的逻辑
  • 两种日志都是记录在系统文件中,文件格式略有不同;redo log 是循环写入,固定空间大小存在用完的可能;bin log 可以追加写入,只要磁盘允许,不存在空间限制
  • redo log 的主要作用在于保证事务的完整性;bin log 的主要作用在于数据归档

redo log 的刷盘时机

  • log buffer 空间不足
  • 提交事务
  • 后台线程不停地刷写
  • 正常关闭服务器
  • checkpoint

两阶段提交

  • 两阶段提交并不是 mysql 中独有的,是一种规范
  • X/Open 组织提出了 XA 规范,是针对分布式事务的规范

该规范中定制了两个角色

  • 全局事务的管理者,称为事务协调器 Transaction Coordinator 和资源管理者 Resourse Manager
  • 事务管理器 Transaction Manager,管理全局事务中某一个小事务

该规范指出,要提交一个全局事务,必须分为2步

  • Prepare 阶段: 事务协调器准备提交一个全局事务时,提前通知各个事务管理器,事务即将提交,请检查是否还有问题;事务管理器做好准备,例如将 redo 日志刷盘,之后向事务协调器回复成功或者失败;

  • Commit 阶段:如果 prepare 阶段,所有事务管理器都回复成功,那事务协调器就会再次通知各个事务管理器可以提交事务了;如果有任意一个事务管理器回复失败,那事务协调器就会再次通知各个事务管理器,大家一起回滚事务


InnoDB 中的事务提交,为了保证 redo log 与 bin log 一致,采用了两阶段提交
  • Prepare 阶段: InnoDB 将事务过程中产生的 redo log 写入磁盘,并置为 Prepared 状态,除了 redo log,其实还有处理 undo log;该阶段 server 层,即 bin log 方面啥也没做
  • Commit 阶段: server 层将事务过程中产生的 bin log 刷盘;InnoDB 更新 undo log / redo log 的状态为 committed

undo log

概念

undo log,又名撤销日志或回滚日志,为了回滚而记录的日志

buffer pool

  • Buffer pool 本质上是 InnoDB 向 OS 申请的一段连续内存空间,通过参数 innodb_buffer_pool_size 控制大小
  • InnoDB 使用大量的链表来管理 Buffer Pool
  • free 链表中的每一个节点都代表一个空闲的缓存页,在将磁盘中的数据加载到内存时,会在 free 链表中寻找空闲的缓存页安置这些数据
  • 为了快速定位某个页是否被加载到 Buffer Pool, 使用表空间号 + 页号作为 key, 缓存页作为 value,建立哈希表
  • 在 Buffer Pool 中被修改的页被称为脏页,修改的数据并不会立即被刷盘,而是加入到 flush 链表,等待某个时机
  • LRU 链表分为 young / old 两个部分,通过参数 innodb_old_blocks_pct 调节 old 区域占比;当 buffer pool 空间不足时,会优先淘汰 old 区域的数据页
  • 我们可以通过指定参数 innodb_buffer_pool_instances 来控制 Buffer Pool 实例的个数,每个 Buffer Pool 实例中都有各自独立的链表,互不干扰。
  • 自从 MySQL 5.7.5 之后,可以在服务运行过程中调整 Buffer Pool 大小;每个 Buffer Pool 由若干个 chunk 组成
  • 可以通过 SHOW ENGINE INNODB STATUS 命令查看 buffer pool 状态

redo / undo / bin / buffer pool

相互之间如何配合的呢,观察一个更新操作过程

update table_1 set col_1 = 1 where id < 5 
  • 首先定位需要更新的数据所在(页面)位置,如果所在页面已经进入 buffer pool,直接加锁读取,否则,需要将整个页面数据从磁盘加载到 buffer pool,再加锁读取;
  • 读取后,判断更新前后记录是否会发生变化,如果没有变化,则直接跳过该记录;
  • 在真正更新记录之前,InnoDB 会记录 undo log,即修改了 undo log 所在的页面,在此之前也会记录对应的 redo log;
  • 真正执行页面修改动作(更新聚簇索引页),在此之前也会记录 redo log;
  • 更新二级索引页,不会记录 undo log,但是会记录相应页面的 redo log;
  • 记录 bin log;
  • 两阶段提交

备注: 更新聚簇索引,首先尝试在当前页面更新记录,即乐观更新;如果发现在当前页面无法完成语句,则会尝试悲观更新