单库的三个痛点
MySQL 用一台库跑起来很简单,但业务一上来就碰到三个问题:单点故障(这台机挂了全站停写)、读压力大(所有查询都打在主库上,CPU 打满)、备份影响线上(在主库上做备份会占资源)。主从复制正是为解决这三件事而生:把数据复制到多台从库,读请求分流到从库,主库挂了还有备份顶上。
核心思路一句话:主库负责写,从库负责读和灾备。主库的每一次变更,通过 binlog 异步同步到从库,从库回放同样的操作,保持数据一致。
- binlog主库记录所有写操作的日志,是复制的源头。
- IO/SQL 线程从库拉日志并回放的两个核心线程。
- 读写分离写走主库、读走从库,分摊压力。
- 高可用切换主库宕机时自动提升从库为主。
binlog 复制原理
主库把每一次 INSERT、UPDATE、DELETE 记到 binlog(二进制日志)。从库拉起两个线程:IO 线程负责连到主库、把 binlog 拉下来写到本地 relay log;SQL 线程负责读 relay log、在从库上重放这些变更。这样从库就一步步跟上了主库的每一次写入。
-- 主库:开启 binlog
[mysqld]
server-id=1
log-bin=mysql-bin
-- 从库:指定主库位置并启动
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='10.0.0.1',
SOURCE_USER='repl',
SOURCE_PASSWORD='***',
SOURCE_LOG_FILE='mysql-bin.000001',
SOURCE_LOG_POS=156;
START REPLICA;
server-id 每台库必须唯一,主从靠它区分身份。复制默认是异步的:主库写完就返回,不等从库确认,性能好但极端情况下从库可能落后。
一主多从与读写分离
一台主库可以挂多个从库,通常 1 主 2 到 3 从。写操作走主库,读操作通过代理层(如 ProxySQL、ShardingSphere-JDBC)分发到从库。这样查询压力被分摊,主库专注写入。
但要牢记:刚写完立刻读,可能命中从库、还没同步到,读到旧数据。所以“写后立刻读”的场景(如下单后查订单)要强制走主库,普通列表、报表、后台查询再走从库。
自动故障切换
主库宕机不能靠人手工切,太慢。高可用方案(MHA、Orchestrator、或云厂商的高可用版)会自动做三件事:检测主库是否真挂了(防止误判)、从多个从库里选一个数据最新的提升为新主、把其他从库和应用切到新主上。
切换一般配合 VIP(虚拟 IP)或服务发现:应用连的是一个漂移地址,主变了地址跟着漂,应用无感。这个过程通常在几十秒到一两分钟内完成。
复制延迟与一致性
异步复制下,从库总会比主库慢一点,叫复制延迟。延迟来源:从库单线程回放(高并发主库写入太快追不上)、从库本身在跑大查询拖慢了回放。监控上必须盯 Seconds_Behind_Source 这个延迟值,延迟过大就要告警。
降低延迟的常用手段:从库用并行复制(按库或按事务组并行回放)、给从库留出足够内存和 IO、别在从库上跑重型报表。
半同步与数据安全
异步复制的风险是:主库写完就崩、binlog 还没同步到从库,这条数据就丢了。对不能丢钱的业务(如账户、订单),用半同步复制:主库写完后,至少等一个从库确认收到 binlog 才向客户端返回成功。这样牺牲一点延迟,换“主库宕机不丢已提交数据”。
更严格的还可以配合 GTID(全局事务 ID),让主从切换时不用人工记 binlog 位点,自动找到从哪个位置继续,切换更稳。
脑裂与常见故障
主从架构最可怕的是脑裂:网络分区时,自动切换误以为主库挂了、把从库提成新主,结果旧主其实还活着,于是出现“两个主同时在写”,数据两边冲突。防脑裂的关键是仲裁要可靠——用多数派投票(如借助 ZooKeeper/etcd)而不是单节点判断,并且旧主被降为从库、拒绝写入。
其他常见故障:从库复制中断(Last_SQL_Error)、误在从库上写导致数据不一致、binlog 被误删。日常要定期备份、演练切换,别等真出事才发现流程跑不通。
实战清单
落地清单:
- 主库写、从库读,用代理层做读写分离。
- 写后立刻读的场景强制走主库。
- 关键业务用半同步复制,防主库宕机丢数据。
- 盯复制延迟,从库开并行回放降延迟。
- 自动切换用多数派仲裁,防脑裂。
- 定期备份、定期演练主从切换。
主从复制是 MySQL 高可用的基石。把 binlog 原理、读写分离、故障切换和防脑裂这几件事做扎实,数据库就从“一台会挂的机器”变成“一套能扛故障的服务”。