全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-15 10 分钟 ✍️ juanwangdev

MySQL 分库分表策略

当单表数据量超过千万级,性能瓶颈显现,分库分表是核心解决方案。

拆分策略概述

垂直拆分

垂直分库:按业务拆分数据库

SQL
拆分前:
┌─────────────────────────┐
│      单库 all_db        │
│  ├─ 用户表 users        │
│  ├─ 订单表 orders       │
│  └─ 商品表 products     │
└─────────────────────────┘

拆分后:
┌──────────┐  ┌──────────┐  ┌──────────┐
│ user_db  │  │order_db  │  │product_db│
│  users   │  │  orders  │  │ products │
└──────────┘  └──────────┘  └──────────┘

垂直分表:按列拆分,冷热分离

SQL
-- 拆分前:大宽表
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100),
    nickname VARCHAR(50),
    avatar VARCHAR(200),
    signature TEXT,
    last_login DATETIME,
    login_count INT
);

-- 拆分后:基础信息表 + 扩展信息表
CREATE TABLE users_base (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100),
    nickname VARCHAR(50)
);

CREATE TABLE users_ext (
    user_id BIGINT PRIMARY KEY,
    avatar VARCHAR(200),
    signature TEXT,
    last_login DATETIME,
    login_count INT
);

水平拆分

水平分库:同一表数据分散到多个库

SQL
orders 表按 user_id 分3个库:

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  order_db_0 │  │  order_db_1 │  │  order_db_2 │
│  orders     │  │  orders     │  │  orders     │
│  user_id%3=0│  │  user_id%3=1│  │  user_id%3=2│
└─────────────┘  └─────────────┘  └─────────────┘

水平分表:同一库内拆分多表

SQL
-- 按 order_id 分表
orders_0  -- order_id % 4 = 0
orders_1  -- order_id % 4 = 1
orders_2  -- order_id % 4 = 2
orders_3  -- order_id % 4 = 3

分片键选择

选键原则

原则说明
高频查询选择最常用查询条件字段
数据均匀避免数据倾斜
业务关联关联查询可命中同库
无修改分片键不应变更

常见分片键

SQL
-- 用户中心:user_id
SELECT * FROM orders WHERE user_id = 1001;

-- 订单系统:order_id 或 user_id
SELECT * FROM orders WHERE order_id = '202405150001';

-- 日志系统:create_time
SELECT * FROM logs WHERE create_time BETWEEN '2024-05-01' AND '2024-05-15';

分片路由算法

1. Hash 取模

SQL
-- 分库:user_id % 库数量
库序号 = user_id % db_count

-- 分表:order_id % 表数量
表序号 = order_id % table_count

-- 示例:user_id = 1001, 分3库
 = 1001 % 3 = 2  order_db_2

优点:数据分布均匀 缺点:扩容需数据迁移

2. 范围分片(Range)

SQL
-- 按时间范围
orders_202401  -- 2024年1月
orders_202402  -- 2024年2月
orders_202403  -- 2024年3月

-- 按 ID 范围
orders_0_1m    -- id 1-100万
orders_1m_2m   -- id 100万-200万

优点:扩容简单,按时间查询高效 缺点:数据可能不均匀

3. 一致性哈希

YAML
         节点A
           |
节点D ---- o ---- 节点B
           |
         节点C

数据分布:按哈希值顺时针定位到节点

优点:扩容只影响相邻节点 缺点:实现复杂

4. 基因法(关联查询优化)

SQL
-- user_id 编码到 order_id
order_id = 时间戳 + user_id后4位 + 序列号

-- 通过 order_id 反推 user_id
user_id = (order_id >> 16) & 0xFFFF

-- 查询时可直接定位分库

主键策略

分布式主键生成

方案实现优点缺点
UUIDUUID()简单无序、索引效率低
雪花算法Snowflake有序、分布式依赖时钟
号段模式数据库批量获取高效需要额外表
自增步长auto_increment_increment简单扩容麻烦
SQL
-- 自增步长方案
-- 库1
SET GLOBAL auto_increment_increment = 3;
SET GLOBAL auto_increment_offset = 1;
-- 生成:1, 4, 7, 10...

-- 库2
SET GLOBAL auto_increment_increment = 3;
SET GLOBAL auto_increment_offset = 2;
-- 生成:2, 5, 8, 11...

-- 库3
SET GLOBAL auto_increment_increment = 3;
SET GLOBAL auto_increment_offset = 3;
-- 生成:3, 6, 9, 12...

中间件选型

中间件类型特点
ShardingSphere-JDBC客户端轻量、无侵入、主流
MyCat服务端功能全、需部署
ShardingSphere-Proxy服务端兼容 MySQL 协议
Vitess服务端YouTube 出品、大规模

ShardingSphere-JDBC 配置示例

SQL
spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/order_db_0
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/order_db_1

    rules:
      sharding:
        tables:
          orders:
            actual-data-nodes: ds$->{0..1}.orders_$->{0..3}
            table-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: orders-inline
            key-generate-strategy:
              column: order_id
              key-generator-name: snowflake
        sharding-algorithms:
          orders-inline:
            type: INLINE
            props:
              algorithm-expression: orders_$->{user_id % 4}

常见问题与解决方案

跨库 Join

问题:分库后无法直接 JOIN

解决方案

text
-- 方案1:应用层组装
-- 分别查询,内存合并
List<Order> orders = orderMapper.selectByUserId(userId);
List<User> users = userMapper.selectByIds(orderUserIds);

-- 方案2:冗余字段
-- 订单表冗余用户名
CREATE TABLE orders (
    order_id BIGINT,
    user_id BIGINT,
    user_name VARCHAR(50),  -- 冗余
    ...
);

-- 方案3:绑定表(同库)
-- user_id 相同的用户表和订单表路由到同一库

分页查询

text
-- 问题:跨库分页结果不准
-- 方案1:各库查全量,内存聚合
SELECT * FROM orders WHERE user_id = ? LIMIT 100;

-- 方案2:路由到单库
SELECT * FROM orders WHERE user_id = ? LIMIT 10 OFFSET 0;

全局表

text
-- 配置表、字典表在所有库同步
CREATE TABLE config (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    value VARCHAR(500)
);

-- 各库保持数据一致,避免跨库查询

要点总结

  1. 垂直拆分:按业务或列拆分,降低单表宽度
  2. 水平拆分:按数据行拆分,降低单表数据量
  3. 分片键选择:高频查询、数据均匀、业务关联
  4. 路由算法:Hash 取模(均匀)、Range(易扩容)、一致性哈希
  5. 主键策略:雪花算法为主流分布式 ID 方案
  6. 中间件选型:ShardingSphere-JDBC 客户端方案最常用

📝 发现内容有误?点击此处直接编辑

← 上一篇 MySQL 分布式事务与XA协议
下一篇 → MySQL 高可用方案MHA与Orchestrator
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库