数据库相关面试题

数据库索引是用于加速数据查询的一种数据结构,类似于书籍的目录。它通过建立特定字段的快速访问路径,减少数据库全表扫描的开销,从而提升查询效率。核心作用提高查询速度:快速定位目标数据(如 [代码] 、 [代码] 条件)。强制数据唯一性:唯一索引确保字段值不重复(如主键)。排序优化:索引可避免排序操作的临时表生成(如 [代…

作者:TaotaoYao

数据库索引是用于加速数据查询的一种数据结构,类似于书籍的目录。它通过建立特定字段的快速访问路径,减少数据库全表扫描的开销,从而提升查询效率。

核心作用

  1. 提高查询速度:快速定位目标数据(如 WHEREJOIN 条件)。
  2. 强制数据唯一性:唯一索引确保字段值不重复(如主键)。
  3. 排序优化:索引可避免排序操作的临时表生成(如 ORDER BY)。

常见类型

  1. B-tree 索引:默认类型,支持等值查询和范围查询(如数字、字符串)。
  2. 哈希索引:仅支持精确匹配,查询效率极高(如内存表)。
  3. 全文索引:针对文本内容的关键词搜索优化(如 LIKE "%keyword%")。
  4. 复合索引:基于多个字段组合,满足多条件查询。

优缺点

  • 优点:显著加速查询,减少磁盘 I/O。
  • 缺点:占用额外存储空间;增删改数据时需维护索引,影响写入性能。

设计原则

  1. 高频查询字段优先建索引。
  2. 区分度高的字段(如用户 ID)效果更佳。
  3. 避免对频繁更新的字段过度索引。

示例:为 users 表的 email 字段创建索引:  

CREATE INDEX idx_email ON users(email);


聚集索引和非聚集索引的区别:

1. **物理存储**:聚集索引决定数据物理存储顺序,数据行按索引顺序排列;非聚集索引不改变数据物理顺序,索引与数据分开存储。

2. **数量限制**:一个表只能有一个聚集索引;可创建多个非聚集索引。

3. **结构组成**:聚集索引本身就是数据表;非聚集索引是独立结构,包含指向数据行的指针。

4. **查找效率**:聚集索引直接定位数据;非聚集索引需先查索引再访问数据,可能产生额外I/O。

5. **适用场景**:聚集索引适合范围查询;非聚集索引适合精确匹配查询。


MySQL选择B+树作为索引底层结构的核心原因:

  1. 矮胖树结构优化I/O
  2. 多路平衡特性使树高度远低于二叉树(3-4层可存储千万级数据)
  3. 单次查询减少磁盘I/O次数(节点大小匹配磁盘页)
  4. 数据存储结构优势
  5. 非叶子节点仅存索引(可容纳更多键值)
  6. 数据全存于有序叶子节点(链表连接相邻节点)
  7. 特殊适配场景
  8. 范围查询:直接遍历叶子节点链表即可
  9. 全表扫描:只需遍历所有叶子节点
  10. 排序查询:天然有序结构避免额外排序
  11. 对比淘汰方案
  12. 哈希表:无法支持范围查询
  13. 二叉树:树高导致I/O次数剧增
  14. B树:非叶节点存数据导致树更高,范围查询效率低
  15. 稳定查询性能
  16. 任何查询都需到叶子节点(稳定O(logn)复杂度)
  17. 避免B树因数据位置不同导致的性能波动



回表:当查询字段不在非聚集索引中时,数据库需要通过索引查找到主键,再通过主键回到聚集索引中获取完整数据行的过程。


核心要点
1. 触发条件:使用非聚集索引且查询字段未被索引完全覆盖
2. 执行过程(两步查找):
  - 通过非聚集索引查找目标记录的主键
  - 通过主键在聚集索引中获取完整数据行
3. 性能影响:比直接使用聚集索引多一次索引查找(额外I/O)

示例说明

-- 创建索引
CREATE INDEX idx_email ON users(email);
-- 发生回表的查询(需获取未索引的name字段)
SELECT name FROM users WHERE email = '[email protected]';

执行过程:
1. 在idx_email索引树查找[email protected]对应的主键ID
2. 用该ID在聚集索引中查找完整的用户数据行
3. 从数据行中提取name字段返回


优化方案
- 覆盖索引:创建包含所需字段的复合索引
 sql CREATE INDEX idx_email_name ON users(email, name);
- 索引设计:将查询频次高的字段包含进索引


最左前缀原则

核心定义

复合索引的生效规则:查询条件必须从索引最左侧列开始,且不跳过中间字段,才能触发索引使用。

匹配规则

  1. 连续左匹配:  
  2. 索引 (A,B,C)
  3. 有效:WHERE A=? / WHERE A=? AND B=?
  4. 无效:WHERE B=? / WHERE B=? AND C=?
  5. 断点失效(中间列缺失): WHERE A=? AND C=? → 仅A列索引生效
  6. 范围查询截断: WHERE A>? AND B=? → B列不参与索引过滤

典型示例

-- 创建复合索引
CREATE INDEX idx_name_phone ON users(last_name, first_name, phone);

-- ✅ 触发索引的查询
SELECT * FROM users 
WHERE last_name = '张' 
AND first_name = '伟'; -- 连续使用前两列

-- ❌ 未触发索引的查询
SELECT * FROM users 
WHERE first_name = '伟' -- 未从最左列开始
AND phone = '13800138000';

设计建议

  1. 高频列前置:将WHERE子句中最常出现的字段放左侧
  2. 范围查询右置:范围查询字段(如>,<)尽量安排在索引末尾
  3. 避免冗余设计:索引(A,B)(A)功能重叠,可只保留前者

类比理解(电话簿模型)

  • 索引 (姓氏, 名字, 城市) 犹如电话簿排序:

     必须先按姓氏查找 → 再按名字过滤 → 最后按城市筛选

     直接按名字或城市查找无效


索引失效的常见情况

使用函数或表达式

对索引列使用函数或表达式会导致索引失效:

-- 失效:对索引列使用函数
SELECT * FROM users WHERE UPPER(name) = 'JOHN';

-- 失效:对索引列进行计算
SELECT * FROM orders WHERE total_amount * 0.8 > 1000;

不等操作符

使用!=、<>或NOT等于操作符通常无法使用索引:

-- 失效
SELECT * FROM products WHERE price <> 100;

通配符查询

LIKE以通配符开头时索引失效:

-- 失效:前导通配符
SELECT * FROM articles WHERE title LIKE '%数据库%';

-- 有效:后置通配符
SELECT * FROM articles WHERE title LIKE '数据库%';

OR连接条件

OR连接的条件中有一个没有使用索引时,整个索引可能失效:

-- 失效:status字段无索引
SELECT * FROM users WHERE name = '张三' OR status = 'active';

类型不匹配

字符串和数字类型不匹配或隐式类型转换导致索引失效:

-- 失效:id为数字类型
SELECT * FROM orders WHERE id = '1001';

违反最左前缀原则

复合索引未从最左列开始查询或跳过中间列:

-- 索引为(last_name, first_name, phone)
-- 失效:未从最左列开始
SELECT * FROM users WHERE first_name = '伟';

NULL值查询

对索引列使用IS NULL/IS NOT NULL在某些数据库中索引效果有限:

-- 可能失效:大量NULL值
SELECT * FROM products WHERE category IS NULL;

负向查询

使用NOT、NOT LIKE、NOT IN等否定操作通常无法使用索引:

-- 失效
SELECT * FROM customers WHERE NOT city IN ('北京', '上海');

表达式运算

对索引列进行数学运算或函数调用:

-- 失效:create_date为索引列
SELECT * FROM orders WHERE YEAR(create_date) = 2023;

数据量小

表数据量过小,全表扫描可能比使用索引更快。