ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

如何在 MySQL 中实现读写分离?

如何在 MySQL 中实现读写分离? 考点分析是否真正理解读写分离与主从复制的关系而不是只会背概念是否清楚读写分离的常见实现方式以及不同方案在业务中的取舍是否意识到主从延迟带来的数据一致性问题并具备解决方案是否具备使用 Java 生态组件落地读写分离的实际编码能力是否能进一步回答高可用切换、强制主库读、与分库分表结合等追问。一、标准回答读写分离简单说就是把数据库的读操作和写操作分开分别路由到不同的数据库节点主库负责写从库负责读。它的实现前提是 MySQL 主从复制主库上的数据变更通过 binlog 同步到从库从库保持一份可读的数据副本。应用层或中间件根据 SQL 类型自动选择主库还是从库。它的作用主要有三点降低主库压力把大量查询流量分散到多个从库提升系统吞吐和并发能力提升可用性主库发生故障时从库可以承担读请求甚至切换为主库支撑规模化扩展读多写少的典型场景可以通过增加从库水平扩展读能力。它的特点也很明显实现成本相对低但会引入数据延迟和路由复杂度需要业务上容忍一定时限的主从延迟。二、核心原理读写分离底层的核心是 MySQL 主从复制。主从复制的流程可以概括为主库将所有写操作记录到二进制日志 binlog从库的 I/O 线程连接主库把主库的 binlog 复制到本地写入 relay log 中继日志从库的 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 groupIdorg.apache.shardingsphere/groupId artifactIdshardingsphere-jdbc-core/artifactId version5.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 ListMapString, 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); } }执行流程解释应用启动后ShardingSphere 根据配置文件初始化主库 master 和从库 slave1、slave2 三个数据源当执行SELECT查询时规则引擎识别为读操作从负载均衡器选择的从库中获取连接当执行INSERT、UPDATE、DELETE时路由到 write-data-source 指定的 master如果业务方法标注了Transactional且事务内有写操作ShardingSphere 会强制走主库避免读旧数据。注意事项主从延迟场景写后立即读的接口应强制走主库或者使用半同步复制降低延迟事务一致性同一事务内不要混合主从数据源否则可能出现事务提交前读到未同步数据从库可用性需要配置从库健康检查和自动摘除避免从库故障导致查询全部失败SQL 类型识别ShardingSphere 对复杂 SQL、存储过程等解析可能有限需提前评估兼容性。五、扩展延伸主流方案对比方案优势劣势适用场景应用层手动路由实现简单、可控性强代码侵入大、维护成本高小型项目或临时需求ShardingSphere-JDBC轻量、灵活、支持分库分表依赖应用配置版本升级需关注Java 技术栈主流选择MyCat/ProxySQL 代理对应用透明、支持多语言引入额外组件、运维复杂度高多语言、大规模集中管控读写分离的优缺点优点提升读性能、分散压力、便于横向扩展读节点、与主从高可用天然结合。缺点存在主从延迟数据一致性需要业务容忍增加路由和运维复杂度写扩展仍然受限。实际开发注意事项生产环境建议开启从库只读模式防止误写从库监控主从复制状态设置延迟告警阈值关键业务写后必读场景使用HintManager或注解强制走主库如果进一步需要分库分表优先选择同时支持读写分离和数据分片的中间件避免重复建设。六、面试追问追问一主从延迟导致写后读不到如何解决回答思路先说明主从延迟的原因再给出业务层和架构层两类解决手段。标准答案主从延迟主要来自网络传输、从库重放速度和从库负载。解决方式包括写后立即读的接口强制走主库使用半同步复制减少数据丢失风险但无法完全消除延迟在从库读前判断同步位点未追平则改为读主库业务上通过缓存或延迟容忍设计规避强一致读。追问二主库宕机后如何切换读写分离还能正常工作吗回答思路从高可用组件、数据补偿和路由切换三个层面回答。标准答案主库宕机后需要依靠 MHA、Orchestrator 等工具把某个从库提升为主库并修改读写分离的路由配置。切换过程中会出现短暂不可写或数据不一致风险因此需要提前配置半同步复制降低切换时的数据丢失切换后原主库恢复时作为从库重新接入避免数据冲突读写分离中间件监听切换事件自动更新写库地址和读库列表。追问三读写分离和分库分表可以一起用吗回答思路先肯定可以再说明组合方式及注意点。标准答案可以一起用。分库分表解决单库单表数据量过大的问题而读写分离解决读多写少的性能问题。ShardingSphere 等中间件可以同时配置分片规则和读写分离规则写请求先按分片键路由到对应主库读请求再分发到该主库下的从库。组合使用时要注意事务一致性、分片键设计以及管理复杂度建议先做读写分离再逐步引入分库分表。
返回列表