厦门市网站建设_网站建设公司_搜索功能_seo优化
2025/12/26 2:00:36 网站建设 项目流程

目录

1、分库分表分区

1.1、联系

1.2、对比

2、分区(Partitioning)

2.1、介绍

2.2、核心原理

2.3、常见分区类型

2.4、分区管理命令

3、分表(Table Sharding)

3.1、介绍

3.2、使用原因

3.3、分片策略设计

3.4、MyBatis + 分表

4、分库(Database Sharding)

4.1、分库原因

4.2、分库分表组合模式

4.3、架构

5、最佳实践


前言

单机 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、核心原理

  • 物理存储分离:每个分区是一个独立.ibd文件
  • 逻辑视图统一:应用仍操作一个表名
  • 查询自动裁剪:优化器只扫描相关分区

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;

✅ 优点

  • 对应用透明:SQL 不用改,SELECT * FROM logs WHERE create_time > '2024-01-01'自动只查p2024

  • 提升查询性能:分区裁剪(Partition Pruning)

  • 方便数据管理ALTER TABLE logs DROP PARTITION p2023;快速删除旧数据

❌ 缺点

  • 仍在单机:无法突破单 MySQL 实例的 CPU/内存/磁盘瓶颈

  • 分区数有限:通常建议 < 100 个分区

  • 不支持所有引擎:仅 InnoDB、MyISAM 支持

📌 适用场景

  • 单表过大(> 1000万行),但总数据量未超单机容量

  • 按时间冷热分离(如日志、订单)

  • 需要快速删除历史数据


3、分表(Table Sharding)

3.1、介绍

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

如下所示:

3.2、使用原因

为什么需要分表?

  • InnoDB 单表建议 < 5000万行(B+树深度增加)

  • 表锁/元数据锁竞争(DDL 操作阻塞)

如:

  • user_0, user_1, user_2, user_3

如何路由?

通常用分片键(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); } }

优点

  • 突破单表性能瓶颈(InnoDB 单表建议 < 5000万行)

  • 实现简单,无需中间件

缺点

  • 应用强耦合:每个 SQL 都要拼表名;

  • 无法跨表 JOIN / 聚合:select count(*) from user_* 需查 4 次再 sum;

  • 扩容困难:从 4 表扩到 8 表,需迁移一半数据;

适用场景

  • 单库能扛住,但单表太大

  • 查询基本都带分片键(如 where user_id = ?)


4、分库(Database Sharding)

4.1、分库原因

为什么需要分库?

  • 单机资源耗尽(CPU/内存/IO)

  • 连接数瓶颈(max_connections

  • 多租户数据隔离需求

4.2、分库分表组合模式

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

  • 4 个库 × 每库 16 张表 = 64 分片

  • 优点:减少数据库连接数

模式 2:仅分库

  • 4 个库 × 每库 1 张表

  • 优点:简化表结构

📊计算公式

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

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

表名 = 分片ID % 表数量

4.3、架构

如下所示:

是什么?

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

如:

  • db_0(IP: 10.0.0.1)

  • db_1(IP: 10.0.0.2)

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

  • db_0.user_0, db_0.user_1

  • db_1.user_0, db_1.user_1

架构图

Application

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

优点

  • 水平扩展:突破单机资源限制(CPU/内存/连接数)

  • 高可用:一个库挂了,其他库仍可用

  • 隔离性:租户/业务线数据物理隔离

缺点

  • 复杂度飙升

    • 跨库事务(需 Seata/TCC)

    • 跨库 JOIN(需应用层聚合)

    • 全局 ID(需 Snowflake/Leaf)

  • 运维成本高:备份、监控、扩容都变复杂

适用场景

  • 海量数据(> 1TB)

  • 超高并发(> 1万 QPS)

  • 多租户 SaaS 系统


5、最佳实践

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

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

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

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


总结

结论:

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


参考文章:

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询