MySQL字段类型、索引设计、大字段处理及外键使用需依实际场景权衡:手机号用CHAR(11)或BIGINT,状态用TINYINT;联合索引须按查询顺序排列且范围条件置右;TEXT/BLOB易致磁盘临时表;外键在高并发写入时成性能瓶颈。

字段类型选错导致索引失效
MySQL里VARCHAR(255)不是万能默认值,尤其当字段实际只存手机号、状态码这类固定长度数据时,用CHAR(11)或TINYINT更稳。InnoDB对VARCHAR前缀索引支持有限,如果在VARCHAR(500)上建普通索引,而查询只用到前10个字符,索引可能根本用不上。
- 手机号统一用
CHAR(11)(不带+86)或BIGINT UNSIGNED(纯数字无前导零),避免VARCHAR隐式转换 - 状态字段优先用
TINYINT配注释,别用VARCHAR(20)存"active"/"inactive"——字符串比较比整数慢,且无法利用紧凑的索引页 - 时间字段不用
VARCHAR存"2024-03-15 14:22:03",必须用DATETIME或TIMESTAMP,否则WHERE created_at > '2024-01-01'会全表扫
联合索引顺序没按查询条件排列
联合索引(a, b, c)能加速WHERE a = ? AND b = ?,但对WHERE b = ? AND c = ?完全无效。MySQL只支持最左前缀匹配,中间断掉就退化成全索引扫描。
- 把高频等值查询字段放最左,比如订单表常查
user_id = ? AND status = ?,索引就该建(user_id, status),而不是反过来 - 范围查询字段(
>、BETWEEN、LIKE 'abc%')必须放在联合索引末尾,它之后的字段无法被索引命中 - 用
EXPLAIN看key_len值:如果查(a,b,c)却只显示用了2个字节,说明c没参与索引查找
TEXT/BLOB字段引发隐式磁盘临时表
只要SELECT或ORDER BY里出现未加前缀的TEXT字段,哪怕只查1行,MySQL也可能放弃内存临时表,转而写磁盘,性能断崖下跌。特别是配合GROUP BY或ORDER BY时,max_sort_length配置也救不了。
- 非必要不直接
SELECT content,拆成主表+详情表,主表只留content_id或摘要字段 - 真要排序或分组,给
TEXT字段加前缀索引(如ADD INDEX idx_title (title(191))),但注意前缀不能超767字节(utf8mb4下最多191字符) -
GROUP_CONCAT结果超group_concat_max_len(默认1024)会截断,需提前SET或改配置,否则逻辑出错
外键约束在高并发写入场景反成瓶颈
外键会触发额外的元数据锁和子表检查,插入10万行带外键的订单记录,可能比无外键慢3–5倍。线上OLTP系统若已靠应用层保证一致性,外键反而拖慢吞吐。
- 读多写少、强一致要求高的场景(如财务核心)可保留外键;但电商订单、日志类表建议去掉,靠应用层或定时校验补位
- 删除父记录前,务必先确认子表是否有未清理数据,否则
DELETE FROM users WHERE id = 123会卡住并报Cannot delete or update a parent row - 迁移旧表加外键前,先用
SELECT COUNT(*)验证子表数据是否合法,否则ALTER TABLE会卡死在元数据锁上
规范化不是贴标签,是权衡。字段类型、索引顺序、大字段处理、外键取舍——每个选择背后都是查询模式、写入压力、运维成本的具体博弈。漏掉任一环,表结构就变成性能负债。



















