MySQL分区数上限为128个,超限会触发open_files_limit导致启动失败;RANGE分区单区不宜超1500万行,HASH分区数应为16/32/64且不超64;LIST分区单区枚举值不超过32个。

分区数量超过128个会直接触发 open_files_limit 限制
MySQL 5.7 对每个分区都持有一个独立的 .ibd 文件句柄,分区数一多,open_files_limit 很快就耗尽。现象是启动失败或报错 ERROR 23 (HY000): Out of resources when opening file,尤其在 table_open_cache 值偏高时更明显。
实操建议:
- 硬性上限卡死在
128个分区以内——不是“建议”,是避免启动失败的底线 - 若当前已有超限分区表,别急着删数据,先用
ALTER TABLE ... REMOVE PARTITIONING退化为普通表,再按需重建 - 检查实际使用:运行
SELECT COUNT(*) FROM information_schema.PARTITIONS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';确认真实分区数 - SSD 环境下也别盲目堆到 128,NVMe 上压到
96更稳妥,留余量给其他表和系统文件
RANGE 分区单分区超 1500 万行会导致 B+ 树层级膨胀
时间类 RANGE 分区(比如按月/按天)最容易踩这个坑:初期数据少,分区建得细;后期单分区暴涨,查询反而变慢。因为 InnoDB 的 B+ 树页层级随数据量增加,超过 1500 万行后,深度从 3 层涨到 4 层,每次索引查找多一次磁盘 IO。
实操建议:
- OLTP 场景严格控制在
200–500万行/分区;OLAP 可放宽到500–1000万,但绝不可碰2000万红线 - 已有超大分区,优先用
ALTER TABLE ... REORGANIZE PARTITION合并,而不是DROP + ADD——后者会锁全表且重建索引 - 按天分区的表,如果日均写入
20万行,三个月就逼近临界点,这时该切到按周或按月 -
EXPLAIN PARTITIONS必须定期跑,确认查询是否真的只打到目标分区,避免隐式全分区扫描
HASH 分区数不是越多越好,必须是 2 的幂次且 ≤64
HASH 分区靠取模路由,分区数非 2 的幂次会导致数据倾斜。比如设 PARTITIONS 30,实际分布严重不均,某些分区挤占 40% 数据,其他空转。同时,HASH 分区数超过 64 后,哈希冲突概率陡增,INSERT 性能不升反降。
实操建议:
- 固定用
16、32、64这三个值,别试48或56 - 用户 ID 类整型主键,优先选
PARTITIONS 64;若总数据量不到 5000 万,32更省内存 - 别对字符串字段直接 HASH——先用
CRC32()或MURMUR_HASH()转成整型再分区,否则效率极低 - 监控
INFORMATION_SCHEMA.PARTITIONS中的TABLE_ROWS,任一分区偏差超 ±25%,就要重估分区数
LIST 分区枚举值过多会拖慢元数据加载
LIST 分区依赖显式值映射(VALUES IN (1,2,3,...)),当一个分区里塞了上百个 province_id 或 status_code,MySQL 每次打开表都要解析整个枚举列表,导致 table_definition_cache 占用飙升,冷启动变慢。
实操建议:
- 单个 LIST 分区的枚举值别超
32个;超了就拆成多个分区,比如p_east_1和p_east_2 - 避免用动态生成的枚举(如时间戳、UUID 片段),LIST 只适合稳定、有限、业务可归类的维度
- 如果枚举集合会频繁增删,改用 RANGE 或 HASH + 查找表(lookup table)替代 LIST
- 用
SHOW CREATE TABLE检查 DDL,确认没有类似VALUES IN (1,2,3,...,256)这种长列表直写
SHOW VARIABLES LIKE '%open_files%'; 和 SHOW STATUS LIKE 'Open_tables';,再决定动不动分区。


















