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

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

  • 当你的业务从"小作坊"变成"大工厂",单台数据库就像一辆满载的拖拉机,拉不动了。这时候,你需要的不是给拖拉机换个更大的轮胎(加内存、加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+),不要轻易使用。"

相关推荐
爱丽_2 小时前
缓存一致性:Cache Aside、双删/延迟双删、穿透/击穿/雪崩与 CDC
java·spring·缓存
majingming1232 小时前
接口的嵌入式实现
java·后端·spring
Flittly3 小时前
【SpringAIAlibaba新手村系列】(8)持久化会话与 Redis 内存管理
java·人工智能·spring boot·spring·ai
roman_日积跬步-终至千里3 小时前
【系统架构设计师-综合题(4-2)】软件架构 · 下册(六至十类 · 含解析)
系统架构
Lyyaoo.3 小时前
Spring中Bean的作用域与生命周期
java·后端·spring
星晨雪海3 小时前
缓存更新操作实例
java·spring·缓存
sjmaysee3 小时前
docker离线安装及部署各类中间件(x86系统架构)
docker·中间件·系统架构
空空潍3 小时前
Spring AI 实战系列(六):Tool Calling深度实战,让大模型自动调用你的业务接口
java·人工智能·spring