数据库常见问题小结
一、主键相关
1. 主键和唯一键的区别
- 一张表只能有一个主键,可以有多个唯一索引;
- 主键字段不允许为 NULL,唯一索引字段可以存最多一条 NULL;
- InnoDB 主键是聚簇索引,唯一索引是普通二级索引,叶子节点存主键值,所以使用主键查询更快;
- 只有主键能设置自增
auto_increment; - 主键逻辑代表行唯一标识,可作为外键关联。
- 主键使用场景:
- 每条数据的唯一标识,关联外键;
- 分页、根据 ID 查询、分库分表分片键;
- 需要自增 ID 场景
- 唯一键使用场景:业务字段唯一性校验,如手机号、邮箱、身份证、订单编号、设备编号等,业务上不能重复,但不适合做主键(可能修改、允许为空)。
2. 为什么生产环境每张表必须设置主键?
- InnoDB 表数据依托聚簇索引存储,无主键会自动生成隐藏 rowid,查询、分页、更新性能差;
- 分库分表、乐观锁、分布式 ID、关联外键都依赖主键;
- 批量更新 / 删除、binlog 同步、事务锁机制都以主键为基准;
- 避免全表扫描,精准定位单行数据。
3. 自增主键 vs 雪花 ID vs UUID 优缺点
- 自增 int/bigint :有序、索引紧凑、插入快、占用空间小。但分库分表会 ID 冲突,容易暴露业务数据量。
- 雪花算法 ID :分布式唯一、有序、数字类型,适合分表。但依赖机器时间,时钟回拨会产生重复 ID。
- UUID :本地生成无冲突; 但无序,插入时页分裂严重,索引膨胀,查询慢,占用空间大。
4. 联合主键有什么问题?
- 索引字段变长,占用更多磁盘内存;
- 关联其他表作为外键时字段过多;
- 更新、索引维护成本更高; 规范:尽量用单一 bigint 主键,业务唯一约束交给唯一索引。
5. 如果主键删除了,表会怎么样?
InnoDB 会使用隐藏 6 字节 rowid 作为聚簇索引,业务查询性能大幅下降,不推荐。
二、索引相关
1. 索引及其作用和优缺点
- 索引:数据库的目录结构,B+Tree 为主。
- 作用:加快查询速度,避免全表扫描;
- 优点:查询、排序、分组速度提升;
- 缺点:占用磁盘空间;插入 / 更新 / 删除需要维护索引,降低写入性能。
2. InnoDB 聚簇索引和二级索引区别
- 聚簇索引(主键索引):叶子节点存储完整整行数据,整张表按主键排序,一张表只有一个。
- 二级索引(普通 / 唯一 / 联合索引):叶子节点只存储主键值,查询到主键后需要回表,再走聚簇索引拿完整数据。
3. 回表是什么?什么场景不会回表?
- 回表:通过二级索引拿到主键,再去聚簇索引查询完整数据。
- 覆盖索引:查询字段全部包含在联合索引内,不需要回表,性能最优。
4. B 树和 B + 树索引区别,为什么 MySQL 用 B + 树?
- B + 树所有数据只存在叶子节点,非叶子只存索引键;B 树每行节点都存数据;
- B + 树叶子节点链表相连,范围查询、分页遍历极快;
- 非叶子节点更小,内存可缓存更多索引,IO 次数更少。
5. Hash 索引和 B + 树索引适用场景
- Hash:等值查询极快,不支持范围、排序、模糊匹配,Memory 引擎使用;
- B+Tree:支持等值、范围、排序、分页,InnoDB 默认。
三、索引相关
1. 什么是最左前缀匹配原则
建立联合索引 idx(a,b,c),查询条件必须匹配最左边前列字段,才能走索引。如:
- 能走索引:where a=? /where a=? and b=? /where a=? and b=? and c=?
- 索引失效:where b=? /where c=? /where b=? and c=?
2. 联合索引什么时候失效
- 不满足最左前缀;
- 索引列使用函数、运算,如
where date(create_time) = '2026-01-01'; - 索引列使用隐式类型转换:字符串字段用数字查询;
- 模糊查询
%xxx前置通配符; - or 连接无索引字段。
- not in / != / is not null;
- 索引列参与四则运算;
- MySQL 优化器判断全表扫描更快,主动放弃索引。
3. 联合索引字段顺序怎么设计
- 区分度高的字段放前面;
- 等值查询字段放前面,范围查询放最后; 例:
idx(uid,status,create_time),create_time 是范围,放末尾
4. 唯一索引、普通索引、主键索引、覆盖索引
- 主键索引:聚簇索引,唯一非空;
- 唯一索引:二级索引,值唯一,允许一条 null;
- 普通索引:无唯一性限制,仅加速查询。
-
覆盖索引:查询字段全部在联合索引中,不需要回表,
explainExtra 显示 Using index。 写法:select id,name from user where uid=?索引包含 uid,id,name。
5. 索引优化
1) 索引越多越好吗?
索引并不是越多越好。插入、更新、删除时需要同步修改所有索引,写入性能下降;索引占用磁盘,内存缓冲池压力变大。
2) 百万大表如何建索引?
- 业务低峰期执行;
- 使用
ALTER TABLE ... ADD INDEX LOCK=NONE避免锁表; - 优先建立高频查询、区分度高的联合索引;
- 避免过长字符串作为索引,可前缀索引。
3) 前缀索引使用场景
- 长字符串(url、文本),只取前 N 位建立索引,节省空间;
- 缺点:无法用于 order by、覆盖索引。
4) 分页深偏移优化
limit 100000,10偏移过大扫描大量数据;- 优化:主键过滤
where id>100000 limit 10;延迟关联、覆盖索引分页。
6. 锁与索引关联
- 不走索引会升级为表锁;走索引仅锁匹配行(行锁);
- 唯一索引等值命中唯一数据,锁范围极小;范围查询会产生间隙锁,容易死锁。
7. Explain 相关
-
explain 不能看到锁和真实执行耗时。explain 仅生成执行计划,不会真实执行 SQL,无锁、无耗时;要看真实执行耗时,使用
explain analyze(MySQL8.0+),会真实执行并输出实际扫描行数、耗时; -
联表查询 explain 中小表是驱动表。id 相同的情况下,type 更好、rows 更少的表为驱动表,小表驱动大表;
-
limit 100000,10 explain 的问题是rows 扫描行数巨大,偏移量大,需要扫描前面十万行。优化方案是主键分页、延迟关联、覆盖索引;
- type :性能级别,衡量查询好坏。性能从优到劣排序是
system > const > eq_ref > ref > range > index > ALL。需要特别注意的是:type 是 all的原因是索引失效,优化器判断使用索引成本更高,主动放弃索引走全表扫描。 常见原因:数据倾斜、隐式转换、字段运算、左模糊 % xxx;-
type 是 ref 但很慢的原因:
-
扫描 rows 预估数量很大;
-
需要大量回表,没有覆盖索引;
-
出现 Using filesort / Using temporary;
-
索引区分度极低,大量重复数据。
-
- key:实际使用索引;
- key_len:使用索引字段占用字节长度,用来判断联合索引使用了几个列。越短越好;可判断是否满足最左匹配;字符串、是否允许 null 会占用额外字节:
-
key_len 判断联合索引用了几列是通过字节长度累加判断,比如索引 (a,b,c),key_len 只包含 a 说明只用到第一列,最左匹配失效;
-
- rows:MySQL 预估需要扫描的行数,数值越大越慢,仅预估,不完全准确;
- Extra 优化关键:
- Using index 覆盖索引,不需要回表,性能最优;
- Using filesort 文件排序,必须优化 。代表 MySQL 无法利用索引排序,额外开辟内存 / 磁盘排序,出现 order by 无索引。 解决方案:排序字段加入联合索引末尾;
- Using temporary 临时表,临时表,严重损耗性能。 group by、distinct、多表联查无索引时生成临时表。 优化:分组字段建立索引。
-
Not exists :仅出现在
LEFT JOIN + 右表主键 IS NULL的场景,匹配到第一条关联数据就终止扫描,减少 IO,是 MySQL 的内置优化策略,不需要优化 SQL; -
Range for each record :in 子查询低效,建议改成 join 联表。
更多推荐



所有评论(0)