想象你管理的不是数据库,而是一座藏书过亿的图书馆仓库。书被随意堆在一起,读者报出书名,管理员只能从头翻到尾,找一本花两小时,整个仓库怨声载道。后来馆长给仓库砌起隔断:按年代分房间、按类别分房间,或者用盖章机把新书均匀摊到几间房里。读者再来找书,直奔对应房间,翻找时间从两小时变成两分钟。数据库分区表干的就是这件事:当一张表的数据量从百万冲到千万、上亿,全表扫描就像翻遍整个仓库;分区表把大表按规则拆成多个物理分区,逻辑上还是同一张表,查询却能精准落在少数分区上。这篇文章就用这座“图书馆仓库”当主线,带你搞懂范围分区、列表分区、哈希分区怎么选,分区键怎么设计,裁剪如何生效,以及日常怎么维护。读完你也能为海量数据表设计出又快又省心的分区方案。

岗位关联

分区表是海量数据下数据库优化的核心武器,掌握它能让查询提速数倍到数十倍,是后端开发工程师与 DBA 进阶的必备技能。

目录
  1. 可视化演示
  2. 分区表概述
  3. 范围分区
  4. 列表分区
  5. 哈希分区
  6. 分区键设计
  7. 分区裁剪
  8. 维护策略
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

三种分区策略的数据分布对比 范围分区按连续值区间划分数据,适合时间序列等有序数据的高效范围查询 列表分区按离散枚举值分组,适合按地区或类别等维度组织数据的场景 哈希分区通过哈希函数均匀分散数据,有效缓解数据倾斜并支持并行查询 hash(key) mod N = partition_id 选择与查询模式匹配的分区策略是分区表设计的关键

分区表概述

市图书馆的仓库里堆着几千万本书,管理员找一本《数据库之美》要从入口翻到尽头,两小时过去书没找到,读者先崩溃了。仓库越大,翻找越慢。一张数据表从百万行涨到千万、上亿行时,数据库的处境一模一样——全表扫描的成本随行数线性膨胀,索引层变深、内存命中率下降,一次简单查询都能拖到秒级。

单表数据量过大会带来三重难题:查询慢、管理难、备份难。想删百万行历史数据要逐条 DELETE,想加索引要重建整个表,备份文件膨胀到难以处理。面对这些问题,数据库工程师发现解决办法不是堆更强的机器,而是像馆长那样给仓库“砌隔断”——这就是分区表诞生的背景。

分区表在逻辑上仍是一张表,应用端的 SQL 几乎无需改动;物理上,数据按分区规则被拆成若干独立存储的片段,每个片段叫作一个分区(Partition)。常见规则有三种:范围分区、列表分区、哈希分区。查询时如果条件命中了分区规则,优化器就只访问相关分区,扫描量成倍缩减。

仓库里所有书架加起来还是那几千万本书,但隔断让“找书”从线性翻找变成“先定位房间、再找书架”。分区的价值同理:它不是减少数据,而是减少“无意义翻看的数据”。带着这个直觉,我们去看三种最常见的隔断方式。

Dpruned=Dtotalnparts\displaystyle D_{\text{pruned}} = \frac{D_{\text{total}}}{n_{\text{parts}}}
裁剪后需要扫描的数据量,约等于数据总量除以分区个数。分区越多,单次查询扫描越少,但管理成本也随之上升。
  1. 数据总量$D_{\text{total}}$ 代表整张表参与扫描的总行数,对应仓库里所有书的数量。
    Dtotal\displaystyle D_{\text{total}}
  2. 分区个数$n_{\text{parts}}$ 表示把表切成了几个物理分区,对应仓库被隔成了几间房。
    nparts\displaystyle n_{\text{parts}}
  3. 裁剪后扫描量$D_{\text{pruned}}$ 表示只查一个分区时需要扫描的行数,约为总量的 $1/n_{\text{parts}}$。
    Dpruned=Dtotalnparts\displaystyle D_{\text{pruned}} = \frac{D_{\text{total}}}{n_{\text{parts}}}

范围分区

馆长按出版年代把仓库分成一间间房:「1980 年代房」「1990 年代房」「2000 年代房」「2010 年代房」。新书一到,看出版年份放对应房间;读者要找 2015 年的书,只进 2010 年代房。这就是范围分区最朴素的样子——按一个连续的区间来决定每一行的归属。

在数据库里,范围分区(RANGE Partitioning)按照分区键的值落在哪个区间,决定行进入哪个分区。它是最常用的一种分区方式,特别适合订单、日志、流水这类天然带时间属性的数据:记录按时间持续生成,区间划分清晰,查询也常带时间范围条件,天然能对上裁剪。

以 MySQL 为例,范围分区用 VALUES LESS THAN 定义边界,每个分区是一个“左闭右开”的区间,例如 p2021 存放 2020 年(含)到 2021 年(不含)的数据。设计时要为边界留好余量,并配上最后一个 MAXVALUE 兜底分区,否则超出边界的新数据会因无处安放而直接报错。

年代房间的灵感来自“给数据排序”。数据是历史的堆积品,时间是最自然的隔断;当读者问“最近十年有什么新书”,你立刻知道该去哪个房间,而不是把建馆以来的书全翻一遍。范围分区正是把这种直觉变成了数据库规则。

Pi={r∣vi−1≤key(r)<vi},i=1,2,…,n\displaystyle \text{P}_{i} = \{ r \mid v_{i-1} \leq \text{key}(r) < v_{i} \}, \quad i = 1, 2, \ldots, n
第 i 个分区是“键值在区间 $[v_{i-1}, v_i)$ 内的所有记录”的集合。左闭右开保证相邻分区不重不漏。
  1. 第 i 个分区$\text{P}_{i}$ 表示第 i 个范围分区,相当于第 i 个年代房间,总共有 $n$ 个。
    Pi (i=1,2,…,n)\displaystyle \text{P}_{i} \ (i = 1, 2, \ldots, n)
  2. 成员条件集合中的记录 $r$ 必须满足分区键 $\text{key}(r)$ 的值落入某个区间,才会被放进这个分区。
    {r∣vi−1≤key(r)<vi}\displaystyle \{ r \mid v_{i-1} \leq \text{key}(r) < v_{i} \}
  3. 左闭右开下界可以取到、上界取不到,保证相邻两个分区之间没有任何一行会同时属于两边。
    vi−1≤key(r)<vi\displaystyle v_{i-1} \leq \text{key}(r) < v_{i}

列表分区

另一家书店不问年代,而是按类型隔房间:科幻房、历史房、艺术房、文学房。每本书贴上唯一的类型标签,进门就放进对应房间。读者只关心科幻小说,就直奔科幻房——这是列表分区的日常。

列表分区(LIST Partitioning)按分区键的离散取值来划分配置:每个分区绑定一个取值集合,例如 VALUES IN ('华东','华北')。它适合地区、业务类型、状态码这类枚举值场景。比如全国订单按「华东、华北、华南、西部」四个大区划分,一张大表就变成四间“地区房”。

列表分区要求不同分区的取值集合互不重叠,且每个取值必须落入某个分区;设计时通常加一个 DEFAULT 兜底分区,承接将来可能出现的未知值。相比范围分区,列表分区更适合“按类别查”而非“按区间查”——例如按城市查门店、按渠道查订单转化,分区数量等于枚举类别数,直观可控。

类别房间的标签是有限的几类,新书到时先看类别再看房间,管理效率极高。但前提是类别体系得稳定——一旦标签体系频繁重构,房间隔断也要跟着大动干戈。所以列表分区适合那些“枚举值固定、分类清楚”的业务。

Lj={v1,v2,…,vk},Li∩Lj=∅ (i≠j)\displaystyle L_j = \{ v_1, v_2, \ldots, v_k \}, \quad L_i \cap L_j = \varnothing \ (i \neq j)
第 j 个列表分区绑定一个离散取值集合,不同分区的集合互不相交,所有取值合起来覆盖全部数据。
  1. 取值集合$L_j$ 是第 j 个列表分区绑定的所有合法取值,相当于房间门牌上贴的类别清单。
    Lj={v1,v2,…,vk}\displaystyle L_j = \{ v_1, v_2, \ldots, v_k \}
  2. 互不重叠任意两个分区的取值集合交集为空,一行数据只可能属于一个分区,不会左右横跳。
    Li∩Lj=∅\displaystyle L_i \cap L_j = \varnothing
  3. 全集覆盖所有取值集合的并集等于全部合法取值的集合 $U$,没被任何分区接住的取值要交给兜底分区。
    L1∪L2∪⋯∪Lm=U\displaystyle L_1 \cup L_2 \cup \cdots \cup L_m = U

哈希分区

新书仓库没有明显的时间或类别特征,书号连续,新书上架时总是扎堆堆在入口。馆长想出一个办法:在门口放一台“盖章机”,按书号对 8 取余,余数为 0 进 0 号房,余数为 1 进 1 号房……每本书进门就被均匀分流到 8 个房间,再热门的批次也不会挤在同一间屋里。

这就是哈希分区(HASH Partitioning):先对分区键做哈希运算,再对分区总数取模,得到分区编号。它不关心数据的自然分布,用计算换均匀。适合那些分区键没有明显区间或类别规律、但写入量巨大且要求打散的场景,例如按用户 ID 分布的用户行为表。

哈希分区的分区数在创建时就固定下来,后续扩缩容通常需要重新映射全部数据,代价较高。它的弱点是无法高效做范围裁剪:查询指定某个用户 ID 时能直接定位分区,但查询“某个时间区间”就必须遍历所有分区,因为时间根本不是分布依据。

盖章机让“随机入库”变成“均匀入库”。哈希分区用同样的思路,把热点写入均匀摊到每个房间。但房间数量像固定摊位,将来想增加摊位,要把所有书重新盖一遍章——所以选它之前,必须对未来规模心里有数。

p=hash(key) mod n\displaystyle p = \text{hash}(\text{key}) \bmod n
分区编号等于分区键的哈希值对分区数取模。计算决定去向,天然均匀。
  1. 分区键$\text{key}$ 是参与哈希计算的列,例如 user_id、order_id,对应书上盖的号码。
    key\displaystyle \text{key}
  2. 哈希函数$\text{hash}(\cdot)$ 把任意长度的键值映射成一个整数,相当于盖章机吐出的随机数。
    hash(key)\displaystyle \text{hash}(\text{key})
  3. 取模对分区总数 $n$ 取余数,把整数映射到 $[0, n-1]$ 的范围内。
    hash(key) mod n\displaystyle \text{hash}(\text{key}) \bmod n
  4. 分区编号$p$ 是余数,决定这行进哪个分区,取值范围从 0 号到 $n-1$ 号。
    p∈{0,1,…,n−1}\displaystyle p \in \{ 0, 1, \ldots, n-1 \}

分区键设计

不管房间怎么隔,最怕的是隔错了。馆长发现读者大多按「书名」找书,仓库却按「封皮颜色」分区——结果每本书都得翻遍所有房间才能找到。分区键就是决定“隔法”的那个核心标尺,选错了一切白搭。

分区键设计的第一原则:选择高频查询中稳定出现的过滤列。对订单表而言,绝大多数查询带时间条件,于是选 order_time;对用户维度查询多的表,则要围绕用户标识设计。其次,要避免单次查询跨越多个分区——一次查询涉及的分区越多,裁剪收益越小,性能越接近全表扫描。

如果查询条件里根本没有分区键,优化器就无法裁剪,只能全分区扫描,分区形同虚设;如果分区键基数太低(比如性别),数据会严重倾斜到少数几个分区。另外,分区键的值原则上应不可修改——一旦某行的键值变更,这一行就要在物理上“搬家”,从一个分区挪到另一个分区,代价极高。

好的分类要问读者怎么找书。分区键不是“数据库需要什么”,而是“业务怎么查”。把查询模式当作读者动线,分区键才能让每间房都精准承接对应的需求——这也是分区设计里最考验经验的一步。

Costcross=∑i∈S∣Pi∣,S⊆{1,2,…,n}\displaystyle \text{Cost}_{\text{cross}} = \sum_{i \in S} |\text{P}_i|, \quad S \subseteq \{ 1, 2, \ldots, n \}
跨分区扫描的总代价,等于本次涉及的所有分区行数之和。涉及的分区越多,代价越接近全表扫描。
  1. 涉及分区集合$S$ 是一次查询需要访问的分区编号集合,理想情况下它只包含 1 个元素。
    S⊆{1,2,…,n}\displaystyle S \subseteq \{ 1, 2, \ldots, n \}
  2. 分区行数$|\text{P}_i|$ 表示第 i 个分区里的数据行数,相当于每个房间的藏书量。
    ∣Pi∣\displaystyle |\text{P}_i|
  3. 总代价累加把涉及分区的行数全部加起来,就是这次查询要付出的扫描代价。
    Costcross=∑i∈S∣Pi∣\displaystyle \text{Cost}_{\text{cross}} = \sum_{i \in S} |\text{P}_i|

分区裁剪

现在仓库入口有位值班管理员,他手上有张“房间地图”。读者一报需求,他立刻判断“这本书在 2010 年代房”,然后刷卡开门带你直奔目标;其他房间连门都不用开。这个动作,就是数据库里的分区裁剪。

分区裁剪(Partition Pruning)是查询优化器的一项优化:执行 SQL 时,优化器根据 WHERE 条件里的分区键,提前把不可能命中的分区排除掉,只扫描真正有数据的分区。裁剪发生在执行计划生成阶段,对应用完全透明——你写的是普通 SQL,优化器偷偷帮你做了“只进一间房”的判断。

判断裁剪是否生效,最直接的方法是看执行计划:MySQL 的 EXPLAIN 会列出实际访问的分区列表。如果结果显示全部分区都在列表中,说明裁剪失败,要检查 WHERE 条件是否包含分区键、分区键是否被函数包裹导致无法推导。另外注意:裁剪解决“进哪个房间”,房间内的精确定位仍要靠普通索引,两者是叠加关系。

值班管理员的价值不在跑得快,而在“少跑”。裁剪让查询从“翻遍整个仓库”变成“只进一间房”,扫描成本从 O(总行数) 降到 O(单分区行数)——这正是分区表存在的全部理由:不是让硬盘更快,而是让数据库少干活。

Scanpruned≈Scanfulln\displaystyle \text{Scan}_{\text{pruned}} \approx \frac{\text{Scan}_{\text{full}}}{n}
裁剪后扫描的行数约等于全表扫描行数除以分区数。分区数越多、条件越准,省得越多。
  1. 全表扫描行数$\text{Scan}_{\text{full}}$ 是不做分区时将要读取的总行数,即翻遍整个仓库的工作量。
    Scanfull\displaystyle \text{Scan}_{\text{full}}
  2. 除以分区数$\frac{\text{Scan}_{\text{full}}}{n}$ 是理想情况下只扫一个分区的工作量。
    Scanfulln\displaystyle \frac{\text{Scan}_{\text{full}}}{n}
  3. 近似相等约等于号表示真实值还受数据倾斜、索引回表等因素影响,但量级上已大幅缩小。
    Scanpruned≈Scanfulln\displaystyle \text{Scan}_{\text{pruned}} \approx \frac{\text{Scan}_{\text{full}}}{n}

维护策略

仓库不是建好就一劳永逸:每年要为新书加一间「2026 年代房」,过时的旧书要整体搬去冷库,读者变少的相邻房间要合并。分区维护,就是这套日常运营。

对范围分区来说,时间驱动的数据管理是最大红利:新增分区只需 ADD PARTITION 加一个未来边界;清理三年前的历史数据,直接 DROP PARTITION 即可,不必逐行 DELETE。相比普通表几百万行的 DELETE 要写日志、要锁表,分区删除是近乎瞬间的元数据操作。

维护还包括 REORGANIZE 拆分过大的分区(例如把全年分区拆成季度分区)、合并过小的分区、监控各分区大小与增长趋势,以及定期检查执行计划确认查询仍在走裁剪。冷热数据借此分层:热分区放高性能存储,冷分区归档或直接删除,让仓库永远只装该装的书。

会运营的馆长才能让仓库永远干净高效:及时加新房、果断搬旧书、定期盘点房间大小。分区表不是一次性设计,而是与数据增长节奏同步的生命周期工程——设计定生死,维护定长久。

Saved=∑i=1krows(Pi)×row_size\displaystyle \text{Saved} = \sum_{i=1}^{k} \text{rows}(\text{P}_i) \times \text{row\_size}
删除或归档 k 个分区时,释放的空间等于这些分区行数之和乘以平均行宽。
  1. 分区行数$\text{rows}(\text{P}_i)$ 是被清理的第 i 个分区中的数据行数,相当于冷库房间的藏书量。
    rows(Pi)\displaystyle \text{rows}(\text{P}_i)
  2. 平均行宽$\text{row\_size}$ 是一行数据平均占用的存储字节数,对应每本书的平均厚度。
    row_size\displaystyle \text{row\_size}
  3. 总释放空间把每个被清理分区的行数与行宽相乘再求和,就是这次维护释放的总存储空间。
    Saved=∑i=1krows(Pi)×row_size\displaystyle \text{Saved} = \sum_{i=1}^{k} \text{rows}(\text{P}_i) \times \text{row\_size}

AI 应用场景

场景 1:电商订单表的按月范围分区

阶段:数据建模与存储优化

解决的问题:电商订单表突破亿行后,全表扫描延迟数秒,历史备份耗时,清理旧数据要逐行 DELETE。按订单时间做范围分区后,查询“近 30 天订单”自动裁剪到 1-2 个分区;清理 3 年前数据直接 DROP PARTITION。

具体案例:某电商平台订单表 2 亿行,按月分区后按时间范围查询从平均 3.2 秒降到 120 毫秒;月度整理历史数据从每小时删十万行,变成秒级删除整个分区。

场景 2:用户行为日志的哈希分区

阶段:数据写入与负载均衡优化

解决的问题:用户行为日志没有天然时间聚类,若按范围分区会导致部分时段写入扎堆。按 user_id 哈希分区,把写入均匀打散到 16 个分区,消除单分区写热点,同时支持按用户 ID 快速定位轨迹。

具体案例:某 App 每日写入 5 亿条行为日志,哈希分区后写入吞吐提升 3 倍,按 user_id 查询用户行为轨迹从全表扫描降为单分区检索。

场景 3:多租户 SaaS 的列表分区

阶段:数据隔离与合规管理

解决的问题:多租户系统按行业或区域分区,既隔离了不同业务线的数据,又让区域维度查询只扫本区数据;配合合规要求按区域定期清理,操作清晰可控。

具体案例:某 SaaS 平台按租户行业做列表分区,行业报表查询裁剪到本行业分区,季度合规导出与清除时间从按天计缩短到按分钟计。

Python 演示

python67 行
import randomimport time  def make_orders(n):    """生成 n 条模拟订单,年份分布在 2018-2025"""    orders = []    for _ in range(n):        orders.append({'id': random.randint(1, 10**8),                       'year': random.randint(2018, 2025),                       'amount': random.randint(1, 1000)})    return orders  def partition_key(year, boundaries):    """分区键函数:返回 year 所属分区编号,按边界 [b1, b2, ...) 切分"""    for i, b in enumerate(boundaries):        if year < b:            return i    return len(boundaries)  def build_partitions(orders, boundaries):    """构建分区:把每行数据放入对应分区的列表"""    parts = {}    for r in orders:        p = partition_key(r['year'], boundaries)        parts.setdefault(p, []).append(r)    return parts  def query_full_scan(orders, lo, hi):    """全表扫描:遍历所有行,统计年份落在 [lo, hi) 的订单"""    return sum(1 for r in orders if lo <= r['year'] < hi)  def query_with_pruning(parts, boundaries, lo, hi):    """分区裁剪:只扫描与 [lo, hi) 有交集的分区"""    count = 0    for p, rows in parts.items():        p_lo = boundaries[p - 1] if p > 0 else 0        p_hi = boundaries[p] if p < len(boundaries) else 3000        if p_hi <= lo or p_lo >= hi:            continue  # 区间无交集,整个分区跳过        count += sum(1 for r in rows if lo <= r['year'] < hi)    return count  def main():    n = 200000    boundaries = [2020, 2022, 2024]    print('生成订单数据...')    orders = make_orders(n)    parts = build_partitions(orders, boundaries)     for lo, hi in [(2018, 2020), (2021, 2023), (2024, 2026)]:        t0 = time.perf_counter()        r1 = query_full_scan(orders, lo, hi)        t1 = time.perf_counter()        r2 = query_with_pruning(parts, boundaries, lo, hi)        t2 = time.perf_counter()        print('区间 [%d, %d): 全表=%d条(%.4fs) 裁剪=%d条(%.4fs)'              % (lo, hi, r1, t1 - t0, r2, t2 - t1))  if __name__ == '__main__':    main()
  1. def partition_key(year, boundaries):分区键函数:按边界列表返回年份所属分区编号,对应仓库的“房间查找规则”。
  2. parts.setdefault(p, []).append(r)把每一行订单放入它所属的分区列表,这就是物理分区的构建过程。
  3. if p_hi <= lo or p_lo >= hi: continue裁剪核心:目标区间与某分区的区间没有交集时,整个分区直接跳过,一行业务数据都不看。
  4. query_full_scan(orders, lo, hi)返回遍历所有行做条件判断的结果,作为性能基准。
  5. parts = build_partitions(orders, boundaries)只构建一次分区结构,之后无数次查询都能复用,对应分区表“建表一次、查询万次”的收益模型。

常见误区

误区大表做了分区后,查询自然就快了,分区可以完全替代索引,不用再建任何索引。

正解分区解决的是“进哪个房间”的问题,索引解决的是“房间内哪本书”的问题。以图书馆为例,分区让你直达 2010 年代房,但房间里还有几十万本书,没有索引导航照样得一本本翻。实际做法是二者叠加:分区裁剪缩小范围,分区内索引精确定位。

为何常见:很多人把“扫描量减少”误当成“查找完全免费”,忽略了分区内部仍然需要数据定位机制。

误区只要能分区就行,任选一列做分区键效果差不多,反正数据都被拆开了。

正解分区键必须与高频查询的 WHERE 条件对应。如果读者总是按书名找书,你却按封皮颜色分区,每本书依然要翻遍所有房间。分区键选错,优化器无法裁剪,查询退化为全分区扫描,性能反而更差。

为何常见:只关注“分没分区”的形式,忽略了分区价值来自“查询条件能否命中分区规则”这个本质。

误区既然裁剪是按分区减少扫描量,那把表分成几千个分区,查询不就会快到飞起吗?

正解分区过多会带来元数据膨胀、文件句柄开销、优化器评估成本上升等问题,很多数据库对分区数量也有上限。以图书馆类比,把书分到几千个微型房间,管理员光查“该进哪间”就要花半天。分区数应该与数据量和查询模式匹配,通常几十到几百个足够。

为何常见:把公式 $D_{\text{total}}/n$ 做了线性外推,忽略了 n 增长带来的管理成本这一侧。

误区哈希分区跟范围分区一样,数据多了随时 ADD PARTITION 加一个新区就行。

正解哈希分区用 hash(key) mod n 决定归属,n 是固定分区数。增加一个分区后,取模基数变了,几乎全部旧数据的映射都要重算,等于把所有书重新盖一遍章,代价极大。范围分区加新区只需加边界,而哈希分区扩缩容通常要重建表。

为何常见:把范围分区的“加边界即加区”经验套用到哈希分区,忽略了取模运算对分区总数的强依赖。

误区表查询慢就做分区,分区后所有 SQL 都会变快,不用再分析其他原因。

正解分区只对“条件包含分区键”的查询有效。如果慢查询的 WHERE 里没有分区键,裁剪根本不生效;如果慢查询缺索引、SQL 写法糟糕,分区也救不了。正确姿势是先看执行计划定位瓶颈,再决定分区是否对症。

为何常见:把分区当成万能优化器,忽略了它只是“减少扫描范围”这一种手段,病因没找对,药自然不灵。

面试题

Q1. 分区表的本质是什么?

单选题 · easy

  • A.按规则拆成多个物理存储单元← 正确答案
  • B.将大表复制到多台服务器
  • C.给大表添加更多二级索引
  • D.将表数据进行压缩存储

答案:A。分区把一张表按规则拆成物理上独立的分区片段,逻辑上仍是一张表,就像把大仓库隔成小房间。复制到多服务器是读写分离,加索引是加速定位,压缩是减少存储,都不是分区的定义。

Q2. 日志表按天不断追加数据,最合适的分区方式是?

单选题 · easy

  • A.范围分区← 正确答案
  • B.列表分区
  • C.哈希分区
  • D.随机分区

答案:A。日志天然带时间属性、按时间持续追加,范围分区按时间区间切分最自然,还能支持时间范围查询裁剪。列表分区适合离散枚举,哈希分区适合均匀打散,都没有利用时间的有序性。

Q3. 哈希分区最主要的优势是什么?

单选题 · easy

  • A.支持高效范围查询
  • B.数据均匀避免热点← 正确答案
  • C.任意列都可做键值
  • D.可动态增加分区

答案:B。哈希分区通过取模运算把数据均匀摊到各个分区,避免某些分区写入集中。范围查询恰恰是它的弱项,分区数固定也不方便动态扩容,所以 B 才是它的核心价值。

Q4. 分区键设计的首要原则是什么?

单选题 · mid

  • A.选择唯一索引主键列
  • B.选择高频过滤条件列← 正确答案
  • C.选择存储空间较小的列
  • D.选择数值类型的主键列

答案:B。分区键必须出现在高频查询的 WHERE 条件里,裁剪才能生效。就像仓库要按读者最常用的找书方式分房间,而不是按书号长短。唯一索引列、小空间列、数值主键列如果不被查询条件命中,选了也白选。

Q5. 分区裁剪(Partition Pruning)指的是什么?

单选题 · mid

  • A.自动删除无用的分区
  • B.只扫描相关的目标分区← 正确答案
  • C.压缩分区中的存量数据
  • D.合并体积较小的分区

答案:B。裁剪是优化器在执行计划阶段排除无关分区的行为,查询只进入有数据的分区。删除分区、压缩数据、合并小分区都是维护操作,与裁剪完全是两回事。

Q6. 下列哪些场景适合使用列表分区?(多选)

多选题 · mid

  • A.按地区维度分组数据← 正确答案
  • B.按业务类型枚举切分← 正确答案
  • C.连续日期范围查询日志
  • D.无限增长的流水序号

答案:AB。列表分区针对离散枚举值,地区、业务类型都有有限且稳定的取值集合,天然适合。连续日期适合范围分区;无限增长的流水序号没有类别语义,不适合列表分区。

Q7. 关于范围分区与哈希分区,下列说法正确的是?

单选题 · mid

  • A.哈希分区支持区间裁剪
  • B.范围分区可能产生数据倾斜← 正确答案
  • C.哈希分区可任意增加分区
  • D.范围分区无法删除旧数据

答案:B。范围分区按区间切分,若某时间段数据暴涨,该分区会明显偏大,这就是数据倾斜。哈希分区用取模定位,不支持区间裁剪;扩分区要重算全部映射;范围分区可以用 DROP PARTITION 删除旧数据。B 正确。

Q8. 以下属于分区表维护操作的有?(多选)

多选题 · hard

  • A.新增分区承接未来数据← 正确答案
  • B.删除过期的历史数据分区← 正确答案
  • C.拆分或合并调整现有分区← 正确答案
  • D.修改表的主键列的类型

答案:ABC。分区维护围绕“分区”本身的增删改:ADD PARTITION、DROP PARTITION、REORGANIZE 拆分合并都属于维护。修改主键列类型是表结构变更,与分区维护无关。

Q9. 订单表按 order_time 范围分区,查询 WHERE user_id = 12345 时无法裁剪分区,根本原因是?

单选题 · hard

  • A.分区键未参与查询条件← 正确答案
  • B.user_id 列没有建立索引
  • C.分区总数配置得不够多
  • D.查询返回的结果集太大

答案:A。裁剪只认分区键。WHERE 里只有 user_id,没有 order_time,优化器无法判断该去哪几个分区,只能全部扫描。索引影响分区内的查找速度,分区数多少不改变“无法裁剪”的事实。

Q10. 关于哈希分区的调整与使用,正确的说法有?(多选)

多选题 · extreme

  • A.扩大分区数需要重算旧分布← 正确答案
  • B.扩大分区数可直接复用旧映射
  • C.固定分区数适合均匀写入场景← 正确答案
  • D.哈希分区擅长时间范围裁剪

答案:AC。哈希分区按 hash(key) mod n 路由,n 变化后余数全变,旧数据必须重算分布,所以 B 错 A 对。固定分区数能在创建时把写入均匀摊开,适合打散热点,C 对。哈希分区对时间区间查询无法裁剪,D 错。