一、为什么需要读写分离
互联网应用通常读多写少——页面浏览量是订单量的10-100倍。如果所有查询都走主库,主库压力会越来越大。
读写分离的思路:
- 主库(Master):处理所有写操作(INSERT、UPDATE、DELETE),也处理强一致性读(如刚下单就要查订单状态)。
- 从库(Slave):通过主从复制同步数据,处理普通读请求(商品列表、文章详情)。
这样读压力被分散到多台从库,写仍集中在主库。
二、主从复制原理
MySQL主从复制的工作流程:
- 主库将变更写入二进制日志(binlog)。
- 从库的I/O线程拉取主库的binlog,写入中继日志(relay log)。
- 从库的SQL线程重放中继日志中的事件,执行同样的数据变更。
整个过程是异步的,因此从库数据会有毫秒到秒级延迟(取决于网络和主库压力)。
三、搭建步骤
3.1 主库配置
[mysqld]
server-id=1
log-bin=mysql-bin
binlog-format=mixed
创建用于复制的账号:GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' IDENTIFIED BY 'password';
3.2 从库配置
[mysqld]
server-id=2
relay-log=relay-bin
设置主库信息:
CHANGE MASTER TO
MASTER_HOST='主库IP',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=0;
启动从库:START SLAVE;
3.3 验证
在从库执行SHOW SLAVE STATUS\G,检查Slave_IO_Running和Slave_SQL_Running都是Yes。
四、应用层如何路由
应用需要知道哪些SQL走主库、哪些走从库。2012年我们有两种做法:
4.1 代码层面手动路由
在DAO层根据SQL类型选择数据源。写操作走主库连接池,读操作走从库连接池。优点是可控,缺点是代码侵入性强。
4.2 使用中间件
2012年我们开始研究MySQL Proxy(官方早期中间件)和Amoeba(国产数据库中间件)。中间件在应用和数据库之间透明代理,自动根据SQL语句路由。但当时中间件稳定性一般,我们还是以手动路由为主。
五、关键问题与对策
5.1 主从延迟
刚写入主库的数据,立即去从库查询可能查不到。对策:
- 写操作后的强一致性读(如下单后查看订单)强制走主库。
- 对延迟不敏感的场景(商品列表)容忍短暂延迟。
- 监控Seconds_Behind_Master,延迟过大时报警。
5.2 从库挂了怎么办
应用必须有降级逻辑:从库连不上时自动切回主库读,虽然性能下降但功能可用。
5.3 多从库负载均衡
读压力更大时可以部署多个从库,应用层随机或轮询分发。我们用简单的轮询算法即可,不需要复杂的负载均衡器。
六、实施效果
2012年我们为两个电商客户实施了读写分离:
- 主库CPU占用率从80%降到30%。
- 页面平均加载时间减少30%。
- 高峰期数据库连接数不再打满。
- 成本只增加了一台从库服务器(约几千元),远优于升级主库硬件。
七、总结
读写分离是数据库扩展的第一步,实现简单、效果明显。2012年我们的客户数据量还没到需要分库分表的程度,读写分离足以应对。等单表数据超过5000万或写入压力持续增长时,再考虑更复杂的分库分表方案。