ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

advanced-java 高并发系列:MySQL 读写分离实现方案、主从复制原理与主从同步延时问题全解

advanced-java 高并发系列:MySQL 读写分离实现方案、主从复制原理与主从同步延时问题全解 advanced-java 高并发系列MySQL 读写分离实现方案、主从复制原理与主从同步延时问题全解【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/gh_mirrors/ad/advanced-java导读互联网业务普遍是读多写少高并发架构下必须借助 MySQL 主从复制搭建一主多从的读写分离架构用多个从库承接读流量。本篇文章基于 advanced-java 仓库高并发专题系统讲解读写分离的实现方式、binlog 主从复制原理、半同步复制与并行复制两个核心机制并结合真实线上案例给出主从同步延时的排查命令与完整解决方案帮助你在面试与生产实践中都能把这条链路讲透。面试题读写分离这条链路你能答到什么程度在 advanced-java 的高并发专题中围绕读写分离提出了这样一组经典面试题你们有没有做 MySQL 读写分离如何实现 MySQL 的读写分离MySQL 主从复制原理是啥如何解决 MySQL 主从同步的延时问题这组问题层层递进先问有没有做考察的是架构实践经验再问原理是什么考察的是对复制链路底层机制的理解最后问延时怎么解考察的是遇到真实线上事故时的排查与解决能力。本文就沿着这条脉络逐一展开。为什么高并发阶段必须做读写分离先回答为什么。实际上绝大部分互联网公司、网站或 App 的业务流量都是读多写少用户浏览、搜索、列表展示的请求量远大于下单、改资料等写入量。既然读是主要矛盾就不能让所有读请求都压在同一个库上。读写分离的做法非常直接写一个主库主库下面挂多个从库所有写请求只走主库读请求分散到多个从库上从而用横向扩容从库的方式支撑更高的读并发压力。这套思路在 advanced-java 的高并发系统设计中被明确列为扛住高并发的四大手段之一缓存、MQ、读写分离、分库分表大部分时候数据库可能也是读多写少没必要所有请求都集中在一个库上可以搞个主从架构主库写入从库读取搞一个读写分离。读流量太多的时候还可以加更多的从库。同系列中 Redis 主从架构也遵循同样的套路——主从架构 → 读写分离 → 水平扩容支撑读高并发可见读多写少 主从复制 读写分离是缓存与数据库两个层面通用的高并发范式。如何实现 MySQL 的读写分离实现本身并不复杂基于主从复制架构搞一个主库挂多个从库业务代码只写主库主库自动把数据同步到从库。搭建要点可以概括为三步配置主库开启log-bin二进制日志并设置唯一的server-id主库的所有变更都会记录进 binlog配置从库设置唯一的server-id通过CHANGE MASTER TO指定主库地址、复制用户与日志位点然后执行START SLAVE启动复制应用层路由业务侧将写操作路由到主库将读操作路由到从库。其中第 3 步在工程上通常有两种落地方式应用层封装在 DAO 层或中间件层根据 SQL 类型INSERT/UPDATE/DELETE走主库SELECT走从库做路由实现简单可控数据库中间件接入读写分离代理。advanced-java 的分库分表专题中对这类中间件做过梳理例如淘宝 TDDL 属于 client 层方案支持基本的 CRUD 语法和读写分离ShardingSphere 同时提供 client 层Sharding-JDBC与 proxy 层Sharding-Proxy方案支持分库分表、读写分离、分布式 id 生成等能力。MySQL 主从复制原理binlog relay log 双线程接力读写分离的地基是主从复制其核心流程如下对应仓库图片 mysql-master-slave.png主库记录 binlog主库将每一次数据变更写入 binlog 二进制日志从库 IO 线程拉日志从库连接到主库后从库上的 IO 线程把主库的 binlog 日志拷贝到本地写入relay log中继日志从库 SQL 线程重放从库上的 SQL 线程从中继日志读取 binlog 内容在自己本地再执行一遍同样的 SQL从而保证从库数据与主库一致。整个过程可以理解为日志接力主库的变更先进 binlog经过 IO 线程搬运到 relay log再由 SQL 线程落地到从库数据文件。关键点从库同步是串行化的延时不可避免这里有一个非常重要、也最容易被忽视的点从库同步主库数据的过程是串行化的——主库上并行的操作在从库上会串行执行。由于从库从主库拷贝日志 串行执行 SQL这两个特点在高并发场景下从库的数据一定会比主库慢一些是存在延时的。所以经常出现这样的现象刚写入主库的数据可能读不到要过几十毫秒甚至几百毫秒才能读到。更严重的是连带风险如果主库突然宕机而数据恰好还没同步到从库那么这些数据在从库上可能缺失造成数据丢失。两大兜底机制半同步复制与并行复制针对上述两个问题MySQL 提供了两个对应机制半同步复制semi-sync解决主库数据丢失所谓半同步复制semi-sync指的是主库写入 binlog 日志之后强制立即将数据同步到从库从库把日志写入自己本地的 relay log 之后会返回一个 ack 给主库主库接收到至少一个从库的 ack 之后才会认为这次写操作完成。与异步复制的区别在于异步复制下主库写完 binlog 就立刻返回成功从库是否收到、何时重放都不管半同步复制则引入至少一个从库确认的屏障从机制上堵住了主库宕机、数据未同步就丢失的漏洞代价是写路径上多了一次从库确认的往返延迟。并行复制解决主从同步延时所谓并行复制指的是从库开启多个线程并行读取 relay log 中不同库的日志然后并行重放不同库的日志——这是库级别的并行。它针对的正是前面说的串行执行 SQL瓶颈如果多个库之间互不干扰那么把不同库的日志分给不同线程并行重放从库的追平速度就能大幅提升。注意它的粒度是库如果写并发全部集中在同一个库上并行复制就无法发挥效果这一点在后面的延时解决方案里会再次体现。主从同步延时问题精华一次真实的线上事故复盘这一节是整个专题的精华部分源自 advanced-java 作者团队的真实线上经历曾经因为主从同步延时问题导致线上 bug属于小型生产事故。事故场景还原事故的代码逻辑是这样的先插入一条数据再把它查出来然后更新这条数据。生产环境高峰期写并发达到了2000/s此时主从复制延时大概在小几十毫秒线上每天总有那么一些数据期望更新重要的数据状态但在高峰期却没有被更新用户向客服反馈客服再反馈给开发团队。问题链条很清楚插入走主库成功后紧接着的查询被路由到了从库而数据还没同步过去查不到自然就无法执行后面的更新逻辑导致重要状态更新丢失。排查命令show slave status事故排查靠的是 MySQL 命令show slave status查看其中的Seconds_Behind_Master字段可以看到从库复制主库的数据落后了几毫秒ms。该字段就是当前主从延时的直接量化指标也是日常监控主从健康度的核心依据一旦Seconds_Behind_Master持续增大或长期不为 0就说明从库追不上主库的写入速度需要介入处理。四种解决方案与取舍针对主从延迟一般有以下解决方案方案做法适用场景与取舍分库将一个主库拆分为多个主库每个主库的写并发就减少几倍从根上摊薄单主库写压力此时主从延迟可以忽略不计代价是需要引入分库改造开启并行复制打开 MySQL 支持的并行复制多个库并行复制适合写并发分散在多个库的场景如果某个库的写入并发就是特别高例如单库写并发达到 2000/s并行复制还是没意义重写代码写代码的同学要慎重插入数据时立马查询可能查不到从业务逻辑上规避插入即查的时序依赖成本最低、最推荐查询直连主库如果确实存在必须先插入、立马就要查到、随后立即执行操作的强一致需求对该查询设置直连主库不推荐一旦这么搞读写分离的意义就丧失了方案背后的本质思考这四种方案其实对应了三个层面的权衡架构层面分库是从减少单主库写并发入手让复制压力落在可控范围内数据库层面并行复制是从提升从库重放速度入手但受限于库级并行的粒度应用层面重写代码与直连主库是从业务语义入手——要么消除插入即读的强时序依赖要么用牺牲读写分离收益的方式换取强一致。生产环境中的正确姿势通常是组合拳能用分库降低单库写压力的先分库能改的业务逻辑优先改成插入后不立即查询把插入即查这类强一致场景收敛到极少数必须的场景而不是全局直连主库。延伸从架构全局看读写分离的边界把读写分离放进 advanced-java 高并发体系的全局里看它的边界与配套手段非常清晰读多写少才适合读写分离如果业务本身就是写密集主从复制带来的延时会持续放大这时候应该考虑 分库分表 与 MQ 削峰而不是单纯堆从库缓存是第一道防线高并发系统设计中缓存是抗读并发的首选大量读请求应先被缓存吸收读写分离承接的是缓存未命中后的数据库读流量中间件是工程化抓手当读写分离规则散落在多个业务团队时通过数据库中间件统一路由如分库分表专题中提到的 TDDL、ShardingSphere 等比每个应用各自封装更可控主从复制与 Redis 主从同源同理Redis 主从架构展示了同一套主从 异步复制 读写分离 水平扩容范式在缓存层的应用两者对照理解主从复制的通用心法异步复制有延时、节点扩容扛读就一通百通了。总结回到开头的面试题完整的回答路径是实现方案主从复制架构 一主多从写主库、读从库应用层或中间件层做读写路由复制原理主库写 binlog → 从库 IO 线程拉取写入 relay log → 从库 SQL 线程串行重放从库同步串行化决定了延时必然存在两大机制半同步复制至少一个从库 ack 后写操作才算完成解决主库宕机丢数据并行复制库级别多线程并行重放缓解从库同步延时延时排查与解决show slave status看Seconds_Behind_Master用分库、并行复制、重写代码规避插入即查、必要时直连主库不推荐来治理。推荐结合仓库原文与相关专题继续深入MySQL 读写分离原文、高并发系统设计、分库分表方案、Redis 主从架构。【免费下载链接】advanced-java Core Interview Questions Answers For Experienced Java(Backend) Developers | 互联网 Java 工程师进阶知识完全扫盲涵盖高并发、分布式、高可用、微服务、海量数据处理等领域知识项目地址: https://gitcode.com/gh_mirrors/ad/advanced-java创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表