如何在 MySQL 中实现读写分离?
2026/9/1 6:59:16 网站建设 项目流程

考点分析:

  • 是否真正理解读写分离与主从复制的关系,而不是只会背概念;
  • 是否清楚读写分离的常见实现方式,以及不同方案在业务中的取舍;
  • 是否意识到主从延迟带来的数据一致性问题,并具备解决方案;
  • 是否具备使用 Java 生态组件落地读写分离的实际编码能力;
  • 是否能进一步回答高可用切换、强制主库读、与分库分表结合等追问。

一、标准回答

读写分离,简单说就是把数据库的读操作和写操作分开,分别路由到不同的数据库节点:主库负责写,从库负责读。它的实现前提是 MySQL 主从复制:主库上的数据变更通过 binlog 同步到从库,从库保持一份可读的数据副本。应用层或中间件根据 SQL 类型自动选择主库还是从库。

它的作用主要有三点:

  • 降低主库压力:把大量查询流量分散到多个从库,提升系统吞吐和并发能力;
  • 提升可用性:主库发生故障时,从库可以承担读请求,甚至切换为主库;
  • 支撑规模化扩展:读多写少的典型场景可以通过增加从库水平扩展读能力。

它的特点也很明显:实现成本相对低,但会引入数据延迟和路由复杂度,需要业务上容忍一定时限的主从延迟。

二、核心原理

读写分离底层的核心是 MySQL 主从复制。主从复制的流程可以概括为:

  1. 主库将所有写操作记录到二进制日志 binlog;
  2. 从库的 I/O 线程连接主库,把主库的 binlog 复制到本地,写入 relay log 中继日志;
  3. 从库的 SQL 线程读取 relay log,重放其中的事件,完成数据同步。

binlog 有三种常用格式:STATEMENT记录 SQL 语句、ROW记录行级变更、MIXED混合模式。其中 ROW 模式在大多数读写分离和同步场景下更可靠,因为它记录的是实际数据变化,不会因为函数、触发器等导致主从不一致。

主从复制按同步策略又分为:

  • 异步复制:主库提交事务后无需等待从库确认,性能高但可能丢数据;
  • 半同步复制:主库等待至少一个从库收到 binlog 后再返回客户端,能减少数据丢失风险;
  • 全同步复制:所有从库确认后才返回,性能较差,实际使用较少。

读写分离正是在这套复制机制之上,通过路由层把读请求发送给从库。因此,只要从库没有追平主库,就可能出现“写完立刻读不到”的主从延迟问题,这是面试中核心的追问点。

三、应用场景

  • 日常开发场景:电商商品详情、文章内容页、用户中心信息展示等读多写少的业务,查询接口可以走从库,下单、支付等写入接口走主库。
  • 企业真实场景:互联网公司通常采用一主多从架构,配合 ShardingSphere、MyCat 等中间件,实现透明读写分离;同时还会加监控、告警和主从延迟检测组件。
  • 报表和运营分析:大查询、统计任务独立放在从库或专门的只读实例上,避免拖垮主库。
  • 微服务读模型优化:某些服务只读业务数据,整个服务可以只连接从库,从链路层面隔离主库压力。

四、使用方式

实现读写分离的常见方式有三种:

  • 应用层路由:手动维护多个数据源,根据方法名或注解选择主从数据源。实现简单但侵入业务代码。
  • 中间件层:使用 ShardingSphere-JDBC、MyBatis 插件等,在 Java 应用内完成路由,对业务代码侵入小。
  • 数据库代理层:部署 MyCat、ProxySQL、Atlas 等代理,应用连接代理即可,代理负责分发主从请求。

下面以ShardingSphere-JDBC为例,演示 Java 中实现读写分离的落地方式。ShardingSphere-JDBC 以 jar 包形式运行在应用内,通过配置读写分离规则,自动解析 SQL 并路由。

步骤一:引入依赖

<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>shardingsphere-jdbc-core</artifactId> <version>5.4.1</version> </dependency>

步骤二:配置数据源和读写分离规则

application.yml中配置主库和两个从库:

spring: shardingsphere: datasource: names: master, slave1, slave2 master: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.10:3306/shop username: root password: root slave1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.11:3306/shop username: root password: root slave2: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://192.168.1.12:3306/shop username: root password: root rules: readwrite-splitting: >@Service public class OrderQueryService { @Resource private JdbcTemplate jdbcTemplate; public List<Map<String, Object>> listOrdersByUser(long userId) { String sql = "SELECT order_id, amount, status FROM t_order WHERE user_id = ?"; return jdbcTemplate.queryForList(sql, userId); } @Transactional public void createOrder(long userId, BigDecimal amount) { String sql = "INSERT INTO t_order(user_id, amount, status) VALUES (?, ?, ?)"; jdbcTemplate.update(sql, userId, amount, "CREATED"); } }

执行流程解释:

  1. 应用启动后,ShardingSphere 根据配置文件初始化主库 master 和从库 slave1、slave2 三个数据源;
  2. 当执行SELECT查询时,规则引擎识别为读操作,从负载均衡器选择的从库中获取连接;
  3. 当执行INSERTUPDATEDELETE时,路由到 write-data-source 指定的 master;
  4. 如果业务方法标注了@Transactional且事务内有写操作,ShardingSphere 会强制走主库,避免读旧数据。

注意事项:

  • 主从延迟场景:写后立即读的接口应强制走主库,或者使用半同步复制降低延迟;
  • 事务一致性:同一事务内不要混合主从数据源,否则可能出现事务提交前读到未同步数据;
  • 从库可用性:需要配置从库健康检查和自动摘除,避免从库故障导致查询全部失败;
  • SQL 类型识别:ShardingSphere 对复杂 SQL、存储过程等解析可能有限,需提前评估兼容性。

五、扩展延伸

主流方案对比

方案优势劣势适用场景
应用层手动路由实现简单、可控性强代码侵入大、维护成本高小型项目或临时需求
ShardingSphere-JDBC轻量、灵活、支持分库分表依赖应用配置,版本升级需关注Java 技术栈主流选择
MyCat/ProxySQL 代理对应用透明、支持多语言引入额外组件、运维复杂度高多语言、大规模集中管控

读写分离的优缺点

  • 优点:提升读性能、分散压力、便于横向扩展读节点、与主从高可用天然结合。
  • 缺点:存在主从延迟,数据一致性需要业务容忍;增加路由和运维复杂度;写扩展仍然受限。

实际开发注意事项

  • 生产环境建议开启从库只读模式,防止误写从库;
  • 监控主从复制状态,设置延迟告警阈值;
  • 关键业务写后必读场景,使用HintManager或注解强制走主库;
  • 如果进一步需要分库分表,优先选择同时支持读写分离和数据分片的中间件,避免重复建设。

六、面试追问

追问一:主从延迟导致写后读不到,如何解决?

回答思路:先说明主从延迟的原因,再给出业务层和架构层两类解决手段。

标准答案:主从延迟主要来自网络传输、从库重放速度和从库负载。解决方式包括:

  • 写后立即读的接口强制走主库;
  • 使用半同步复制减少数据丢失风险,但无法完全消除延迟;
  • 在从库读前判断同步位点,未追平则改为读主库;
  • 业务上通过缓存或延迟容忍设计规避强一致读。

追问二:主库宕机后如何切换,读写分离还能正常工作吗?

回答思路:从高可用组件、数据补偿和路由切换三个层面回答。

标准答案:主库宕机后需要依靠 MHA、Orchestrator 等工具把某个从库提升为主库,并修改读写分离的路由配置。切换过程中会出现短暂不可写或数据不一致风险,因此需要:

  • 提前配置半同步复制,降低切换时的数据丢失;
  • 切换后原主库恢复时作为从库重新接入,避免数据冲突;
  • 读写分离中间件监听切换事件,自动更新写库地址和读库列表。

追问三:读写分离和分库分表可以一起用吗?

回答思路:先肯定可以,再说明组合方式及注意点。

标准答案:可以一起用。分库分表解决单库单表数据量过大的问题,而读写分离解决读多写少的性能问题。ShardingSphere 等中间件可以同时配置分片规则和读写分离规则,写请求先按分片键路由到对应主库,读请求再分发到该主库下的从库。组合使用时要注意事务一致性、分片键设计以及管理复杂度,建议先做读写分离,再逐步引入分库分表。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询