首页/数据/SQL 速查/前缀索引:长字符串列的折中
性能与执行计划72 条

前缀索引:长字符串列的折中

长字符串列只索引前 N 个字符:减小索引体积,但无法覆盖查询,也不能完全替代整列排序用途。

SQL 速查数据更新于 2026-10-13

前缀索引的取舍

前缀索引只索引长字符串列的前 N 个字符:MySQL 用「列名(N)」的语法建索引,索引体积明显缩小,写入与内存占用也跟着下降。代价是它无法覆盖查询——靠前缀定位到候选行后,还要回表核对完整值。PostgreSQL 没有这个语法,长文本匹配一般借助 pg_trgm 之类扩展。

基本信息

适用平台MySQL 特有;PostgreSQL 无此语法
语法ALTER TABLE 表 ADD INDEX 索引名 (列名(N))
区分度评估用 COUNT(DISTINCT LEFT(列, N)) 对比,N 越大区分度越高、索引越大
限制不能用于覆盖索引,也不能完整支撑 ORDER BY
PostgreSQL 替代普通 B 树可做前缀匹配,模糊匹配用 pg_trgm

典型使用场景

  • URL、邮箱、地址这类长字符串列建索引
  • 索引体积超出预算时用前缀做折中
  • 评估加索引的写入成本前先算区分度
  • 对比不同前缀长度的选择性,选够用的最小值
  • 避免在长文本列上直接建全列索引

示例

示例
-- 先量化不同前缀长度的区分度,再决定长度
SELECT COUNT(DISTINCT LEFT(url, 10)) / COUNT(*) AS sel10,
       COUNT(DISTINCT LEFT(url, 20)) / COUNT(*) AS sel20,
       COUNT(DISTINCT url) / COUNT(*)             AS sel_full
FROM pages;

-- 按选定的长度建前缀索引
ALTER TABLE pages ADD INDEX idx_url20 (url(20));

-- 验证命中:key 应为 idx_url20,且仍需要回表核对完整值
EXPLAIN SELECT id FROM pages WHERE url = 'https://example.com/a';

前缀长度怎么选

前缀长度区分度索引体积适用场景
10较低小只按域名或前缀查询
20中等中大多数 URL 检索
50较高较大需要更精确的定位
整列最高最大需要覆盖索引或精确排序
前缀索引始终需要回表:即使查询只用到这一列,MySQL 也要回表核对完整值,所以它替代不了覆盖索引,做纯只读加速时收益有限。
区分度不是越高越好:很多列前 10 个字符就能区分绝大多数值,把前缀加到 50 只增加体积却几乎不提升过滤能力,用统计对比定长度更靠得住。

易错点

  • 随手选前缀长度:先用区分度统计对比再定长度,否则要么浪费空间要么区分度不足
  • 指望前缀索引覆盖查询:前缀被截断后仍需回表核对完整值,覆盖索引的收益拿不到
  • 用前缀索引支撑 ORDER BY:只能保证前缀有序,完整值的排序仍要额外排序
  • 在 PostgreSQL 里套用这个语法:PG 不支持列级前缀索引,需改用 pg_trgm 之类的方案
  • 把前缀索引等同于全列索引:等值查询要回表,前缀区分度低时反而引入更多随机读

数据来源与参考

  • MySQL 8.0 Reference Manual §8.3.5 Column Prefixes
  • PostgreSQL Documentation · pg_trgm
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌