分区和分表有什么区别?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(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+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 概念清晰度:面试官想知道你是否能把 “分区” 和 “分表” 这两个看起来很像的概念区分开,很多人面试时把这俩混为一谈,一开口就跪。

  2. 实践深度:考察你有没有真正做过大表优化,是否知道每种方案的适用场景、成本和坑。

  3. 架构思维:分区属于单机优化,分表属于分布式架构。面试官想看你能否根据数据量和访问量选择合适的方案。

核心答案

先说结论:分区是 “一张表” 的事,分表是 “多张表” 的事

维度 分区(Partition) 分表(Sharding)
逻辑层级 逻辑上还是一张表 逻辑上也是多张表
实现层级 数据库引擎内部(MySQL DDL) 应用层 / 中间件
对应用是否透明 透明,业务无感 不透明,需路由
是否跨实例 不能,只在单库内 可以,跨库跨实例
实现难度 简单,一条 DDL 复杂,路由/聚合/事务
突破的瓶颈 单表数据量大 单库数据量/连接/IO
MySQL 原生支持 ✅ 5.1+ 原生 ❌ 需要中间件

深度解析

一、分区(Partition):MySQL 自带的单表切分

分区是 MySQL 引擎层提供的功能,一张表在逻辑上还是一张表,但物理上把数据拆到了不同的文件里。

MySQL 分区表
MySQL 分区表

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),只扫描 p2022p_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:得用雪花算法、号段模式
  • 聚合查询复杂countsumorder by limit 都要合并多张表
  • 扩容迁移:一开始分 4 张表,后续要扩到 8 张,数据要重新分布

面试高频追问

  1. 追问一:分表后怎么做分布式事务?

    一般三种方案:XA(强一致但性能差)、TCC(性能好但开发成本高)、本地消息表 + 最终一致(最常用)。生产环境推荐用 Seata 或者直接走最终一致。

  2. 追问二:分表键怎么选?

    选查询最频繁的字段,比如订单表用 user_id。还要考虑数据均匀性,避免热点。

  3. 追问三:分表后怎么分页?

    最头疼的问题。常见做法:全局视图(性能差)、二次查询法、ES 辅助查询、把分页限制在前 N 页。

  4. 追问四:分区和分表能一起用吗?

    可以。先分表再分区,或者先分区再分表,数据量特别大的场景(比如日志表)经常这么干。

常见面试变体

  • “MySQL 的分区表用过吗?有什么坑?”
  • “什么时候需要分库分表?”
  • “分库分表后怎么做跨库 JOIN?”
  • “分表后主键 ID 怎么生成?”

记忆口诀

分区是表面功夫(逻辑还是一张表),分表是真动刀(物理拆开)。

一个判据:数据量大到单表扛不住用分区,大到单库扛不住才用分表。

总结

分区和分表压根不是一回事。分区是 MySQL 引擎层的 “障眼法”,逻辑上一张表,物理上分散到不同文件,对应用透明,适合单表千万级到亿级的优化。分表是架构层的 “真拆分”,物理上就是多张表,跨库跨实例,突破单库瓶颈,但带来路由、事务、JOIN 一堆麻烦。面试时只要把 “一张表 vs 多张表” 和 “数据库层 vs 应用层” 这两个维度讲清楚,基本稳了。