必须用分区键原值写WHERE条件,否则分区裁剪失效;MySQL仅识别partition_column OP constant形式的条件,加函数如DATE()会导致全分区扫描,上线前须用EXPLAIN验证partitions字段。

必须用分区键原值写 WHERE 条件,否则分区裁剪直接失效——哪怕只包一层 DATE() 或 YEAR() 都会触发全分区扫描。
WHERE 条件必须直写分区键,不能包裹任何函数
MySQL 分区裁剪只识别形如 partition_column OP constant 的条件(OP 是 =、>=、BETWEEN 等)。一旦对分区键加函数,优化器就无法推导出对应分区边界。
WHERE start_time >= '2024-07-01' AND start_time ✅ 能精准命中 <code>p202407-
WHERE DATE(start_time) = '2024-07-01'❌ 扫全分区 -
WHERE YEAR(start_time) = 2024 AND MONTH(start_time) = 7❌ 同样失效 -
WHERE TO_DAYS(start_time) BETWEEN TO_DAYS('2024-07-01') AND TO_DAYS('2024-07-31')✅ 可行,但没必要——多此一举还易错
分区表达式必须是单调递增整型,优先选 TO_DAYS()
用 TO_DAYS(start_time) 做 RANGE 分区,是因为它输出连续整数,边界清晰、无歧义。别用 YEAR(start_time)*100 + MONTH(start_time),它看似直观,但会导致分区不连续(比如 202413 不存在),且 VALUES LESS THAN (202408) 实际对应的是“2024年8月之前所有日期”,而非“2024年8月整月”。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 正确写法:
PARTITION BY RANGE (TO_DAYS(start_time)) (PARTITION p202407 VALUES LESS THAN (TO_DAYS('2024-08-01'))) - 错误写法:
PARTITION BY RANGE (YEAR(start_time)*100 + MONTH(start_time)) (PARTITION p202407 VALUES LESS THAN (202408)) - 注意:
TO_DAYS()返回的是从公元0年开始的天数,是确定值,不受格式/时区干扰
主键或唯一索引必须包含分区键
这是硬性限制:如果表有主键或任何唯一索引,分区键字段必须是其一部分。否则建表会报错 ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function。
- 假设
start_time是分区键,主键就得写成PRIMARY KEY (id, start_time),不能只有PRIMARY KEY (id) - 如果有联合唯一约束(如
UNIQUE (order_no, tenant_id)),也得把start_time加进去,否则同样失败 - 这个设计不是为了性能,而是 MySQL 内部一致性要求——分区键参与索引,才能保证同一分区内的唯一性可验证
用 EXPLAIN 验证是否真正裁剪
别猜,直接看 partitions 列。只要它显示多个分区名(如 p202406,p202407)或 NULL,就说明裁剪没生效。
- 执行
EXPLAIN SELECT * FROM tb_rap_order_info WHERE start_time >= '2024-07-01' AND start_time - 正常结果中
partitions应只显示p202407 - 若出现
p202406,p202407,常见原因是查询时间范围跨了分区边界(比如start_time >= '2024-06-25'),或该分区数据实际落在相邻分区(检查TO_DAYS('2024-07-01')是否真等于你定义的LESS THAN值)
最容易被忽略的一点:分区裁剪只在查询条件**严格覆盖分区键本身**时才起作用。应用层拼接 SQL 时,哪怕只是加了个 DATE() 或用了 ORM 自动生成的带函数条件,都会让整个分区策略归零。上线前务必用真实语句跑 EXPLAIN 看 partitions 字段。

















