Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL分区、分表、分库

MySQL分区、分表、分库从原理到生产最佳实践记录

作者:找不到、了

MySQL分库分表是为了解决大规模数据的存储和查询问题,当一个应用的数据量逐渐增大,单个数据库可能无法满足存储和查询的需求,这时就需要进行分库分表,这篇文章主要介绍了MySQL分区、分表、分库从原理到生产最佳实践的相关资料,需要的朋友可以参考下

前言

单机 MySQL 的瓶颈如下:

关于分库、分表和读写分离的架构图如下所示;

1、分库分表分区

1.1、联系

技术物理位置逻辑视图是否跨实例
分区(Partitioning)同一表、同一库、同一 MySQL 实例仍是一个表否
分表(Table Sharding)多个表(如 user_0, user_1),同一库应用需知道多个表否
分库(Database Sharding)多个数据库(如 db_0, db_1),可能跨 MySQL 实例应用需路由到不同库是

 核心思想:

1.2、对比

能力分区分表分库
是否跨实例❌❌✅
应用是否感知❌(透明)✅(需拼表名)✅(需路由)
突破单机瓶颈❌❌(仍在单库)✅
支持跨分片查询✅(自动裁剪)❌❌(需中间件模拟)
扩容难度低高极高
典型工具MySQL 原生应用层ShardingSphere, Vitess

2、分区(Partitioning)

2.1、介绍

MySQL 原生支持将一个大表的数据物理拆分到多个“分区”中,但逻辑上仍是一个表,数据库内置能力。

开发人员在数据操作时仍然是对这个整体大表进行操作,之后由数据库底层内部去寻找对应的分区进行操作,这样在数据操作时可以只对特定分区操作以提高效率,存储时也可以将不同分区的物理文件分开存放。

2.2、核心原理

2.3、常见分区类型

1.RANGE 分区(按范围)

如下所示:

-- 按年份分区
CREATE TABLE sales (
  id BIGINT,
  sale_date DATE,
  amount DECIMAL(10,2)
) PARTITION BY RANGE (YEAR(sale_date)) (
  PARTITION p2022 VALUES LESS THAN (2023),
  PARTITION p2023 VALUES LESS THAN (2024),
  PARTITION p2024 VALUES LESS THAN (2025),
  PARTITION p_future VALUES LESS THAN MAXVALUE
);

-- 查询自动裁剪
EXPLAIN SELECT * FROM sales WHERE sale_date = '2023-06-01';
-- 只扫描 p2023 分区

(2) LIST 分区(按枚举值)

如下所示:

-- 按地区分区
CREATE TABLE users (
  id BIGINT,
  region VARCHAR(10)
) PARTITION BY LIST COLUMNS(region) (
  PARTITION p_north VALUES IN ('BJ', 'TJ'),
  PARTITION p_south VALUES IN ('GZ', 'SZ'),
  PARTITION p_west VALUES IN ('CD', 'XA')
);

(3) HASH 分区(均匀分布)

如下所示:

-- 按 user_id 均匀分布到 4 个分区
CREATE TABLE orders (
  id BIGINT,
  user_id BIGINT
) PARTITION BY HASH(user_id) PARTITIONS 4;

(4) KEY 分区(类似 HASH,支持多列)

如下所示:

PARTITION BY KEY(user_id, order_type) PARTITIONS 8;

2.4、分区管理命令

-- 添加新分区
ALTER TABLE sales ADD PARTITION (
  PARTITION p2025 VALUES LESS THAN (2026)
);

-- 删除旧分区(秒级!)
ALTER TABLE sales DROP PARTITION p2022;

-- 重建分区(整理碎片)
ALTER TABLE sales REBUILD PARTITION p2023;

✅ 优点

❌ 缺点

📌 适用场景

3、分表(Table Sharding)

3.1、介绍

将一张大表拆成多张结构相同的表,应用层拆分。

如下所示:

3.2、使用原因

为什么需要分表?

如:

如何路由?

通常用 分片键(Shard Key) + 取模/哈希 决定数据存哪张表。

如下所示:

示例:按 user_id 分 4 张表

// Java 伪代码
public String getTableName(Long userId) {
    int tableIndex = (int) (userId % 4);
    return "user_" + tableIndex;
}

// 查询用户
String sql = "SELECT * FROM " + getTableName(userId) + " WHERE id = ?";

3.3、分片策略设计

1.分片键选择

字段优点缺点
user_id用户数据聚集热点用户问题
order_id均匀分布无法按用户查订单
tenant_id多租户隔离租户数据不均

最佳实践:选高频查询字段作为分片键

2.分片算法

// 取模(简单但扩容难)
int tableIndex = userId % tableCount;

// 一致性哈希(扩容友好)
HashFunction hash = Hashing.murmur3_32();
int bucket = hash.hashLong(userId).asInt() & Integer.MAX_VALUE;
int tableIndex = bucket % tableCount;

3.4、MyBatis + 分表

步骤 1:定义分表路由

@Component
public class TableShardingRouter {
    private static final int TABLE_COUNT = 4;
    
    public String getTableName(String baseName, Long shardKey) {
        int index = (int) (shardKey % TABLE_COUNT);
        return baseName + "_" + index;
    }
}

步骤 2:MyBatis 动态表名

<!-- UserMapper.xml -->
<select id="selectById" resultType="User">
  SELECT * FROM ${tableName} WHERE id = #{id}
</select>

步骤 3:Service 层调用

@Service
public class UserService {
    @Autowired
    private TableShardingRouter router;
    
    @Autowired
    private UserMapper userMapper;
    
    public User getUser(Long userId) {
        String tableName = router.getTableName("user", userId);
        return userMapper.selectById(tableName, userId);
    }
}

优点

缺点

适用场景

4、分库(Database Sharding)

4.1、分库原因

为什么需要分库?

4.2、分库分表组合模式

模式 1:库内分表(推荐)

模式 2:仅分库

📊 计算公式:

总分片数 = 库数量 × 表数量;

分片ID = hash(shard_key) % 总分片数;
库名 = 分片ID / 表数量;

表名 = 分片ID % 表数量

4.3、架构

如下所示:

是什么?

将数据分散到多个数据库实例(可能在不同机器),真正的分布式。

如:

通常分库 + 分表一起用,如:

架构图

Application
     │
     ├── Router (ShardingSphere / MyCat / 自研)
     │      │
     │      ├── db_0 (10.0.0.1)
     │      │     ├── user_0
     │      │     └── user_1
     │      │
     │      └── db_1 (10.0.0.2)
     │            ├── user_0
     │            └── user_1

优点

缺点

适用场景

5、最佳实践

1.不要过早分库分表→ 先优化 SQL、加索引、读写分离、缓存

2.分片键至关重要→ 选高频查询字段(如 user_id),避免热点

3.避免跨分片操作→ 设计时让关联数据落在同分片(如订单 & 订单项都按 user_id 分)

4.全局 ID 必须趋势递增→ 避免 UUID 导致 InnoDB 性能下降;

总结

结论:

分区是“治标”,分库分表是“治本”。90% 的系统,用好 分区 + 读写分离 + 缓存 就足够了;
只有真正遇到单机瓶颈,才考虑分库分表。

参考文章:

1、【MySQL】之分区、分库、分表

到此这篇关于MySQL分区、分表、分库从原理到生产最佳实践记录的文章就介绍到这了,更多相关MySQL分区、分表、分库内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文