需對 DATETIME 類型欄位按天進行分區。MySQL 支援四大分區類型:RANGE、LIST、HASH、KEY,各有衍生子類型。本文針對「按天分區」需求,對比三種主流方案的分區裁剪效果。
建立測試表 test,含 100 萬筆資料,hiredate 跨度 10 天(2015-12-01 ~ 2015-12-10),每天約 10 萬筆。
mysql> SELECT MIN(hiredate), MAX(hiredate) FROM test; +---------------------+---------------------+ | MIN(hiredate) | MAX(hiredate) | +---------------------+---------------------+ | 2015-12-01 00:00:00 | 2015-12-10 23:59:56 | +---------------------+---------------------+ mysql> SELECT DATE(hiredate), COUNT(*) FROM test GROUP BY DATE(hiredate); +----------------+----------+ | DATE(hiredate) | COUNT(*) | +----------------+----------+ | 2015-12-01 | 99963 | | 2015-12-02 | 100032 | ... (共 10 天) | 2015-12-10 | 99902 | +----------------+----------+
核心指標:執行 EXPLAIN PARTITIONS SELECT ... WHERE hiredate BETWEEN ... 時,partitions 欄位是否僅顯示相關分區(即實現 Partition Pruning)。
CREATE TABLE range_datetime (
id INT, hiredate DATETIME
) PARTITION BY RANGE (TO_DAYS(hiredate)) (
PARTITION p1 VALUES LESS THAN (TO_DAYS('20151202')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('20151203')),
...
PARTITION p10 VALUES LESS THAN (TO_DAYS('20151211'))
);
結果: EXPLAIN 顯示 partitions: p7,p8,p9,p10 —— 成功裁剪。
CREATE TABLE range_columns (
id INT, hiredate DATETIME
) PARTITION BY RANGE COLUMNS(hiredate) (
PARTITION p1 VALUES LESS THAN ('20151202'),
PARTITION p2 VALUES LESS THAN ('20151203'),
...
PARTITION p10 VALUES LESS THAN ('20151211')
);
結果: 同樣顯示 p7,p8,p9,p10 —— 成功裁剪,且語法更直觀,無需函數轉換。
CREATE TABLE hash_datetime ( id INT, hiredate DATETIME ) PARTITION BY HASH(TO_DAYS(hiredate)) PARTITIONS 10;
結果: EXPLAIN 顯示 partitions: p0,p1,...,p9(全部分區)—— 無法裁剪,HASH 分區不支援範圍查詢裁剪。
TIMESTAMP 僅支援 UNIX_TIMESTAMP() 函數分區,使用 TO_DAYS() 會報錯:ERROR 1486 (HY000): Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed。
CREATE TABLE range_timestamp (
id INT, hiredate TIMESTAMP
) PARTITION BY RANGE (UNIX_TIMESTAMP(hiredate)) (
PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2015-12-02 00:00:00')),
...
PARTITION p10 VALUES LESS THAN (UNIX_TIMESTAMP('2015-12-11 00:00:00'))
);
測試同樣實現分區裁剪(p7,p8,p9,p10)。
DATE/DATETIME 分區裁剪僅支援 YEAR()、TO_DAYS();5.7 新增 TO_SECONDS()。LIST 雖可實現按天分區,但需枚舉每一天值,維護成本高,不如 RANGE 適合。TIMESTAMP 只能 使用 UNIX_TIMESTAMP() 進行分區,切勿嘗試其他函數。