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;
- 两阶段提交
备注: 更新聚簇索引,首先尝试在当前页面更新记录,即乐观更新;如果发现在当前页面无法完成语句,则会尝试悲观更新