各位架构师、数据库的"领航员",大家好!今天我们来聊聊数据库架构的"终极进化论"。
- 当你的业务从"小作坊"变成"大工厂",单台数据库就像一辆满载的拖拉机,拉不动了。这时候,你需要的不是给拖拉机换个更大的轮胎(加内存、加CPU),而是把它变成一列高铁。
这就是我们今天要讲的主题:高可用与扩展------从主从复制到分库分表,看MySQL如何从"单兵作战"进化为"集团军"。
第一站:主从复制------老板与秘书的"猫鼠游戏"
底层解剖:Binlog的"三生三世"
主从复制的本质,就是日志的传输与回放。
- 老板(Master) :负责处理写操作,并把每一次修改记录在**Binlog(二进制日志)**里。
- 秘书(Slave):负责从老板那里拷贝日志,并在自己家里重演一遍。
底层流程(三步走):
- Dump线程(老板的秘书):Master 为每个 Slave 启动一个 Dump 线程,专门负责发送 Binlog。
- I/O线程(Slave的搬运工) :Slave 连接 Master,请求日志,收到后写入本地的中继日志(Relay Log)。
- 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 才返回成功。虽然牺牲了写性能,但保证了数据不丢。
- MySQL 5.7+ 并行复制 :基于
第二站:读写分离------中间件的"交通指挥"
当读请求远大于写请求时,我们需要把读流量分摊到从库。这时候,中间件(如 ShardingSphere、MyCat、ProxySQL)就登场了。
中间件的工作原理
它就像一个智能路由器,挡在应用和数据库之间。
- SQL解析 :中间件拦截 SQL,判断是读还是写。
SELECT→ 路由到 Slave。INSERT/UPDATE/DELETE→ 路由到 Master。
- 负载均衡:如果有多个 Slave,中间件会根据策略(轮询、权重)分配读请求。
致命Bug:主从延迟导致的数据不一致
场景:
- 用户在 Master 下单(写)。
- 用户立即刷新订单列表(读)。
- 请求被路由到 Slave,但 Slave 还没同步刚才的订单。
- 用户大喊:"我的订单呢?!"
架构师解法:
- 强制读主库:对于核心业务(如订单详情、支付状态),在写操作后的短时间内(如 1 秒),强制走主库。
- 延迟双删:在更新数据时,先删缓存,再更新数据库,延时(如 500ms)后再删一次缓存。
第三站:分库分表------图书馆的"大迁徙"
当单表数据突破 500w-1000w,索引树太高,磁盘 I/O 太慢,这时候必须进行分库分表。
垂直拆分:把"胖子"拆成"瘦子"
- 垂直分库:按业务拆分。用户库、订单库、商品库分开部署。
- 垂直分表 :把大字段(如
text、blob)拆分到扩展表,主表只留核心字段。 - 底层收益:减少单页数据量,提高缓存命中率。
水平拆分:把"长队"拆成"多窗口"
这是真正的"核武器"。将一张表的数据分散到多个库/表中。
分片策略(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+),不要轻易使用。"