什么是 MySQL 的主从同步机制?它是如何实现的?

Sherwin.Wei Lv7

什么是 MySQL 的主从同步机制?它是如何实现的?

回答重点

MySQL 的主从同步机制是一种数据复制技术,用于将主数据库(Master)上的数据同步到一个或多个从数据库(Slave)中。

主要是通过二进制日志(Binary Log,简称 binlog)实现数据的复制。主数据库在执行写操作时,会将这些操作记录到 binlog 中,然后推送给从数据库,从数据库重放对应的日志即可完成复制。

扩展知识

MySQL 主从复制类型

MySQL 支持异步复制、同步复制、半同步复制。

  • 异步复制:主库不需要等待从库的响应(性能较高,数据一致性低)。
  • 同步复制:主库同步等待所有从库确认收到数据(性能差,数据一致性高)。
  • 半同步复制:主库等待至少一个从库确认收到数据(性能折中,数据一致性较高)。

异步复制

MySQL 默认是异步复制,具体流程如下:

主库:

  • 接受到提交事务请求
  • 更新数据
  • 将数据写到binlog中
  • 给客户端响应
  • 主库推送 binlog 变更事件到从库中
  • 从库接收到事件去主库拉取数据

从库:

  • 由 I/O 线程将同步过来的 binlog 写入到 relay log 中。
  • 由 SQL 线程从 relay log 重放事件,更新数据
  • 给主库返回响应。
image.png

用一句话概括一下:主库提交事务会写 binlog,会由一个 dump 线程监听 binlog 文件的变更,如果 binlog 有更新则推送更新事件给从库,从库接收到事件后拉取数据,会有一个 I/O 线程将其写到 relay log 中,慢慢消化,由 SQL 线程来重放更新数据。

异步复制有数据丢失风险,例如数据还未同步到从库,主库就给客户端响应,然后主库挂了,此时从库晋升为主库的话数据是缺失的。

同步复制

主库需要将 binlog 复制到所有从库,等所有从库响应了之后才会给客户端响应,这样的话性能很差,一般不会选择同步复制。

半同步复制

MySQL 5.7 之后搞了个半同步复制,有个参数可以选择“成功同步几个从库就返回响应。”

比如一共有 3 个从库,我参数配置 1,那么只要有一个从库响应说复制成功了,主库就直接返回响应给客户端,不会等待其他两个从库。

这样的话性能就比较好,并且数据可靠性也增强了,只有当那个从库和主库同时都挂了,才会缺失数据。

并行复制

以前从库是通过一个 SQL 线程按照顺序逐条执行主库的 binlog 日志指令(即从 relay log 重放事件)。对于主库的高并发写入操作,这种串行执行的方式会导致从库的复制速度跟不上主库,从而产生主从延迟问题。

所以 MySQL 引入了并行复制,说白了就是通过 多个 SQL 线程 来并发执行重放事件。MySQL 提供了以下几种并行复制模式。

MySQL 5.6 基于库级别的并行复制

假设主库有多个数据库(如 db1db2),在从库上,db1 的事务和 db2 的事务可以同时执行。即将不同数据库上的事务分配到不同的 SQL 线程中执行。如果主库大部分事务集中在一个数据库上,这个就没啥用了。

MySQL 5.7 基于组提交(Group Commit)事务的并行复制

进一步细化了并行的颗粒度,从库级别细化到组提交级别。即 MySQL 会将组提交的事务视为彼此独立的事务,可以在从库并行重放。

如果主库开启了组提交,且事务之间没有冲突。那么这些事务都可以由多线程并行执行。

binlog 中如果两个事务的 last_committed 相同,说明这两个事务是在同一个 Group 内提交的。但是粒度还是不够,如果大部分事务都集中在一张表上?

那么只有相同 last_committed 可以并发,即使有些数据的更改和当前事务是不冲突的,也无法并发。

MySQL 5.7 基于逻辑时钟(LOGICAL_CLOCK)的并行复制

LOGICAL_CLOCK 是基于 Group Commit 的并行复制,引入了时间标记的概念。

基于所有在主库同时处于 prepare 阶段且未提交的事务不会存在锁冲突的情况(就是这些事务在从库执行时都可以并行执行),将这些事务都打上一个时间标记(实际实现用的是上一个提交事务的 sequence_number,即上面提到的 binlog 中的 last_committed)。

这样从库在识别到这些事务时,可以并行,进一步的提高并发度。

所以提升主库的组提交事务数可以让从库复制的并行度更高,所以 MySQL 5.7 引入了两个参数:

  • binlog_group_commit_sync_delay:组提交等待延迟提交的时间。即每次 binlog 组提交时等待一段时间再 fsync。让进入 group 的事务更多,提高并行度。
  • binlog_group_commit_sync_no_delay_count:组提交时允许的最大事务数量,达到该数量时立即触发组提交,而无需等待延迟时间(binlog_group_commit_sync_delay)。即达到期望的并行度后立即提交,缩小等待时长。

MySQL 8.0 基于 WriteSet 的并行复制

在 MySQL 5.7 中,为了提升从库的事务回放速度,需要在主库提高事务的并行度。主库上的事务越多线程并行提交,备库就能在更大程度上实现并行回放。然而,这种方式依赖于主库的并行提交情况,当主库事务是串行提交时,备库的回放效率会显著下降。

所以 MySQL 8.0 引入了 基于 WriteSet 的并行复制,即使主库上的事务是串行提交的,只要事务之间没有冲突,备库也可以并行回放这些事务,提升复制效率。

WriteSet 是事务更新行的集合,通过哈希算法对主键或唯一索引生成标识,记录在二进制日志中。即通过 WriteSet 判定事务之间的冲突,如果两个事务的 WriteSet 没有冲突,则它们可以并行回放。

Comments