分区和分表有什么区别?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
-
概念清晰度:面试官想知道你是否能把 “分区” 和 “分表” 这两个看起来很像的概念区分开,很多人面试时把这俩混为一谈,一开口就跪。
-
实践深度:考察你有没有真正做过大表优化,是否知道每种方案的适用场景、成本和坑。
-
架构思维:分区属于单机优化,分表属于分布式架构。面试官想看你能否根据数据量和访问量选择合适的方案。
核心答案
先说结论:分区是 “一张表” 的事,分表是 “多张表” 的事。
| 维度 | 分区(Partition) | 分表(Sharding) |
|---|---|---|
| 逻辑层级 | 逻辑上还是一张表 | 逻辑上也是多张表 |
| 实现层级 | 数据库引擎内部(MySQL DDL) | 应用层 / 中间件 |
| 对应用是否透明 | 透明,业务无感 | 不透明,需路由 |
| 是否跨实例 | 不能,只在单库内 | 可以,跨库跨实例 |
| 实现难度 | 简单,一条 DDL | 复杂,路由/聚合/事务 |
| 突破的瓶颈 | 单表数据量大 | 单库数据量/连接/IO |
| MySQL 原生支持 | ✅ 5.1+ 原生 | ❌ 需要中间件 |
深度解析
一、分区(Partition):MySQL 自带的单表切分
分区是 MySQL 引擎层提供的功能,一张表在逻辑上还是一张表,但物理上把数据拆到了不同的文件里。
上图的 orders 表对应用来说就是一张表,但底层被切成了 4 个分区,每个分区对应独立的数据文件。
分区常见的几种类型:
- RANGE 分区:按范围切,比如按年份、按 ID 范围
- LIST 分区:按离散值切,比如按地区、按类型
- HASH 分区:对某列做 hash 取模
- KEY 分区:MySQL 内置 hash,比 HASH 更通用
举个 RANGE 分区的例子:
CREATE TABLE orders (
id BIGINT,
order_no VARCHAR(32),
create_time DATETIME,
amount DECIMAL(10, 2),
PRIMARY KEY (id, create_time)
)
PARTITION BY RANGE (YEAR(create_time)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p_max VALUES LESS THAN MAXVALUE
);
对应用层来说完全无感,SELECT * FROM orders WHERE create_time > '2022-01-01' 这种 SQL,MySQL 内部会做 “分区裁剪”(Partition Pruning),只扫描 p2022 和 p_max 两个分区,其他分区直接跳过。
二、分表(Sharding):把一张大表拆成多张表
分表是真正的 “拆”,物理上就是多张表。分表又分两种:
- 垂直分表:按字段拆,把宽表拆成多张窄表。比如把订单基础信息和订单详情拆开。
- 水平分表:按行拆,把一张表的数据按某种规则(hash、范围、一致性 hash)分散到多张表里。
分表以后,应用层必须知道该查哪张表。常见两种方式:
- 应用层路由:业务代码自己算,根据
user_id % 4决定查哪张表 - 中间件路由:用 ShardingSphere、MyCat 这类中间件,应用感知不到分表
// 应用层路由示意
int shardIndex = userId % 4;
String table = "orders_" + shardIndex;
jdbcTemplate.query("SELECT * FROM " + table + " WHERE user_id = ?", userId);
三、核心区别
这块我用一张图来对比,更直观:
上图把两种方案的差别画得挺明白:
- 分区:数据分散在多个分区文件,但整个表始终在一个 MySQL 实例里,CPU、内存、连接数都共用一个实例的资源。
- 分表:数据分散到多张表,这些表可以在不同实例、不同机器上,真正打破单库瓶颈。
四、什么时候用分区,什么时候用分表
这块是面试官最爱追的,因为能直接区分你是 “背了概念” 还是 “做过项目”。
分区的适用场景:
- 单表数据量大(比如几千万到一两亿),但还在单库能扛的范围内
- 大部分查询能命中分区键,能享受分区裁剪的红利
- 历史数据冷热明显,比如按时间归档
- 不想动业务代码,只想在 DB 层优化
分表的适用场景:
- 单表数据量太大(几亿甚至几十亿),单库扛不住
- 写入 QPS 高,单实例 IO/CPU 成瓶颈
- 需要突破单机连接数限制
- 业务允许最终一致,能接受分布式事务的复杂度
五、分区的坑
很多教程把分区吹得很好,但实际用的时候坑不少:
- 分区键必须在主键/唯一键里:上面例子
PRIMARY KEY (id, create_time)就是为了把分区键create_time加进去,很多人第一次写就踩坑 - 跨分区查询性能差:如果查询条件不带分区键,MySQL 会扫所有分区,比不分还慢
- 分区数有上限:MySQL 5.6.7 之前最多 1024 个分区,5.6.7 之后可以到 8192 个
- 外键不支持:分区表不能用外键
- 唯一索引必须包含分区键
六、分表的坑
分表的坑更多更深,但都是分布式系统的通病:
- 分布式事务:跨表事务要么用 XA,要么用 Seata 这种柔性事务
- 跨表 JOIN 难:要么冗余字段,要么应用层组装
- 全局唯一 ID:得用雪花算法、号段模式
- 聚合查询复杂:
count、sum、order by limit都要合并多张表 - 扩容迁移:一开始分 4 张表,后续要扩到 8 张,数据要重新分布
面试高频追问
-
追问一:分表后怎么做分布式事务?
一般三种方案:XA(强一致但性能差)、TCC(性能好但开发成本高)、本地消息表 + 最终一致(最常用)。生产环境推荐用 Seata 或者直接走最终一致。
-
追问二:分表键怎么选?
选查询最频繁的字段,比如订单表用
user_id。还要考虑数据均匀性,避免热点。 -
追问三:分表后怎么分页?
最头疼的问题。常见做法:全局视图(性能差)、二次查询法、ES 辅助查询、把分页限制在前 N 页。
-
追问四:分区和分表能一起用吗?
可以。先分表再分区,或者先分区再分表,数据量特别大的场景(比如日志表)经常这么干。
常见面试变体
- “MySQL 的分区表用过吗?有什么坑?”
- “什么时候需要分库分表?”
- “分库分表后怎么做跨库 JOIN?”
- “分表后主键 ID 怎么生成?”
记忆口诀
分区是表面功夫(逻辑还是一张表),分表是真动刀(物理拆开)。
一个判据:数据量大到单表扛不住用分区,大到单库扛不住才用分表。
总结
分区和分表压根不是一回事。分区是 MySQL 引擎层的 “障眼法”,逻辑上一张表,物理上分散到不同文件,对应用透明,适合单表千万级到亿级的优化。分表是架构层的 “真拆分”,物理上就是多张表,跨库跨实例,突破单库瓶颈,但带来路由、事务、JOIN 一堆麻烦。面试时只要把 “一张表 vs 多张表” 和 “数据库层 vs 应用层” 这两个维度讲清楚,基本稳了。
