众力资讯网

面试官问我“读写分离怎么做”,我一句话拿下!

面试官问:“如果数据库压力太大,你会怎么做?”我说:“分库分表。”他点点头,又问:“那读写分离呢?”我心想,这回该我装



面试官问:“如果数据库压力太大,你会怎么做?”我说:“分库分表。”他点点头,又问:“那读写分离呢?”我心想,这回该我装逼了——于是,开始了一场关于读写分离的灵魂拷问。

故事从一次线上“事故”开始

那是两年前,我还在一家互联网中厂。我们系统的日活不高,平时数据库 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

其实,读写分离看似简单,背后隐藏的是架构设计的哲学——

性能、可扩展、一致性,永远是一场“取舍的艺术”。

别急着追求“最炫”的技术栈,先搞懂“为什么要用它”。当你理解了背后的原理,再复杂的中间件,也不过是实现思路的另一种表达。

小结一句话:

“读写分离不是为了炫技,而是为了让系统活得更久。”