基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

MySQL分库分表有哪些常见的策略?

知识点图片

讲解MySQL分库分表策略,区分垂直与水平拆分。核心是四种水平分片方法:范围、哈希、一致性哈希和映射表,并分析了各自的优缺点与应用场景。

我们来详细探讨一下 MySQL 分库分表常见的策略。

分库分表(Sharding)是为了解决单一数据库或单一数据表在数据量、并发量、存储容量等方面遇到的性能瓶颈而采用的一种分布式数据库方案。其核心思想是将数据分散存储到多个库或多个表中,从而提高系统的整体性能和可扩展性。

首先,我们需要理解两个基本概念:

  • 垂直拆分 (Vertical Sharding):基于业务或表结构进行拆分。
  • 水平拆分 (Horizontal Sharding):基于数据行进行拆分。

常见的策略主要围绕 水平拆分 展开,因为它才是解决单表数据量过大问题的核心。


一、垂直拆分 (Vertical Sharding)

垂直拆分比较简单,通常是分库分表的第一步。

  1. 垂直分库

    • 思想:按照业务模块将关联度低的表分散到不同的数据库中。
    • 示例:一个大型电商系统,可以将用户库、商品库、订单库、支付库等拆分开。
    • 优点
      • 业务逻辑清晰,便于维护和管理。
      • 不同业务的压力隔离,避免某个业务的性能问题影响全局。
      • 可以针对不同业务的数据库做针对性的优化。
    • 缺点
      • 无法解决单表数据量过大的问题。
      • 涉及跨库的 JOIN 操作会变得非常复杂,通常需要通过服务层接口调用来解决。
      • 可能引发分布式事务问题。
  2. 垂直分表

    • 思想:将一个包含很多列的“宽表”拆分成多个“窄表”。
    • 示例:将一个包含用户基本信息和用户详情(如长篇介绍)的 user 表,拆分为 user_base(包含 id, username, password 等常用字段)和 user_detail(包含 id, biography, profile_picture_blob 等大字段或低频访问字段)。
    • 优点
      • 提高 I/O 效率,查询常用字段时,不会加载不必要的大字段数据。
      • 可以更好地利用缓存,因为单行数据更小。
    • 缺点
      • 需要通过 JOIN 来获取完整的记录,增加了查询的复杂度。
      • 同样无法解决单表行数过多的问题。

二、水平拆分 (Horizontal Sharding)

水平拆分是分库分表的核心,它通过某种规则将一个表的数据行分散到多个物理结构相同的表中(可能在同一个库,也可能在不同库)。选择合适的 分片键 (Sharding Key)分片算法 (Sharding Algorithm) 至关重要。

以下是几种常见的水平拆分策略:

1. 范围分片 (Range Sharding)

  • 思想:根据分片键的范围来决定数据存储到哪个库/表。
  • 示例
    • 按 ID 范围user_id 在 1 到 100万 的存入 user_0 表,100万到 200万 的存入 user_1 表。
    • 按时间范围:按月份或年份分表,例如 orders_2023_01orders_2023_02
  • 优点
    • 实现简单:分片规则清晰。
    • 便于范围查询:如果查询条件是基于分片键的范围查询(如查询某个时间段的订单),可以快速定位到少数几个表,效率很高。
    • 便于扩容:当数据量增长时,只需增加新的节点(库/表)来存储新的数据范围即可,无需迁移历史数据。
  • 缺点
    • 数据倾斜/热点问题:如果分片键的分布不均匀,容易导致数据集中在某几个表中。例如,按时间分片,最新的数据会全部涌入最新的表,造成“热点表”问题,该表的读写压力会远大于历史表。

2. 哈希分片 (Hash Sharding) / 取模分片

  • 思想:根据分片键的哈希值(或直接取模)来决定数据存储到哪个库/表。
  • 公式shard_index = hash(sharding_key) % N (N 是分库/表的总数)
  • 示例:假设有 4 个库,user_id 为分片键,那么 user_id = 1001 的数据存储在 1001 % 4 = 1 号库。
  • 优点
    • 数据分布均匀:只要哈希算法选择得当,数据可以非常均匀地分散到各个库/表中,有效避免热点问题。
    • 请求负载均衡:由于数据均匀,访问请求也会均匀地落到各个节点上。
  • 缺点
    • 范围查询不友好:范围查询需要路由到所有的库/表,然后将结果合并排序,性能很差。
    • 扩容极不方便:如果分片数量 N 发生变化(例如从 4 个库扩容到 5 个库),取模的基数变了,绝大多数数据都需要重新计算哈希并进行迁移(rehash),成本非常高。

3. 一致性哈希分片 (Consistent Hashing)

  • 思想:为了解决哈希分片扩容难的问题而诞生。它将整个哈希值空间组织成一个虚拟的环(例如 0 ~ 2^32-1)。将每个数据库节点通过哈希算法映射到环上,然后将数据的分片键也哈希到环上,数据顺时针存储到遇到的第一个节点上。
  • 优点
    • 扩容友好:当增加或删除一个节点时,只会影响到该节点在环上的邻近节点,需要迁移的数据量非常小,大大降低了扩容成本。
    • 继承了哈希分片数据分布相对均匀的优点(通过引入虚拟节点可以做得更均匀)。
  • 缺点
    • 实现相对复杂
    • 同样不便于范围查询。
    • 可能存在数据倾斜,需要通过增加“虚拟节点”来缓解。

4. 映射表/查询表分片 (Lookup Table / Mapping Table)

  • 思想:创建一个独立的映射表,专门用来存储分片键与数据库节点的映射关系。
  • 示例
    • 创建一个 user_mapping 表,包含 user_idshard_id 两列。
    • 当需要查询某个 user_id 的数据时,先去 user_mapping 表查出它对应的 shard_id,然后再去对应的分库/分表中查询。
  • 优点
    • 高度灵活:分片规则可以任意改变,只需要修改映射表即可,业务数据无需迁移。
    • 可以很好地解决不同分片策略的缺点,例如可以将某些热点用户的数据单独迁移到一个高性能的库中。
  • 缺点
    • 增加了一次查询开销:每次查询都需要先访问映射表,会带来一定的性能损耗。
    • 映射表自身可能成为瓶颈:映射表需要高可用和高性能,如果它挂了,整个数据访问链路都会中断。通常需要对映射表本身做高可用方案。

总结与选择

策略 优点 缺点 适用场景
范围分片 实现简单,范围查询友好,扩容方便 容易产生数据倾斜和热点问题 时间序列数据(日志、账单)、需要按范围查询的业务(如用户ID分段)
哈希分片 数据分布均匀,请求负载均衡 范围查询性能差,扩容极不方便(几乎是灾难性的) 用户信息、订单等,大多数查询都是基于唯一ID的点查场景
一致性哈希 扩容非常方便,数据分布较均匀 实现复杂,范围查询不友好 对扩容伸缩性要求高的场景,如云存储服务
映射表分片 规则灵活,可以自由迁移数据 增加一次查询开销,映射表自身可能成为瓶颈 业务复杂多变,需要灵活控制数据分布的场景

实际应用中的考量

在实际项目中,分库分表还需要解决一系列的衍生问题:

  1. 全局唯一 ID:分库分表后,数据库的自增主键无法保证全局唯一。需要引入独立的ID生成方案,如 Snowflake 算法、UUID、基于 Redis/Zookeeper 的序列生成器等。
  2. 跨库 JOIN:应极力避免。通常通过字段冗余、数据同步或者在服务层进行多次查询并组装数据来解决。
  3. 分布式事务:跨库的写操作会产生分布式事务问题。解决方案包括两阶段提交(2PC)、三阶段提交(3PC)、TCC(Try-Confirm-Cancel)、Saga 模式、本地消息表等,但都会增加系统的复杂性。
  4. 排序、分页与聚合:跨多个库/表进行排序、分页和聚合函数(如 COUNT, SUM)操作会非常困难。通常需要在服务层获取所有分片的数据,然后在内存中进行合并、排序和计算。
  5. 中间件:为了降低应用层的复杂性,通常会引入分库分表中间件,如 Sharding-SphereMyCAT 等。它们对应用层透明,可以自动处理 SQL 路由、结果合并等工作。

选择哪种策略,最终取决于你的 业务场景、数据特点和未来的扩展需求。在做决定前,一定要充分分析业务的查询模式。

00:00
00:00