高可用与扩展:主从复制、读写分离与分库分表

各位架构师、数据库的"领航员",大家好!今天我们来聊聊数据库架构的"终极进化论"。

  • 当你的业务从"小作坊"变成"大工厂",单台数据库就像一辆满载的拖拉机,拉不动了。这时候,你需要的不是给拖拉机换个更大的轮胎(加内存、加CPU),而是把它变成一列高铁。

这就是我们今天要讲的主题:高可用与扩展------从主从复制到分库分表,看MySQL如何从"单兵作战"进化为"集团军"。

第一站:主从复制------老板与秘书的"猫鼠游戏"

底层解剖:Binlog的"三生三世"

主从复制的本质,就是日志的传输与回放

  • 老板(Master) :负责处理写操作,并把每一次修改记录在**Binlog(二进制日志)**里。
  • 秘书(Slave):负责从老板那里拷贝日志,并在自己家里重演一遍。

底层流程(三步走):

  1. Dump线程(老板的秘书):Master 为每个 Slave 启动一个 Dump 线程,专门负责发送 Binlog。
  2. I/O线程(Slave的搬运工) :Slave 连接 Master,请求日志,收到后写入本地的中继日志(Relay Log)
  3. SQL线程(Slave的执行者):读取 Relay Log,重放 SQL 操作,让 Slave 的数据与 Master 保持一致。
延迟的痛:为什么秘书总是抄得慢?

主从延迟是架构师最头疼的问题。

  • 原因
    • 单线程回放:在 MySQL 5.7 之前,Slave 的 SQL 线程是单线程的。Master 多线程写入,Slave 单线程回放,必然积压。
    • 大事务:Master 上执行一个删除 100 万行数据的操作只需几秒,Slave 回放时可能要几分钟。
  • 解决方案
    • MySQL 5.7+ 并行复制 :基于 LOGICAL_CLOCK,允许不同表或无冲突的事务并行回放。
    • 半同步复制:Master 写完日志后,必须等至少一个 Slave 收到并写入 Relay Log 才返回成功。虽然牺牲了写性能,但保证了数据不丢。

第二站:读写分离------中间件的"交通指挥"

当读请求远大于写请求时,我们需要把读流量分摊到从库。这时候,中间件(如 ShardingSphere、MyCat、ProxySQL)就登场了。

中间件的工作原理

它就像一个智能路由器,挡在应用和数据库之间。

  • SQL解析 :中间件拦截 SQL,判断是读还是写。
    • SELECT → 路由到 Slave。
    • INSERT/UPDATE/DELETE → 路由到 Master。
  • 负载均衡:如果有多个 Slave,中间件会根据策略(轮询、权重)分配读请求。
致命Bug:主从延迟导致的数据不一致

场景

  1. 用户在 Master 下单(写)。
  2. 用户立即刷新订单列表(读)。
  3. 请求被路由到 Slave,但 Slave 还没同步刚才的订单。
  4. 用户大喊:"我的订单呢?!"

架构师解法

  • 强制读主库:对于核心业务(如订单详情、支付状态),在写操作后的短时间内(如 1 秒),强制走主库。
  • 延迟双删:在更新数据时,先删缓存,再更新数据库,延时(如 500ms)后再删一次缓存。

第三站:分库分表------图书馆的"大迁徙"

当单表数据突破 500w-1000w,索引树太高,磁盘 I/O 太慢,这时候必须进行分库分表

垂直拆分:把"胖子"拆成"瘦子"
  • 垂直分库:按业务拆分。用户库、订单库、商品库分开部署。
  • 垂直分表 :把大字段(如 textblob)拆分到扩展表,主表只留核心字段。
  • 底层收益:减少单页数据量,提高缓存命中率。
水平拆分:把"长队"拆成"多窗口"

这是真正的"核武器"。将一张表的数据分散到多个库/表中。

分片策略(Sharding Strategy):

  • 取模(Hash)user_id % 4。数据分布均匀,但扩容难(需要数据迁移)。
  • 范围(Range) :按时间或 ID 范围。扩容容易,但容易产生数据热点(最新数据都在一个表)。
  • 一致性Hash:解决扩容迁移问题的进阶算法。
分布式 ID:雪花算法(Snowflake)

分库分表后,自增 ID 不能用了(不同表 ID 会重复)。
Snowflake 原理

生成一个 64 位的 Long 型 ID:

  • 1 位:符号位(固定为 0)。
  • 41 位:时间戳(毫秒级,可用 69 年)。
  • 10 位:机器 ID(区分不同节点)。
  • 12 位:序列号(同一毫秒内的自增序号)。

优点:全局唯一、趋势递增、高性能。

跨库 Join 的痛

分库后,JOIN 操作失效了。
解决方案

  • 字段冗余:在订单表里冗余用户名字段,避免 Join 用户表。
  • 应用层组装:先查订单,再批量查用户,在代码里组装(两次查询)。
  • 绑定表:将关联紧密的表(如订单主表和订单详情表)按相同规则分片,保证它们在同一个库,可以直接 Join。

总结

分库分表是架构演进的"核武器",威力大但副作用也大(运维复杂、跨库查询难)。不到万不得已(单表 500w-1000w+),不要轻易使用。

最后,送上金句

"分库分表是架构演进的'核武器',威力大但副作用也大(运维复杂、跨库查询难)。不到万不得已(单表 500w-1000w+),不要轻易使用。"

相关推荐
蒸汽求职15 小时前
破局“无效互面”:跨国大厂视角的工业级 Mock Interview 价值解析
缓存·面试·职场和发展·金融·notion
Cosolar16 小时前
Agent Skills 深度解析:AI 编码代理的工程化生产级工作流引擎
人工智能·面试·开源
shark222222217 小时前
Spring 的三种注入方式?
java·数据库·spring
hERS EOUS17 小时前
Spring Boot + Spring AI快速体验
人工智能·spring boot·spring
JAVA学习通17 小时前
LangChain4j 与 Spring AI 的技术选型深度对比:2026 年 Java AI 工程化实践指南
java·人工智能·spring
环流_18 小时前
多线程1(面试题--常见的线程创建方式)
java·开发语言·面试
掘金安东尼18 小时前
本周前端与 AI 技术情报|前端下一步 #462
前端·javascript·面试
yaodong51818 小时前
Spring 中使用Mybatis,超详细
spring·tomcat·mybatis
splage18 小时前
Spring Cloud Data Flow 简介
后端·spring·spring cloud
zuowei288920 小时前
spring实例化对象的几种方式(使用XML配置文件)
xml·java·spring