面试官问:“如果数据库压力太大,你会怎么做?”我说:“分库分表。”他点点头,又问:“那读写分离呢?”我心想,这回该我装逼了——于是,开始了一场关于读写分离的灵魂拷问。
故事从一次线上“事故”开始那是两年前,我还在一家互联网中厂。我们系统的日活不高,平时数据库 MySQL 跑得飞快。直到某天,运营发了一波优惠券活动,流量瞬间爆了。
我们后台的订单查询接口直接炸了,日志里全是慢 SQL。
我打开监控一看,数据库 CPU 飙到了 98%,一查慢查询日志,全是 SELECT 语句。那一刻我终于懂了,读多写少的业务,如果不读写分离,迟早要“炸”。
于是,那天晚上,我彻底入了“读写分离”的坑。
什么是读写分离?简单说:
把数据库的“读”和“写”操作,分到不同的数据库服务器上。
写操作(INSERT、UPDATE、DELETE) → 主库(Master)
读操作(SELECT) → 从库(Slave)
主库负责写入数据,并同步给从库;从库负责查询,分担主库压力。好处立竿见影:
读请求负载均衡,性能提升;
主库压力下降,写操作更稳;
可横向扩展,支持更多并发。
一句话:让主库少“操心”,从库多“干活”。
面试官最爱的追问:那怎么实现读写分离?这题一出来,千万别急着背答案,得先明白:读写分离不是某个框架的专属,而是一种架构思维。
我一般会分成三类解法来说:客户端方案、中间件方案、数据库层方案。
客户端方案(代码层面实现)这种方式简单粗暴:
在代码中通过不同的数据源区分读和写,应用自己决定 SQL 该去哪台库执行。
比如:
写操作用 masterDataSource
读操作用 slaveDataSource
Spring Boot 里常见的写法是使用 AbstractRoutingDataSource,根据线程上下文切换数据源。
优点:
实现灵活,业务可控;
不依赖外部组件,维护简单。
缺点:
开发量大,要在代码里管理数据源;
写错路由容易“读到旧数据”;
适合小型项目或中间件不可用场景。
举个例子:
我们当时用 MyBatis + AOP 拦截器,在方法上加注解 @ReadOnly,框架自动路由到从库。
效果不错,但后来从库延迟问题一出,我们被“坑惨”了……
中间件方案(最常见也最推荐)这是大厂最爱用的一种方式:
通过中间件代理数据库连接,由中间件自动决定读写路由。
常见中间件包括:
(1)MyCat
国产老牌中间件,支持读写分离、分库分表、全局表、分片算法等。
配置简单,直接在 schema.xml 中定义主从库规则即可。
优点:
支持 SQL 解析;
无需改应用代码;
一站式解决分库分表 + 读写分离。
缺点:
性能瓶颈明显;
开发社区活跃度下降;
运维复杂度较高。
(2)ShardingSphere
Apache 顶级开源项目,强烈推荐。支持:
ShardingSphere-JDBC(客户端嵌入式)
ShardingSphere-Proxy(数据库代理层)
只要在配置文件中写好规则,框架自动帮你:
拦截 SQL;
识别读写;
动态路由到主或从库。
优点:
生态完善;
性能优于 MyCat;
Spring Boot 原生支持。
缺点:
初次上手配置复杂;
对 SQL 支持不如原生 MySQL 灵活。
(3)DBProxy / Atlas / ProxySQL
这些是运维同学的心头好,部署在应用和数据库之间,透明代理 SQL 请求。比如:
ProxySQL(MySQL 官方推荐)
Atlas(360 出品)
DBProxy(淘宝早期方案)
优点:
对业务透明;
热更新路由;
支持连接池复用。
缺点:
多一层网络跳转;
调优成本高;
延迟和一致性问题仍需考虑。
数据库层方案(MySQL 原生)别忘了,MySQL 自己也支持主从复制!配置方式主要有:
异步复制(Async):主库写完就返回,不等从库;
半同步复制(Semi-Sync):主库至少等一个从库确认;
全同步复制(Sync):主库等所有从库确认。
一般生产环境用“半同步”模式,兼顾性能与安全。应用只要配置连接地址,让中间件或框架去决定“读去哪、写去哪”即可。
优点:
原生稳定;
成熟可靠;
可与中间件配合使用。
缺点:
自身不做路由;
仍需额外逻辑决定读写分发。
那数据一致性怎么办?这是面试官最爱挖的坑。因为主从复制有延迟,写完主库后马上查从库,可能查不到刚写的数据。怎么办?
几个常见方案:
写后强制读主库:用户刚提交订单,短时间内读操作强制走主库。
延时容忍:允许短时间读到旧数据,比如缓存场景,问题不大。
中间件同步感知:像 ShardingSphere 会自动检测主从延迟,当延迟过大时自动切回主库。
版本号或时间戳控制:读操作带上版本号,确保读取的数据不比主库旧。
总之,要根据业务场景决定:
一致性优先?还是性能优先?
真实案例:我们是怎么踩坑的?那次活动后,我们紧急加了两台从库,用 MyCat 实现读写分离。
起初性能提升明显,但上线第二周问题出现了:
用户投诉“下单成功但查询不到订单”。我们一查,果然——主从延迟高达 3 秒!
后来我们改用 ShardingSphere-Proxy + 半同步复制,一致性和性能都稳了。
那一刻我学到了:
技术没有银弹,合适的才是最好的。
总结:如何在面试中优雅回答?当面试官问“读写分离有哪些解决方案”时,别只说“用 MyCat 啊”。
你可以这样答:
读写分离是将数据库读写请求分散到主从库上,以提升性能和扩展性。
常见实现方式包括:
代码层方案:通过多数据源或 AOP 路由;
中间件方案:如 MyCat、ShardingSphere、ProxySQL;
数据库层方案:依托 MySQL 主从复制。
实践中要注意主从延迟和一致性问题,可用半同步复制或读主兜底策略。
说完这段,面试官一般都会点头,然后来一句:
“不错,思路清晰。”
END其实,读写分离看似简单,背后隐藏的是架构设计的哲学——
性能、可扩展、一致性,永远是一场“取舍的艺术”。
别急着追求“最炫”的技术栈,先搞懂“为什么要用它”。当你理解了背后的原理,再复杂的中间件,也不过是实现思路的另一种表达。
小结一句话:
“读写分离不是为了炫技,而是为了让系统活得更久。”