
面试官,我完整、通俗地讲一下 MySQL 主从复制的原理、流程、核心机制、常见问题和优化思路。
首先,主从复制在我们实际项目里主要有三个核心作用。第一是读写分离,把所有写操作压在主库,读操作分摊到从库,极大提升数据库并发能力;第二是实时热备份,从库一直同步主库数据,相当于实时备份,防止主库故障丢数据;第三是支撑高可用架构,像主从切换、MHA、读写分离集群,全部都是基于主从复制实现的。
它的核心原理其实很简单,就是主库记录变更日志,从库拉取日志、本地重放,最终实现数据最终一致。整个过程是异步、分步完成的。
我先讲主库这边的工作机制。
主库所有的增、删、改、建表、改表这些写操作,在事务提交的瞬间,不会立刻同步给从库,而是先写入 binlog 二进制日志。这个日志是主从复制唯一的数据源,它会完整记录每一次数据变更、操作时间、执行位置位点。
当从库启动 slave 之后,会主动连上主库,主库会为每一个从库单独创建一个 DUMP 线程。这个线程专门负责监听 binlog,只要主库产生新日志,它就立刻读取、打包、推送给从库。
接下来是从库,从库是整个复制流程的重点,主要靠两个线程、两个位点文件完成同步。
第一个是 IO 线程。它的作用就是专门拉数据。它会一直跟主库 DUMP 线程保持连接,持续接收推送过来的 binlog 日志,然后把日志落地写到从库本地的 relay-log 中继日志。同时它会更新 master-info 文件,记录当前同步到主库哪个日志文件、哪个偏移位置。这样就算从库重启、断网,下次也能从上次位置继续同步,不会重复同步、也不会漏数据。
第二个是 SQL 线程。它的作用就是重放数据。它不断读取本地的中继日志,把日志里的操作解析出来,在从库本地重新执行一遍,复现主库所有的数据变更。执行完成后会更新 relay-log.info 位点文件,记录已经执行到哪里了,保证日志不重复执行。已经执行完的中继日志,MySQL 会自动清理,不会一直堆积占磁盘。
我把完整流程串一遍,方便整体理解:主库事务提交、写入 binlog,主库 DUMP 线程推送日志,从库 IO 线程拉取日志写入中继日志、记录拉取位点,最后 SQL 线程回放执行、记录执行位点。之后所有增量数据,都会循环这个过程,持续同步。
然后我再说一下 binlog 的三种格式,生产环境非常关键。
第一种是 Statement 语句模式,只记录原始 SQL,日志体积小,但是遇到函数、随机数、时间函数,主从容易数据不一致,现在基本不用。
第二种是 ROW 行级模式,也是我们生产默认必用的,它不记录 SQL,直接记录每一行数据前后的变更,数据一致性百分之百,不会出错,唯一缺点是日志会稍微大一点,但稳定性最高。
第三种是 Mixed 混合模式,数据库自动判断,简单语句用 statement,危险语句自动切 row,现在用得也比较少。
再讲一下复制的同步模式。MySQL 默认是异步复制,主库写完 binlog 直接返回客户端,不用等从库,性能最好,但极端情况下主库宕机,可能会丢失少量还没推送的日志。
所以生产重要业务会开启半同步复制,主库必须等到至少一个从库接收日志、写入中继日志成功之后,才返回成功,数据安全性大幅提升,只牺牲一点点性能。
最后说一下面试高频的主从延迟问题和优化方案。
产生延迟的核心原因有几个:第一,低版本 MySQL 从库只有单 SQL 线程,主库并发写入,从库串行回放跟不上;第二,大事务、大批量更新删除,主库瞬间执行完,从库回放很慢;第三,从库机器性能差、磁盘 IO 压力大;第四,从库承担大量查询压力,抢占了 CPU 和 IO 资源。
对应的优化方案:首先开启并行复制,5.7 版本组提交并行、8.0 事务级并行,大幅提升回放速度;其次业务上严格拆分大事务,禁止大批量 DML;然后做好读写分离,查询压力不要压在从库回放节点上;最后硬件升级,使用 SSD 提升磁盘 IO。
整体总结一下,MySQL 主从复制就是:主库 DUMP 线程推 binlog,从库 IO 线程拉日志存中继,SQL 线程本地回放,依靠位点文件断点续传,最终实现数据最终一致,是 MySQL 高可用、读写分离的基础核心。