结论:RANGE分区在归档场景下易失效,因其不自动删除旧数据且裁剪依赖WHERE条件精准匹配分区键;若查询未直接使用分区列(如用YEAR(create_time))、或条件缺失/含函数,优化器将全分区扫描。

为什么 RANGE 分区在归档场景下容易失效
直接说结论:MySQL 的 RANGE 分区本身不自动删除旧数据,也不保证查询只扫描目标分区——如果 WHERE 条件没用上分区键,或用了函数/表达式,优化器会退化为全分区扫描。归档快的前提是“删旧”和“查新”都精准命中单个(或极少数)分区。
常见错误现象:ALTER TABLE ... DROP PARTITION p_202301 执行慢、锁表久;SELECT * FROM logs WHERE create_time > '2024-06-01' 却触发 12 个分区扫描;EXPLAIN PARTITIONS 显示 partitions: NULL 或列出全部分区。
- 分区键必须是
create_time这类单调递增时间字段,且不能是DATE(create_time)或YEAR(create_time)等函数表达式 - 所有关键查询(含归档 DELETE)必须在
WHERE中直接使用该字段的范围比较,如create_time >= '2024-01-01' AND create_time - 建表时必须显式指定
ENGINE=InnoDB,否则 MySQL 8.0+ 默认可能用MyISAM(不支持事务,归档不可靠)
如何定义可安全归档的 RANGE COLUMNS 分区
优先用 RANGE COLUMNS 而非传统 RANGE,它原生支持 DATETIME/TIMESTAMP 字段,避免隐式类型转换导致分区裁剪失败。关键点在于:每个分区上限必须是精确的、左闭右开边界值。
示例建表语句:
CREATE TABLE logs (
id BIGINT NOT NULL,
create_time DATETIME NOT NULL,
content TEXT,
PRIMARY KEY (id, create_time)
) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(create_time) (
PARTITION p_202401 VALUES LESS THAN ('2024-02-01 00:00:00'),
PARTITION p_202402 VALUES LESS THAN ('2024-03-01 00:00:00'),
PARTITION p_202403 VALUES LESS THAN ('2024-04-01 00:00:00'),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
-
LESS THAN值必须是字符串字面量(带引号),且格式与字段类型严格匹配;写成20240201数字会触发隐式转换,导致分区裁剪失效 - 末尾保留
p_future分区,避免插入未来时间数据时报错;但需定期用REORGANIZE PARTITION拆分它,否则它会越积越大 - 主键必须包含分区键(
create_time),否则建表失败;若业务主键只有id,则需改为PRIMARY KEY (id, create_time)
归档时 DROP PARTITION 为什么卡住?怎么解
DROP PARTITION 在 InnoDB 中本质是 DDL,会重建表(即使只删一个分区),锁表时间随分区数据量线性增长。当 p_202301 含数千万行时,操作可能持续数分钟,阻塞所有写入。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 绝对不要在业务高峰期执行
ALTER TABLE ... DROP PARTITION - 替代方案:用
TRUNCATE PARTITION p_202301—— 它清空分区但不释放磁盘空间,速度极快(毫秒级),且不锁全表(仅锁该分区) - 真正释放磁盘空间需后续执行
OPTIMIZE TABLE logs,但它会锁表;更稳妥的做法是定期在低峰期对整个表做OPTIMIZE,而非每次归档后立刻执行 - 检查是否误用了
innodb_file_per_table=OFF:此时所有分区共享一个ibdata1,TRUNCATE不释放空间,必须调成ON(需重启)
如何自动化维护分区边界(避免手动 ALTER)
手动维护分区边界极易遗漏或出错,推荐用存储过程 + 事件调度器实现月度自动扩充分区。核心逻辑是:每月初检查 p_future 是否即将写满,若是,则拆分出下个月分区。
关键步骤:
- 启用事件调度器:
SET GLOBAL event_scheduler = ON - 创建过程
sp_add_monthly_partition,内含ALTER TABLE ... REORGANIZE PARTITION p_future INTO (...)语句,动态拼接下个月的LESS THAN值 - 创建每月 1 号凌晨 2 点触发的事件:
CREATE EVENT ev_add_partition ON SCHEDULE EVERY 1 MONTH STARTS '2024-07-01 02:00:00' DO CALL sp_add_monthly_partition() - 注意:过程内必须用
CONCAT和DATE_ADD动态生成时间字符串,硬编码日期会导致后续失效
复杂点在于 REORGANIZE PARTITION 要求目标分区的 LESS THAN 值必须严格大于原 p_future 的下界——这意味着你得先查 INFORMATION_SCHEMA.PARTITIONS 获取当前 p_future 的 partition_description,再计算新边界。这步漏掉,过程会报错退出。










