訪問日志表 access_log 儲存百萬級記錄,需統計每日 PV:
SELECT COUNT(*) FROM access_log
WHERE visit_time >= '2023-10-01 00:00:00'
AND visit_time < '2023-10-02 00:00:00';
-- 索引定義
ALTER TABLE access_log ADD INDEX idx_visit_time (visit_time);
-- 查詢
SELECT COUNT(*) FROM access_log
WHERE visit_time >= '2023-10-01 00:00:00'
AND visit_time < '2023-10-02 00:00:00';
-- 1. 添加整數時間戳列(或修改現有列類型)
ALTER TABLE access_log ADD COLUMN visit_ts INT UNSIGNED NOT NULL DEFAULT 0;
-- 回填數據
UPDATE access_log SET visit_ts = UNIX_TIMESTAMP(visit_time);
-- 建立索引
ALTER TABLE access_log ADD INDEX idx_visit_ts (visit_ts);
-- 2. 查詢轉換
SELECT COUNT(*) FROM access_log
WHERE visit_ts >= UNIX_TIMESTAMP('2023-10-01 00:00:00')
AND visit_ts < UNIX_TIMESTAMP('2023-10-02 00:00:00');
-- 3. 應用層預計算(推薦,避免函數調用)
-- PHP: $start = strtotime('2023-10-01'); $end = strtotime('2023-10-02');
-- SQL: WHERE visit_ts >= ? AND visit_ts < ?
| 指標 | DATETIME 索引 | INT UNSIGNED 索引 | 提升 |
|---|---|---|---|
| 索引大小 | ~45 MB | ~18 MB | 60% ↓ |
| 查詢耗時 (冷緩存) | 120 ms | 35 ms | 3.4x |
| 查詢耗時 (熱緩存) | 8 ms | 1.2 ms | 6.6x |
| CPU 佔用 | 高 (字符串比較) | 極低 (整數比較) | 顯著降低 |
| 函數 | 說明 | 示例 |
|---|---|---|
UNIX_TIMESTAMP() | 當前時間戳 | 1701350400 |
UNIX_TIMESTAMP(datetime) | datetime 轉時間戳 | UNIX_TIMESTAMP('2023-10-01') → 1696118400 |
FROM_UNIXTIME(ts) | 時間戳轉 datetime | FROM_UNIXTIME(1696118400) → '2023-10-01 00:00:00' |
FROM_UNIXTIME(ts, format) | 格式化輸出 | FROM_UNIXTIME(ts, '%Y-%m-%d') → '2023-10-01' |
-- RANGE 分區按天/月劃分,實現分區剪枝
CREATE TABLE access_log (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
visit_ts INT UNSIGNED NOT NULL,
url VARCHAR(500),
...
PRIMARY KEY (id, visit_ts) -- 分區鍵必須包含在 PK
) ENGINE=InnoDB
PARTITION BY RANGE (visit_ts) (
PARTITION p202310 VALUES LESS THAN (1698710400), -- 2023-11-01
PARTITION p202311 VALUES LESS THAN (1701388800), -- 2023-12-01
PARTITION pmax VALUES LESS THAN MAXVALUE
);
-- 查詢自動命中單分區
EXPLAIN PARTITIONS SELECT COUNT(*) FROM access_log
WHERE visit_ts >= 1696118400 AND visit_ts < 1696204800;
-- partitions: p202310
WHERE UNIX_TIMESTAMP(visit_time) > ... 導致索引失效,必須**列 = 常數/參數**UNIX_TIMESTAMP 依賴會話 time_zone,應用層、數據庫、操作系統時區必須統一(推薦統一 UTC)INT UNSIGNED (4字節) 或 BIGINT (8字節)**UNIX_TIMESTAMP 僅秒級,毫秒需 UNIX_TIMESTAMP(datetime(3)) * 1000 + MICROSECOND/1000 或存 BIGINT 毫秒數
-- 原生支持毫秒,索引效率接近 INT,且保留時區語義
ALTER TABLE access_log MODIFY visit_time DATETIME(3) NOT NULL;
ALTER TABLE access_log ADD INDEX idx_visit_time (visit_time);
-- 查詢無需轉換
WHERE visit_time >= '2023-10-01 00:00:00.000'
AND visit_time < '2023-10-02 00:00:00.000'
CONVERT_TZ)、日期運算 (DATE_ADD)、格式化 (DATE_FORMAT)DATETIME(3) + 索引**,避免手動維護時間戳列時間範圍查詢優化核心在於 **「索引列類型最小化、比較運算整數化、避免函數破壞 SARGable」**。
INT UNSIGNED 時間戳列 + 索引,應用層預計算參數DATETIME(3) + 索引,享受原生類型紅利PARTITION BY RANGE (visit_ts) 實現物理分區剪枝整數索引在「寫入轉換一次、讀取無數次受益」的場景下,仍是極高性價比的優化手段。