因为FULLTEXT仅支持关键词匹配,不适用等值查询;B+树索引在长文本列上受767字节限制、体积大、写入慢;而CRC32哈希列(INT型)索引紧凑高效,配合原字段精确过滤可兼顾性能与准确性。

为什么不用直接建全文索引或 B+ 树索引?
因为 FULLTEXT 只适合关键词匹配(如 MATCH ... AGAINST),不适用于精确 URL 或长文本等值查询;而对整列建 B+ 树索引,在 VARCHAR(2000) 或 TEXT 字段上会触发长度限制(InnoDB 单列索引上限 767 字节,utf8mb4 下仅约 191 个字符),且索引体积大、写入慢、B+ 树节点分裂频繁。
用 CRC32() 构造哈希列并建索引的实操步骤
核心是新增一个整型哈希列,用触发器自动维护,再在该列建普通 B+ 树索引:
- 添加列:
ALTER TABLE log ADD COLUMN url_hash INT UNSIGNED NOT NULL DEFAULT 0; - 建索引:
CREATE INDEX idx_url_hash ON log(url_hash); - 创建插入触发器:
DELIMITER | CREATE TRIGGER log_url_hash_ins BEFORE INSERT ON log FOR EACH ROW BEGIN SET NEW.url_hash = CRC32(NEW.url); END; |
- 创建更新触发器:
CREATE TRIGGER log_url_hash_upd BEFORE UPDATE ON log FOR EACH ROW BEGIN SET NEW.url_hash = CRC32(NEW.url); END; |
- 补全已有数据:
UPDATE log SET url_hash = CRC32(url) WHERE url_hash = 0;
查询时必须同时带上原字段,否则可能误命中
CRC32() 是 32 位无符号整数,碰撞概率约 1/42.9 亿,但当表行数超百万后,冲突实际可测——比如两个不同 URL 算出相同 CRC32 值,仅靠 url_hash 查询会返回多行。
- ✅ 正确写法:
SELECT rank FROM log WHERE url_hash = CRC32('https://example.com/path?x=1') AND url = 'https://example.com/path?x=1'; - ❌ 错误写法:
SELECT rank FROM log WHERE url_hash = CRC32('...');(可能返回 0 行或 >1 行) - MySQL 优化器能识别
url_hash上的索引,先快速定位小范围候选行,再用url = ...做最终过滤,整体仍远快于全表扫描
为什么不用 MD5() 或 SHA1()?
它们返回的是字符串(MD5 是 32 字符 hex,SHA1 是 40 字符),无法高效走索引:
-
MD5(url)结果是VARCHAR(32),建索引后仍要比较 32 字节,B+ 树节点更臃肿,比较成本高 - MySQL 对字符串哈希列的索引选择性不如整型,且
WHERE md5_col = MD5('...')中函数调用无法使用索引(除非生成列 + 函数索引,但 5.7 不支持) -
CRC32()返回INT UNSIGNED(4 字节),天然紧凑、可排序、B+ 树深度浅、缓存友好
真正容易被忽略的是:触发器只覆盖 DML 路径,如果通过 LOAD DATA INFILE 或主从复制写入,需确保从库也启用触发器,或改用生成列(MySQL 5.7+ 支持 STORED 列 + 索引,更可靠)。


















