mysql的索引优化与数据库存储引擎选择

来源:这里教程网 时间:2026-02-28 20:44:12 作者:

为什么
WHERE
条件用了字段却没走索引?

常见现象是执行

EXPLAIN SELECT * FROM t WHERE status = 'active';
显示
type=ALL
,即全表扫描。根本原因往往不是没建索引,而是索引失效:

status
字段类型为
VARCHAR
,但查询时写了
WHERE status = 1
(隐式类型转换)
在索引列上用了函数,比如
WHERE UPPER(name) = 'JOHN'
联合索引
(a,b,c)
,只用
WHERE b = ?
WHERE a = ? AND c = ?
(缺少最左前缀)
索引列允许
NULL
,且查询条件是
IS NULL
,某些 MySQL 版本下无法使用索引(尤其
MyISAM

验证方式:用

SHOW INDEX FROM t;
看索引结构,再用
EXPLAIN FORMAT=TRADITIONAL
查看实际是否命中。

InnoDB
MyISAM
在索引设计上的关键差异

二者 B+ 树组织方式不同,直接影响你建什么索引、怎么写查询:

InnoDB
主键即聚簇索引,数据行直接存于主键 B+ 树的叶子节点;二级索引叶子存的是主键值,回表成本真实存在
MyISAM
所有索引都是非聚簇的,叶子节点存的是行物理地址(.MYD 文件偏移),没有“回表”概念,但不支持事务和行锁
如果你频繁按
user_id
查询并需要返回大量字段,
InnoDB
下把
user_id
设为主键或建覆盖索引(如
INDEX idx_uid_name_age (user_id, name, age)
)能避免回表
MyISAM
COUNT(*)
很快(内部维护行数),但
InnoDB
必须扫索引树——所以不要在大表上无条件用
SELECT COUNT(*)

什么时候该用
MEMORY
引擎?别只看“快”

MEMORY
表数据全在内存,
SELECT
极快,但极易误用:

只适合临时中间结果、缓存维度表(如城市字典)、或秒级生命周期的会话数据 不支持
TEXT
/
BLOB
类型,也不支持外键和事务
服务器重启后数据全丢——如果误把用户订单表设为
MEMORY
,就是生产事故
默认用哈希索引(
HASH
),只支持等值查询(
=
);范围查询(
BETWEEN
>
)必须显式声明
USING BTREE
CREATE TABLE tmp_user_cache (
  id BIGINT PRIMARY KEY,
  name VARCHAR(64),
  updated_at DATETIME
) ENGINE=MEMORY
  DEFAULT CHARSET=utf8mb4
  AVG_ROW_LENGTH=128;

联合索引字段顺序到底怎么排?看查询模式,不是看字段重要性

错误认知:“把区分度高的字段放前面”。真正决定顺序的是

WHERE
ORDER BY
的组合模式:

如果常查
WHERE category = ? AND status = ? ORDER BY created_at DESC
,索引应为
(category, status, created_at)
,而非反过来
如果还有
WHERE category = ? ORDER BY status ASC
,那
(category, status)
就比单列
category
更有效
注意
ORDER BY
方向一致性:
(a ASC, b DESC)
在 MySQL 8.0+ 才支持,旧版本会忽略
b
的排序,降级为文件排序(
Using filesort
索引总长度别超限制:
InnoDB
单索引前缀最大 3072 字节(
utf8mb4
下约 768 个字符),超了会截断,导致后缀字段失效

最常被忽略的一点:索引不是越多越好。每个写操作都要更新所有相关索引,高并发写入场景下,5 个索引可能比 1 个慢 3 倍以上——先看慢查询,再加索引,别预设。

相关推荐