group by大字段(如text、blob、长varchar)会导致mysql放弃索引、全表扫描及临时表,因索引仅支持前缀且无法保证完整值比较准确性。

GROUP BY 大字段(如 TEXT、BLOB、长 VARCHAR)会直接导致 MySQL 放弃使用索引、强制走全表扫描 + 内存临时表,甚至触发磁盘临时表和 filesort,性能断崖式下跌。
为什么 TEXT/VARCHAR(1000) 这类字段不能直接 GROUP BY?
MySQL 不允许对未加前缀的 TEXT/BLOB 字段建普通索引;即使对 VARCHAR(1000) 建了索引,默认也只索引前 767 字节(utf8mb4 下最多 191 字符),而 GROUP BY 要求“完整值比较”——如果两行前 191 字符相同但后续不同,索引就无法保证分组准确性,优化器干脆放弃用索引。
常见错误现象:
- EXPLAIN 显示
type: ALL(全表扫描),key: NULL -
Extra列同时出现Using temporary和Using filesort - 查询耗时从毫秒级跳到数分钟,
SHOW PROCESSLIST中状态长期卡在Creating sort index或Copying to tmp table
如何确认是不是大字段惹的祸?
先查字段定义:DESCRIBE table_name 或 SHOW COLUMNS FROM table_name LIKE 'col_name',重点关注 Type 列是否为 text、mediumtext、varchar(500) 及以上。
再看执行计划是否“绕开”该字段:
- 如果
GROUP BY long_text_col,但EXPLAIN的key列为空,基本可锁定问题 - 若已有联合索引包含该字段,但
key_len明显偏小(比如预期 200 字节却只显示 767),说明实际只用了前缀 - 对
VARCHAR(2000)执行GROUP BY SUBSTRING(long_col, 1, 200)后变快,就是典型前缀截断失效信号
真正能落地的三种解法
别指望加 SQL_BIG_RESULT 或调大 tmp_table_size——这只是让崩得更稳,不是更快。
- 改字段类型:把
TEXT换成VARCHAR(255)(业务上真需要那么长?多数场景 URL、标签、简短描述够用) - 建前缀索引并显式分组:例如
ALTER TABLE t ADD INDEX idx_long_col (long_col(191)),然后写成GROUP BY long_col(191)(MySQL 8.0+ 支持,5.7 需确保业务接受截断语义) - 用哈希值替代原始值分组:增加一列
long_col_hash CHAR(32) AS (MD5(long_col)) STORED,对该列建索引并GROUP BY long_col_hash;注意校验冲突(极低概率)
最容易被忽略的陷阱:ORDER BY + 大字段 GROUP BY 一起用
即使你强行给 VARCHAR(1000) 加了前缀索引,只要 SQL 里还带 ORDER BY long_col DESC,MySQL 8.0 以下版本仍会触发 Using filesort——因为前缀索引不支持降序排序。此时必须拆成两步:先子查询分组出 hash 或截断值,再外层 ORDER BY,或者改用窗口函数 ROW_NUMBER() OVER (PARTITION BY MD5(long_col) ORDER BY ...) 绕过。











