本节摘要:主从复制把主库的数据变更实时搬运到从库,读写分离在此之上把读流量分出去。本节讲清 binlog 三种格式的取舍、复制链路的两线程模型、延迟的成因,以及写后读一致性的三种策略。位置:6.1 并发地基之上的第一级架构扩展,也是高可用方案的起点。
评审会问"主从复制的原理",能讲清三段式的不多。完整链路是:①主库执行事务,把变更写进 binlog(二进制日志,记录的是"做了什么"或"数据变成了什么");②主库的 dump 线程把 binlog 内容推送给从库;③从库的 IO 线程接收并写入本地 relay log(中继日志);④从库的 SQL 线程重放 relay log 里的变更。理解这条链,延迟问题就有了定位坐标系:主库提交快但 dump 慢?从库 IO 慢?还是 SQL 重放慢?
binlog 三种格式决定重放的忠实度:STATEMENT 记 SQL 语句本身,日志小,但 NOW()、UUID() 这类非确定性函数在从库重放会得到不同结果;ROW 记每行数据的前后镜像,忠实可靠但日志大,是 8.0 的默认;MIXED 混合两者按需切换。现状共识:默认 ROW,配合 binlog_row_image 控制日志量。ROW 还有一个隐藏福利——数据误删时可从 binlog 反向恢复,这在 7.1 备份节还会回来。

复制是异步的,延迟是必然的,问题是多大与怎么办。常见成因按频率排:从库机器规格低于主库(单线程重放追不上多线程写入)、从库上的大查询拖住 SQL 线程、主库大事务(一条语句改一百万行,从库要整体重放完)、从库数量过多摊薄主库 dump 带宽。对症下药:8.0 的并行复制(基于 WRITESET 的多线程重放)解决追不上;大事务拆小解决重放慢;级联复制缓解主库推送压力。
读写分离落地后最大的坑是写后读不一致:用户刚下单,跳转到订单页查不到——写进了主库,读打到了延迟 200ms 的从库。三种策略按业务容忍度选,如图底部所示。我的建议是先做策略③(会话级粘滞)做兜底,再对支付、余额等强一致场景显式走策略①,策略②留给中间件层统一实现。
背景:测试环境搭建主从,验证读写分离前的复制链路。操作四步:
-- 主库:创建复制账号(最小授权,仅 REPLICATION SLAVE) CREATE USER 'repl'@'10.0.%' IDENTIFIED BY '复制专用密码'; GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.%'; -- 主库:查看当前位点(8.0 推荐 GTID 模式,这里演示传统模式) SHOW MASTER STATUS; -- 输出 File: binlog.000042 Position: 88213 -- 从库:指向上游(IP 与位点按实际填写) CHANGE MASTER TO MASTER_HOST = '10.0.1.10', MASTER_USER = 'repl', MASTER_PASSWORD = '复制专用密码', MASTER_LOG_FILE = 'binlog.000042', MASTER_LOG_POS = 88213; START SLAVE; -- 从库:检查复制状态 SHOW SLAVE STATUS; -- 关键字段:Slave_IO_Running: Yes Slave_SQL_Running: Yes -- Seconds_Behind_Master: 0
结果:两项 Running 均为 Yes、延迟为 0,链路就绪。验证动作:主库建一张临时表,从库立刻能查到。解读:真出问题时按"先看两个 Running、再看延迟、再对位点"排查;IO 线程 No 多为网络或账号问题,SQL 线程 No 多为重放冲突(常见于从库被误写,从库必须设 read_only)。变式:GTID 模式用 MASTER_AUTO_POSITION = 1 替代手工对位点,换主、补从都省心,新搭建一律建议 GTID。
要点回顾:复制链路四段式,binlog 格式默认 ROW;延迟定位按 dump、IO、重放三段排查;写后读一致三策略按业务容忍度选择;从库必设 read_only,GTID 优于位点。读写分了流,单库写入的容量天花板还在,下一节把数据切开。
读写分离最大的坑不在架构,而在延迟:刚写入的数据,从库读不到。用户下单成功却看不到订单,是这个架构下最高频的投诉。
先看延迟数字,再看数字可信度。
SHOW SLAVE STATUS\G -- Seconds_Behind_Master: 0 -- Slave_SQL_Running_State: Waiting for dependent transaction to commit
Seconds_Behind_Master 为 0 不等于真的没延迟:它衡量的是从库当前执行的事务时间戳与系统时间的差,若从库 SQL 线程正在等待并行事务提交,这个字段可能显示为 0 而实际已经积压。更可靠的做法是打点对比:在主库写一张心跳表(每秒更新一次时间戳),在从库读这个时间戳,差值才是真实的业务可感知延迟。
延迟的来源按出现频率排序:
处置时的业务侧手段(按侵入性从低到高):
评审会上的默认结论通常是:读写分离默认开启,但对"写后立即读"的场景必须显式指定主库。这条规则写进开发规范,比事后逐个接口排查成本低得多。