1. 读写分离到底在解决什么问题
1.1 从单库瓶颈说起
很多项目一开始都是单库打天下,业务量小的时候完全没问题,查询和写入都走同一个数据库实例,简单直接。但用户量一上来,最先扛不住的就是数据库连接数和磁盘IO。我经历过一个项目,日活刚过五万,数据库的QPS就飙到了八千多,其中八成以上是读操作。这时候你加索引、加缓存,能优化的都优化了,但单实例的物理上限就摆在那里,CPU和IOPS都已经吃满。
读写分离的核心思路很朴素:把读操作和写操作分到不同的数据库实例上。主库专门负责写,从库专门负责读。这样做的好处有三个层面。第一是负载分摊,原本一个实例扛的所有查询压力,现在分散到多个从库上,每个实例的压力就下来了。第二是资源隔离,写操作通常涉及事务、锁、日志刷盘,对IO的冲击比较大,读操作相对轻量,分开之后互不干扰。第三是扩展性,读不够就加从库,理论上可以横向扩展读能力。
但这里有个前提,你的业务得是读多写少的场景。大部分互联网业务都符合这个特征,比如电商的商品详情页、社交的动态流、资讯的内容页,读请求往往是写请求的几十倍甚至上百倍。如果你的业务是写多读少,比如日志采集、物联网传感器数据上报,那读写分离的收益就很有限了。
1.2 主从复制是读写分离的地基
读写分离能成立,靠的是主从复制机制。主库把数据的变更通过binlog记录下来,从库拉取这些binlog并在本地重放,最终达到和主库一致的状态。MySQL原生的主从复制就是这么干的,整个过程是异步的,这也是后面所有一致性问题的根源。
主从复制的工作流程大致是这样的:主库接收到写请求后,先写binlog,然后事务提交,最后把binlog发送给从库。从库的IO线程接收binlog并写入本地的relay log,然后SQL线程读取relay log并执行。注意这里的关键点——主库的事务提交和从库的数据更新之间是有时间差的,这个时间差就是主从延迟。
主从延迟的大小取决于很多因素。从库的硬件配置如果比主库差,重放速度就跟不上。主库的写入压力大,binlog产生速度快,从库消费不过来也会积压。还有大事务的问题,一个涉及百万行更新的SQL,在主库上可能执行了几十秒,在从库上重放同样需要几十秒,这期间从库的数据就是滞后的。网络抖动也会影响binlog的传输速度。
1.3 读写分离的几种实现方式
实现读写分离有几种常见的路子。最简单的是在应用层做,代码里配置两个数据源,写走主库,读走从库。这种方式灵活,但侵入性强,每个需要读写分离的地方都得手动指定。稍微好一点的是用中间件,比如在应用和数据库之间加一层代理,应用只连代理,代理根据SQL类型自动路由。这种方式对应用透明,但多了一层网络开销,而且代理本身可能成为瓶颈。
还有一种是在ORM框架层面做,比如某些ORM支持配置主从数据源,框架自动根据方法名或注解来判断走主还是走从。这种方式对代码的侵入性最小,但灵活性也最差,遇到复杂场景可能不够用。
选择哪种方式,取决于你的团队技术栈和运维能力。如果团队对中间件比较熟悉,运维体系也完善,用代理层是比较省心的。如果团队更习惯在代码里控制,那应用层方案更直接。没有绝对的好坏,只有适不适合。
2. 数据一致性的冲突从何而来
2.1 异步复制带来的时间窗口
前面说了,MySQL的主从复制默认是异步的。这意味着主库提交事务后,不会等待从库确认就返回成功给客户端。从库什么时候能追上,取决于复制线程的处理速度。这个时间窗口就是数据不一致的根源。
举个具体的场景。用户在电商平台下单,写请求打到主库,主库写入订单记录后返回成功。用户马上跳转到订单列表页,这个查询走的是从库。如果从库还没重放到这条订单的binlog,用户就会看到自己的订单不见了。用户可能会刷新几次,或者直接找客服投诉。这种体验问题在业务上是不可接受的。
这个时间窗口可能很短,几十毫秒,也可能很长,几秒甚至几十秒。取决于主从之间的负载差异和网络状况。在压力大的时候,从库延迟几秒是常有的事。对于金融类业务,这个延迟完全不能接受。对于社交类业务,用户发完动态自己能看到就行,别人晚几秒看到问题不大。
2.2 读写分离下的经典不一致场景
除了上面说的“写后读”不一致,还有几种典型场景。第一种是“读后写”不一致。用户看到一个数据,基于这个数据做了修改,但修改的时候发现数据已经变了。比如用户看到商品库存是10件,下单的时候提示库存不足。这是因为用户看到的是从库的旧数据,主库上库存已经被其他订单扣减了。
第二种是“写后写”不一致。用户连续做了两次写操作,第二次写操作依赖第一次的结果。如果两次写操作之间发生了主从切换,或者第二次写请求被路由到了不同的主库,就可能出问题。不过这种情况在单主架构下比较少见,主要是主从切换的时候需要注意。
第三种是“事务内不一致”。一个事务里既有读又有写,如果读走了从库,写走了主库,事务的隔离性就被破坏了。事务内读到的数据可能不是事务开始时的快照,导致逻辑错误。所以事务内的操作必须全部走主库,这一点在实现读写分离时一定要保证。
2.3 一致性需求的业务分级
不是所有业务都需要强一致性。把业务按一致性需求分级,是解决冲突的第一步。强一致性场景包括:支付、订单状态变更、库存扣减、账户余额变动。这些场景下,数据错了就是真金白银的损失,必须保证读到的数据是最新的。
弱一致性场景包括:商品列表、用户动态、评论、点赞数。这些场景下,数据晚几秒更新用户基本无感,或者即使感知到了也不会造成实质损失。对于这类场景,读写分离可以放心用,从库延迟几秒完全没问题。
还有一类是“会话一致性”,同一个用户的同一个会话内,写完之后自己能看到自己的修改。这个需求比强一致性弱,比弱一致性强。实现方式通常是会话粘滞,把同一个用户的请求都路由到同一个从库,或者写完之后短时间内强制走主库。
3. MySQL主从复制的核心机制拆解
3.1 binlog的三种格式与选择
MySQL的binlog有三种格式:STATEMENT、ROW和MIXED。STATEMENT记录的是SQL语句本身,日志量小,但有些函数(比如NOW()、RAND())在主从执行结果可能不一致。ROW记录的是每一行数据的变更,日志量大,但能保证主从数据绝对一致。MIXED是混合模式,MySQL自动判断用哪种。
现在生产环境基本都用ROW格式。虽然日志量大一些,但数据一致性有保障。特别是用了读写分离之后,从库的数据必须和主库严格一致,STATEMENT格式的那些不确定性是绝对不能接受的。ROW格式还有一个好处是恢复数据方便,binlog里直接有变更前后的行数据,做数据恢复的时候不用去猜SQL的执行逻辑。
不过ROW格式也有坑。批量更新的时候,binlog会膨胀得很快。比如一条UPDATE语句更新了十万行,ROW格式下binlog里会有十万条记录。这不仅占用磁盘空间,还会加重主从复制的负担。所以大事务要尽量避免,分批处理是更好的选择。
3.2 主从复制的线程模型
主从复制涉及三个线程。主库上有一个binlog dump线程,负责把binlog发送给从库。从库上有两个线程:IO线程负责接收binlog并写入relay log,SQL线程负责读取relay log并执行。
这里有个关键参数是slave_parallel_workers,控制从库并行重放的线程数。在MySQL 5.7之前,从库的SQL线程是单线程的,这意味着主库上的并行写入,在从库上只能串行重放。主库写入压力大的时候,从库根本追不上,延迟会越来越大。
MySQL 5.7引入了基于组提交的并行复制,5.8(即MySQL 8.0)进一步优化了。原理是主库上同一组提交的事务之间没有冲突,可以在从库上并行重放。这个参数设置多少合适?一般建议设置为CPU核数的2到4倍。但也不是越大越好,线程太多会有上下文切换的开销,而且事务之间的依赖关系可能导致并行度上不去。
3.3 半同步复制的取舍
异步复制的问题是主库宕机可能丢数据。主库提交了事务,binlog还没发给从库,主库就挂了,这部分数据就丢了。半同步复制就是为了解决这个问题。主库提交事务后,等待至少一个从库确认收到binlog,才返回成功给客户端。
半同步复制提高了数据安全性,但牺牲了写入性能。每个写操作都要等从库的网络往返,延迟至少增加一个RTT。在跨机房部署的场景下,这个延迟可能达到几毫秒甚至十几毫秒。对于写入频繁的业务,这个开销是很大的。
而且半同步复制有个退化机制。如果从库响应超时,主库会自动退化为异步复制。这个超时时间由rpl_semi_sync_master_timeout控制,默认是10秒。也就是说,从库挂了之后,主库会等10秒,然后自动切回异步模式。这10秒内所有写操作都会阻塞,对业务的影响很大。所以这个参数要根据业务容忍度来调整,一般建议设小一点,比如1秒。
4. 读写分离与一致性的平衡方案
4.1 强制走主库的策略
最简单的方案是:对一致性要求高的读操作,强制走主库。比如用户刚下完单,跳转到订单详情页,这个查询直接走主库。虽然增加了一点主库的读压力,但保证了用户能看到最新的数据。
哪些操作需要强制走主库?我总结了几类:写操作之后的立即读、事务内的所有读、对数据实时性要求高的查询(比如库存、余额)。实现方式可以是在代码里显式指定,也可以用注解标记,让框架自动路由。
这个策略的代价是主库的读压力会增加。如果所有写后读都走主库,主库的QPS可能会上升不少。所以需要权衡,不是所有写后读都必须走主库。比如用户修改了头像,然后刷新页面,这个读走从库晚几秒看到新头像,用户其实不会太在意。但如果是支付成功后的订单状态查询,那就必须走主库。
4.2 延迟感知与自动降级
更智能的方案是让中间件或框架感知主从延迟,根据延迟情况自动决定路由。如果从库延迟在可接受范围内(比如100毫秒以内),读请求就走从库。如果延迟超过了阈值,就自动降级走主库。
实现延迟感知的方式有几种。一种是通过SHOW SLAVE STATUS命令获取Seconds_Behind_Master的值,但这个值有时候不准,特别是在从库SQL线程繁忙的时候。另一种是通过心跳表,主库定期更新心跳表的时间戳,从库读取这个时间戳和当前时间的差值就是延迟。这种方式更准确,但需要额外的写操作。
延迟感知的阈值设置很关键。设得太小,频繁降级走主库,读写分离的意义就不大了。设得太大,用户会看到旧数据。一般建议设置在500毫秒到1秒之间,具体看业务容忍度。对于大部分业务,1秒的延迟用户基本无感。
4.3 会话一致性保障
会话一致性是指同一个用户的同一个会话内,写完之后自己能看到自己的修改。这个需求在读写分离下需要特别处理。常见的做法是会话粘滞,把同一个会话的请求都路由到同一个从库。这样至少保证了会话内的读一致性,因为同一个从库的重放顺序和主库是一致的。
但会话粘滞有个问题:如果这个从库挂了,会话就断了。而且会话粘滞会导致负载不均,某些从库可能压力过大。另一种做法是写完之后打个标记,短时间内(比如1秒)这个会话的读请求都走主库。过了这个时间窗口再走从库。这种方式实现简单,但需要维护会话状态。
还有一种更精细的做法是记录位点。写操作完成后,记录下主库的binlog位点。读请求的时候,检查从库是否已经重放到这个位点。如果已经重放到了,就走从库;如果没有,就走主库或者等待。这种方式最精确,但实现复杂度也最高,一般只有对一致性要求极高的场景才会用。
5. 实操配置与参数调优
5.1 主从复制的基础配置
先看主库的配置。在my.cnf里需要设置server-id,每个实例必须唯一。log-bin指定binlog的路径和文件名前缀。binlog-format设为ROW。sync-binlog控制binlog刷盘策略,设为1表示每次事务提交都刷盘,最安全但性能最差;设为0表示由操作系统决定什么时候刷盘,性能好但可能丢数据。一般建议设为1,毕竟数据安全第一。
从库的配置类似,server-id不能和主库以及其他从库重复。relay-log指定relay log的路径。read-only设为1,防止误写从库。slave-parallel-workers设置并行重放的线程数。slave-parallel-type在MySQL 8.0里可以设为LOGICAL_CLOCK,基于组提交的并行复制。
配置完成后,在主库上创建复制账号,授予REPLICATION SLAVE权限。然后在从库上执行CHANGE MASTER TO命令,指定主库的地址、端口、账号密码和binlog位点。最后START SLAVE启动复制。用SHOW SLAVE STATUS检查复制状态,确保Slave_IO_Running和Slave_SQL_Running都是Yes。
5.2 关键参数的计算与选择
slave_parallel_workers设多少合适?假设从库的CPU是8核,一般建议设为16到32。但实际能并行多少,取决于主库的事务冲突情况。如果主库的事务都是串行的,从库也并行不起来。可以通过监控slave_parallel_workers的实际使用情况来调整。
rpl_semi_sync_master_timeout设多少?这个要看业务对写入延迟的容忍度。如果业务能接受1秒的写入延迟,就设1000毫秒。如果从库挂了,主库最多阻塞1秒就退化为异步复制。设得太小,网络抖动就可能导致退化;设得太大,从库真挂了主库会阻塞很久。
innodb_flush_log_at_trx_commit和sync_binlog这两个参数决定了数据安全性和性能的平衡。innodb_flush_log_at_trx_commit=1表示每次事务提交都刷redo log到磁盘,sync_binlog=1表示每次事务提交都刷binlog到磁盘。两个都设为1是最安全的,但性能最差。如果业务能容忍少量数据丢失,可以设为2或0,性能会好很多。
5.3 监控与告警配置
主从复制的监控主要看几个指标。Seconds_Behind_Master是最直观的,但前面说了它有时候不准。更可靠的是通过心跳表计算延迟。还可以监控Relay_Log_Space,如果这个值持续增长,说明从库重放速度跟不上接收速度,延迟会越来越大。
告警阈值怎么设?Seconds_Behind_Master超过10秒就应该告警了,超过60秒就是严重问题。但也要看业务,如果是夜间批量任务导致的延迟,可能可以容忍。关键是区分正常波动和异常情况。建议设置多级告警,比如超过10秒发通知,超过60秒发严重告警。
除了延迟,还要监控复制线程的状态。Slave_IO_Running和Slave_SQL_Running任何一个变成No,都说明复制断了。常见的原因包括网络中断、主库binlog被清理、从库磁盘满等。需要配置自动告警,第一时间发现和处理。
6. 常见问题与排查技巧实录
6.1 主从延迟越来越大的排查思路
主从延迟持续增大,通常有几个原因。先看从库的硬件负载,CPU、内存、磁盘IO是不是吃满了。如果从库配置比主库差太多,重放速度跟不上是必然的。这种情况要么升级从库硬件,要么减少从库数量,让每个从库分担的压力小一些。
再看主库的写入模式。如果有大事务,比如一次性更新几十万行,从库重放这个事务需要很长时间,期间其他事务都得等着。解决办法是把大事务拆成小批次,比如每批1000行,分批提交。这样从库可以并行重放不同批次,延迟就下来了。
还要检查是否有长查询阻塞了从库的SQL线程。从库的SQL线程是单线程重放的(如果没开并行复制),一个慢查询会阻塞后面所有事务的重放。可以开启慢查询日志,看看从库上有没有执行时间很长的SQL。如果有,考虑优化这些查询,或者在从库上设置max_statement_time限制执行时间。
6.2 主从数据不一致的修复方法
数据不一致的发现方式有几种。可以用pt-table-checksum工具定期校验主从数据。这个工具会在主库上计算每个表的校验和,然后在从库上对比。如果不一致,会用pt-table-sync工具修复。
修复的时候要注意,pt-table-sync默认是在主库上执行修复SQL,然后让从库通过复制同步。但如果复制已经断了,或者不一致的原因就是复制出了问题,这种方式可能不管用。更稳妥的方式是直接在从库上执行修复SQL,但要注意跳过复制的冲突。
还有一种情况是主从切换后出现的不一致。主库挂了,从库提升为主库,但原来的主库恢复后变成了从库,它的数据可能比新主库多(因为有些事务没来得及同步)。这时候需要把多出来的数据回滚掉,或者把新主库缺失的数据补上。这个过程很复杂,建议用专业的工具来处理,不要手动操作。
6.3 读写分离下的典型故障案例
我遇到过一个案例:用户反馈下单后订单列表看不到新订单。排查发现,订单列表查询走了从库,而从库延迟了3秒。用户下单后立即跳转,正好在这3秒窗口内,所以看不到。解决办法是把下单后的订单详情查询强制走主库,订单列表查询可以走从库但加一个短时间的会话粘滞。
还有一个案例是库存超卖。用户A看到库存10件,下单买了1件。用户B也看到库存10件,也下单买了1件。两个请求都走了从库读库存,都认为库存充足。但主库上库存已经被扣了两次,实际只剩8件。解决办法是库存扣减必须在主库上做,而且要用乐观锁或悲观锁保证并发安全。读库存可以走从库,但扣减必须走主库。
还有一个比较隐蔽的问题:主从切换后,应用还连着旧的从库。旧从库被提升为主库后,应用配置没更新,写请求还是发到旧主库(现在是只读的),导致写入失败。解决办法是应用配置要支持动态更新,或者用VIP(虚拟IP)来屏蔽后端实例的变化。
6.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 从库延迟持续增大 | 从库硬件不足 | 查看从库CPU、IO负载 | 升级从库硬件或减少从库数量 |
| 从库延迟持续增大 | 主库有大事务 | 查看主库binlog大小 | 拆分大事务,分批提交 |
| 从库延迟持续增大 | 从库有慢查询 | 查看从库慢查询日志 | 优化慢查询或限制执行时间 |
| 主从复制中断 | 网络问题 | 检查网络连通性 | 修复网络,重启复制 |
| 主从复制中断 | binlog被清理 | 查看主库binlog保留策略 | 调整binlog保留时间,重建从库 |
| 主从数据不一致 | 复制过程中出错 | 用pt-table-checksum校验 | 用pt-table-sync修复 |
| 写后读不一致 | 从库延迟 | 检查Seconds_Behind_Master | 强制走主库或加会话粘滞 |
| 库存超卖 | 读走了从库 | 检查库存扣减逻辑 | 扣减走主库,加锁保证并发安全 |
7. 架构演进与扩展思考
7.1 从一主一从到多主多从
业务量继续增长,一主一从可能不够用了。读压力大,可以加从库,一主多从。但主库的写压力还是瓶颈,这时候可以考虑分库分表,把数据分散到多个主库上。每个主库有自己的从库,读写分离的粒度更细。
多主架构下,一致性问题更复杂。不同主库之间的数据同步需要额外的机制,比如用中间件做数据同步,或者用分布式事务保证跨库一致性。这些方案的复杂度都比单主高很多,需要仔细评估是否真的需要。
还有一种架构是主主复制,两个主库互为主从。这种架构下,两个主库都可以写,但需要解决冲突问题。比如两个主库同时更新同一行数据,谁赢?通常用时间戳或版本号来解决,但逻辑很复杂,容易出错。除非有特殊需求,一般不建议用主主复制。
7.2 云原生时代的读写分离
云原生环境下,数据库服务通常提供了读写分离的能力。比如云数据库RDS,可以一键创建只读实例,应用通过连接不同的地址来实现读写分离。这种方式省去了自己搭建主从复制的麻烦,但底层原理还是一样的,延迟和一致性问题依然存在。
云原生还带来了新的可能性,比如Serverless数据库,可以根据负载自动扩缩容。读压力大的时候自动加只读实例,压力小了自动缩容。这种弹性能力是自建数据库很难做到的。但Serverless数据库的延迟可能比自建的高,因为涉及到冷启动和网络转发。
还有一个趋势是用NewSQL数据库,比如TiDB、OceanBase,它们原生支持分布式事务和强一致性。读写分离在NewSQL里是透明的,应用不需要关心读走哪个节点。但NewSQL的运维复杂度和成本都比MySQL高,适合对一致性要求极高且预算充足的场景。
7.3 缓存与读写分离的配合
读写分离解决的是数据库层面的读扩展问题,但数据库的读性能再扩展,也比不上缓存。所以实际架构中,缓存和读写分离通常是配合使用的。热点数据放缓存,缓存没命中再查从库,从库也没有再查主库。
缓存和读写分离配合的时候,一致性问题更复杂。缓存更新和数据库更新之间也有时间窗口。常见的做法是写操作更新数据库后,删除缓存。读操作先查缓存,没命中查从库,然后回写缓存。但删除缓存和回写缓存之间也可能有不一致,需要根据业务容忍度来设计。
还有一种做法是用binlog订阅来更新缓存。主库的binlog变更后,通过中间件订阅binlog,然后更新缓存。这种方式解耦了业务代码和缓存更新逻辑,但增加了系统的复杂度。而且binlog订阅本身也有延迟,缓存更新可能滞后于数据库更新。
8. 个人实操经验与避坑建议
8.1 不要过早引入读写分离
我见过不少项目,日活才几千,数据库压力也不大,就急着上读写分离。结果引入了主从延迟、数据不一致、运维复杂度等一系列问题,收益却微乎其微。读写分离应该是数据库压力确实扛不住了才考虑的方案,而不是一开始就上的架构。
判断是否需要读写分离,可以看几个指标。数据库的CPU持续超过70%,磁盘IOPS接近上限,慢查询数量持续增长,连接数经常打满。这些指标出现的时候,先考虑优化SQL、加索引、加缓存,这些手段都用完了还不够,再考虑读写分离。
8.2 从库不是越多越好
加从库可以分摊读压力,但也不是越多越好。每个从库都要从主库拉binlog,主库的binlog dump线程要维护和每个从库的连接。从库太多,主库的网络带宽和CPU都会成为瓶颈。而且从库越多,主从延迟的监控和运维成本越高。
一般建议从库数量控制在3到5个。如果读压力还是很大,可以考虑在从库前面再加一层缓存,或者做分库分表。从库的数量和主库的写入量也有关系,写入量大的话,从库太多会导致主库的binlog发送成为瓶颈。
8.3 定期演练主从切换
主从切换是读写分离架构下必须面对的运维操作。主库挂了,要把从库提升为主库,应用配置要更新,原来的主库恢复后要重新加入复制。这个过程如果没演练过,真出故障的时候很容易手忙脚乱。
建议定期做切换演练,比如每季度一次。演练内容包括:模拟主库故障,手动或自动切换,验证应用是否正常,验证数据是否一致。演练过程中记录每个步骤的耗时,找出可以优化的环节。切换时间越短,业务受影响越小。
8.4 监控要覆盖复制链路的所有环节
主从复制的监控不能只看延迟。要覆盖整个链路:主库的binlog写入是否正常,binlog dump线程是否在运行,网络传输是否稳定,从库的IO线程和SQL线程是否正常,relay log是否有积压。任何一个环节出问题,都会导致复制异常。
告警要分级。延迟超过阈值是警告,复制线程断了是严重告警。告警要能自动通知到负责人,最好能自动触发一些恢复操作,比如重启复制线程。但自动恢复要谨慎,有些问题重启解决不了,反而可能掩盖真正的原因。
8.5 应用层要做好降级预案
读写分离架构下,从库挂了或者延迟太大,应用要有降级预案。最简单的降级是所有读请求走主库,虽然主库压力会增大,但至少业务能继续。降级开关要能动态调整,不需要重启应用。
降级预案要提前测试。不能等到真出故障了才发现降级逻辑有问题。测试的时候要模拟从库不可用的情况,验证应用是否能自动降级,降级后主库是否能扛住。如果主库扛不住,那降级也没意义,需要考虑更彻底的方案,比如限流。
8.6 数据一致性校验要常态化
主从数据不一致是迟早会发生的事,关键是要能及时发现和修复。建议每天做一次数据校验,用pt-table-checksum工具检查核心表的数据一致性。校验放在业务低峰期,避免对数据库造成太大压力。
校验发现不一致后,要分析原因。是复制过程中出错,还是应用层有误写?如果是复制的问题,修复数据后还要检查复制配置,避免再次发生。如果是应用层的问题,要修复代码逻辑。每次不一致都是一次改进的机会,不要只是修复数据就完了。
8.7 大事务是主从延迟的头号杀手
我踩过最大的坑就是大事务。一个批量更新操作,在主库上执行了30秒,从库重放也花了30秒。这30秒内,从库的延迟从0飙升到30秒,所有读请求都读到了旧数据。用户投诉不断,排查了半天才发现是这个批量操作导致的。
从那以后,我定了一条规矩:任何可能影响超过1000行的写操作,必须分批执行。每批控制在500到1000行,批次之间 sleep 一小段时间,让从库有时间重放。虽然总执行时间变长了,但主从延迟可控,业务影响小。
还有一个坑是DDL操作。给大表加索引或改字段,在主库上可能很快,但在从库上重放的时候,如果从库的SQL线程是单线程的,这个DDL会阻塞后面所有事务。MySQL 8.0支持在线DDL,但有些操作还是会锁表。做DDL之前一定要评估影响,最好在业务低峰期做,并且监控从库延迟。
8.8 网络抖动比想象中更常见
跨机房部署的时候,网络抖动是家常便饭。主从之间的网络延迟从1毫秒突然跳到100毫秒,binlog传输变慢,从库延迟就上来了。如果用了半同步复制,主库的写操作也会被拖慢。
应对网络抖动,一方面要选好机房和网络线路,尽量保证网络质量。另一方面要配置合理的超时和重试参数。半同步复制的超时时间不能设太大,否则网络抖动会导致主库长时间阻塞。但也不能设太小,否则频繁退化为异步复制,失去了半同步的意义。
监控网络质量也很重要。可以用ping或tcpdump监控主从之间的网络延迟和丢包率。如果发现网络质量持续下降,要提前和网络团队沟通,看是否需要调整路由或升级带宽。不要等到复制断了才去排查网络问题。