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

30 阅读6分钟



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

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

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

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

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

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

小结一句话:

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

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!