業務表存儲百萬級 URL(平均 50-200 字符),直接建立 VARCHAR(500) 索引導致:
本文介紹 **「哈希列 + 函數索引」** 替代方案,實現 O(1) 級精確查找。
CREATE TABLE `url_map` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`url` VARCHAR(500) NOT NULL,
`url_crc` INT UNSIGNED NOT NULL, -- CRC32 哈希值
`url_fnv64` BIGINT UNSIGNED NOT NULL, -- FNV64 哈希值(可選)
PRIMARY KEY (`id`),
KEY `idx_url_crc` (`url_crc`), -- 核心查找索引
KEY `idx_url_fnv64` (`url_fnv64`) -- 64位備選
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 推薦:CRC32 + 原值雙重過濾(防碰撞)
SELECT id FROM url_map
WHERE url_crc = CRC32('https://www.baidu.com')
AND url = 'https://www.baidu.com';
-- 統計近似值(允許少量碰撞誤差)
SELECT COUNT(*) FROM url_map
WHERE url_crc = CRC32('https://www.baidu.com');
原理:優化器優先走 idx_url_crc(4 字節整數,極高選擇性、極小體積),命中後回表比對原值,完全規避碰撞風險。
| 函數 | 位長 | 輸出類型 | 碰撞概率(百萬行) | 性能 | 推薦場景 |
|---|---|---|---|---|---|
CRC32() | 32-bit | INT UNSIGNED | ~1% (9.3萬行達 1%) | 極快 | 中小表、配合原值雙重過濾 |
FNV64() | 64-bit | BIGINT UNSIGNED | 極低(2^64 空間) | 極快 | 大表、高基數、追求零碰撞 |
MD5() | 128-bit | CHAR(32) | 理論無 | 慢、佔空間 | **不推薦**作為索引哈希 |
SHA1() | 160-bit | CHAR(40) | 理論無 | 慢、佔空間 | **不推薦**作為索引哈希 |
核心原則:嚴禁直接用 MD5/SHA1 做索引列——長字符串佔用空間大、比較慢、且非數值類型無法利用 CPU 整數比較優勢。若需 64-bit 且無 FNV64,可自定義:
-- 取 MD5 後 16 位十六進制轉十進制(簡易 64-bit)
SELECT CONV(RIGHT(MD5('https://www.baidu.com'), 16), 16, 10) AS hash64;
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 應用層寫入時計算 | 無資料庫依賴、效能可控 | 多語言需重複實現、易漏寫 | 微服務架構、統一 DAO 層 |
| BEFORE INSERT/UPDATE 觸發器 | 資料庫強制約束、零侵入業務代碼 | 觸發器調試困難、主從同步需 log_bin_trust_function_creators=ON | 單體應用、歷史系統改造 |
| Generated Column (MySQL 5.7+) | 聲明式、自動維護、可建索引 | 5.7 只支持 VIRTUAL(不佔磁盤),8.0 支持 STORED | **首選方案** |
MySQL 8.0 最佳實踐:
ALTER TABLE url_map
ADD COLUMN url_crc INT UNSIGNED GENERATED ALWAYS AS (CRC32(url)) STORED,
ADD INDEX idx_url_crc (url_crc);
-- 寫入時自動計算,查詢直接走索引,零維護成本
/usr/share/dict/words 9.8 萬詞即出現 1 次 CRC32 碰撞。WHERE hash=CRC32(val) AND col=val,索引定位 + 回表精確比對,**理論零誤判**。WHERE hash=CRC32(val),接受極低誤差換取極致速度。| 方案 | 索引大小 | 精確查詢 QPS | 備註 |
|---|---|---|---|
直接索引 url VARCHAR(500) | ~450 MB | ~1,200 | 緩存命中率低、字符串比較慢 |
url_crc INT + 雙重過濾 | ~18 MB | ~18,000 | **提升 15 倍**,緩存友好 |
url_fnv64 BIGINT + 雙重過濾 | ~22 MB | ~19,500 | 略優於 CRC32,零碰撞焦慮 |