type: all且key: null是字符集或排序规则不一致的明确证据,只要join字段的character_set_name、collation_name、is_nullable三者任一不同,mysql解析阶段即拒绝使用索引,强制全表扫描。

EXPLAIN里type: ALL和key: NULL就是字符集不一致的铁证
只要JOIN字段的collation_name不完全相同,MySQL优化器就会放弃使用索引——不是“可能不走”,而是直接拒绝。哪怕两表都有product_id索引,只要一边是utf8mb4_0900_ai_ci、另一边是utf8mb4_unicode_ci,执行计划里被驱动表的type必为ALL,key为NULL。这不是配置没调好,是MySQL解析阶段就判定无法安全下推索引扫描。
隐式转换让索引彻底失效,不是慢,是根本不用
MySQL在ON条件中发现两边字段CHARACTER SET或COLLATION不兼容时,会悄悄对其中一列做隐式转换(比如把utf8转成utf8mb4),而这个过程发生在执行期,B+树索引无法覆盖转换后的值。结果就是:
- 被驱动表退化为全表扫描,哪怕只有5万行也秒变分钟级
- Extra字段出现
Using join buffer (Block Nested Loop),说明已启用低效嵌套循环 - CONVERT()或COLLATE硬写在ON里也无效——它只是语法通过,索引依然不命中
查字段定义,别信数据库默认值
很多人用SHOW CREATE DATABASE看默认字符集,但那跟已有字段无关。真正起作用的是字段级定义:
- 用
SHOW FULL COLUMNS FROM table_a LIKE 'product_id'查Collation列值 - 更稳妥:查
INFORMATION_SCHEMA.COLUMNS一次性比对:SELECT TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME IN ('t1','t2') AND COLUMN_NAME = 'product_id'; - 必须三者完全一致:
CHARACTER_SET_NAME、COLLATION_NAME、IS_NULLABLE(一边NOT NULL一边允许NULL也会触发隐式转换)
ALTER TABLE MODIFY才是唯一正解
改表不能只改CHARACTER SET,也不能只用CONVERT TO——它不改字段级COLLATE:
- 正确写法:
ALTER TABLE t2 MODIFY product_id VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - 改完立刻执行
SHOW CREATE TABLE t2确认字段定义已更新,别依赖SHOW FULL COLUMNS缓存 - 外键或全文索引字段需先删约束再重建,否则
MODIFY会失败 - 大表操作会锁表重建索引,MySQL 8.0+ 可加
ALGORITHM=INPLACE降低影响
collation_name对不上,优化器就在解析阶段判了“死刑”。更隐蔽的是连接层:@@collation_connection若与字段COLLATE不一致,连WHERE里的字面量都会被错误解释,索引照样废。











