MySQL分区与分表实战指南

一、分区类型详解

1. RANGE分区

RANGE分区是最常用的分区类型,根据列值范围将数据分配到不同分区。

CREATE TABLE sales (
    id INT NOT NULL,
    sale_date DATE NOT NULL,
    amount DECIMAL(10,2)
PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p2020 VALUES LESS THAN (2021),
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION pmax VALUES LESS THAN MAXVALUE
);

实践建议

  • 适合时间序列数据,如日志、交易记录
  • 定期增加新分区,删除旧分区(ALTER TABLE ... REORGANIZE PARTITION
  • 使用MAXVALUE分区作为兜底

2. LIST分区

LIST分区基于离散的值列表进行分区。

CREATE TABLE users (
    id INT NOT NULL,
    region_id INT NOT NULL,
    name VARCHAR(100)
PARTITION BY LIST (region_id) (
    PARTITION p_east VALUES IN (1, 3, 5),
    PARTITION p_west VALUES IN (2, 4, 6),
    PARTITION p_other VALUES IN (DEFAULT)
);

实践建议

  • 适合有明确分类标准的数据
  • 分区列值相对稳定,不频繁变化
  • 考虑使用DEFAULT分区处理异常值

3. HASH分区

HASH分区通过哈希函数均匀分布数据。

CREATE TABLE products (
    id INT NOT NULL,
    name VARCHAR(100),
    category_id INT
) PARTITION BY HASH(category_id)
PARTITIONS 4;

实践建议

  • 适合没有明显分区键但需要分散I/O的场景
  • 分区数通常设为2的幂次方
  • 避免频繁更新的列作为哈希键

4. KEY分区

类似HASH分区,但使用MySQL内置的哈希函数。

CREATE TABLE logs (
    id INT NOT NULL AUTO_INCREMENT,
    created_at TIMESTAMP,
    content TEXT,
    PRIMARY KEY (id)
) PARTITION BY KEY()
PARTITIONS 8;

实践建议

  • 适合没有自然分区键的表
  • 主键或唯一键自动作为分区键
  • 比HASH分区更简单但灵活性较低

5. 复合分区(Subpartitioning)

在分区基础上再进行子分区。

CREATE TABLE sales_archive (
    id INT,
    sale_date DATE,
    region_id INT
) PARTITION BY RANGE (YEAR(sale_date))
SUBPARTITION BY HASH(region_id) (
    PARTITION p2020 VALUES LESS THAN (2021) (
        SUBPARTITION s0,
        SUBPARTITION s1
    ),
    PARTITION p2021 VALUES LESS THAN (2022) (
        SUBPARTITION s2,
        SUBPARTITION s3
    )
);

实践建议

  • 适合需要两级分区的场景
  • 会增加管理复杂度,谨慎使用
  • 确保子分区数量合理,避免过多

二、分表策略

1. 水平分表(按行拆分)

图1

实现方式

  • 按ID范围:user_1user_n
  • 按哈希取模:user_[user_id % 10]
  • 按时间范围:orders_2022, orders_2023

实践建议

  • 选择稳定的分片键,避免后期数据迁移
  • 考虑热点数据问题,避免某个分片过载
  • 预先规划扩容方案

2. 垂直分表(按列拆分)

-- 原始表
CREATE TABLE user (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100),
    profile_json TEXT,
    login_stats JSON,
    created_at TIMESTAMP
);

-- 拆分后
CREATE TABLE user_basic (
    id INT PRIMARY KEY,
    username VARCHAR(50),
    password VARCHAR(100),
    created_at TIMESTAMP
);

CREATE TABLE user_profile (
    user_id INT PRIMARY KEY,
    profile_json TEXT,
    FOREIGN KEY (user_id) REFERENCES user_basic(id)
);

CREATE TABLE user_stats (
    user_id INT PRIMARY KEY,
    login_stats JSON,
    FOREIGN KEY (user_id) REFERENCES user_basic(id)
);

实践建议

  • 将频繁访问的列与不频繁访问的列分开
  • 将大字段(BLOB/TEXT/JSON)单独存放
  • 确保关联查询的性能

3. 分库分表中间件对比

特性ShardingSphereMyCatVitess
协议支持多协议MySQLMySQL
分片策略灵活中等中等
分布式事务支持有限支持支持
生态系统丰富一般云原生
运维复杂度中等较高较高

ShardingSphere示例配置

spring:
  shardingsphere:
    datasource:
      names: ds0,ds1
    sharding:
      tables:
        t_order:
          actual-data-nodes: ds$->{0..1}.t_order_$->{0..15}
          table-strategy:
            inline:
              sharding-column: order_id
              algorithm-expression: t_order_$->{order_id % 16}
          database-strategy:
            inline:
              sharding-column: user_id
              algorithm-expression: ds$->{user_id % 2}

实践建议

  • 评估业务需求选择合适中间件
  • 分片键选择要考虑数据分布均匀性
  • 预先考虑跨分片查询和事务问题
  • 做好数据迁移和扩容规划

三、分区与分表的选择

1. 分区适用场景

  • 单表数据量大但不想分表
  • 有明显的分区键(如时间)
  • 需要定期删除历史数据
  • 查询通常只涉及部分分区

2. 分表适用场景

  • 单表数据量超过单机容量
  • 需要更高的并发性能
  • 需要分布式部署
  • 不同分表可以有不同的存储策略

3. 性能对比

barChart
    title 查询性能对比
    x-axis 方案
    y-axis QPS
    bar 单表: 1000
    bar 分区表: 3500
    bar 分表: 8000

实践建议

  1. 首先考虑SQL优化和索引优化
  2. 数据量在千万级可先尝试分区
  3. 数据量过亿或需要分布式部署考虑分表
  4. 监控系统性能,及时调整策略

四、常见问题解决方案

1. 全局唯一ID生成

  • 雪花算法(Snowflake)
  • UUID(注意索引效率问题)
  • 数据库序列(如Redis INCR)

2. 跨分片查询

  • 使用中间件聚合
  • 应用层并行查询后合并
  • 避免不带分片键的条件查询

3. 分布式事务

  • 最终一致性方案
  • TCC(Try-Confirm-Cancel)模式
  • 使用Seata等分布式事务框架

4. 数据迁移与扩容

  • 双写过渡方案
  • 停机窗口迁移
  • 考虑使用阿里云DTS等工具

五、监控与维护

-- 查看分区使用情况
SELECT partition_name, table_rows 
FROM information_schema.partitions 
WHERE table_name = 'sales';

-- 查看分片均衡情况
SELECT shard, COUNT(*) 
FROM user_shards
GROUP BY shard;

实践建议

  • 定期检查各分区/分片数据量
  • 监控热点分片
  • 建立自动化均衡机制
  • 做好备份策略(分片越多备份越复杂)

通过合理使用分区和分表技术,可以有效解决MySQL单表数据量过大导致的性能问题。选择方案时需要综合考虑业务特点、增长预期和运维成本。

添加新评论