索引夺命10连问,你能顶住第几问?

前言

今天我们来聊聊让无数开发者又爱又恨的------数据库索引

相信不少小伙伴在工作中都遇到过这样的场景:

  • 明明已经加了索引,为什么查询还是慢?
  • 为什么有时候索引反而导致性能下降?
  • 联合索引到底该怎么设计才合理?

别急,今天我就通过10个问题,带你彻底搞懂索引的奥秘!

希望对你会有所帮助。

最近准备面试的小伙伴,可以看一下这个宝藏网站(Java突击队):www.susan.net.cn,里面:面试八股文、场景设计题、面试真题、7个项目实战、工作内推什么都有

一、什么是索引?为什么需要索引?

1.1 索引的本质

简单来说,索引就是数据的目录

就像一本书的目录能帮你快速找到内容一样,数据库索引能帮你快速定位数据。

sql 复制代码
-- 没有索引的查询(全表扫描)
SELECT * FROM users WHERE name = '苏三'; -- 需要遍历所有记录

-- 有索引的查询(索引扫描)
CREATE INDEX idx_name ON users(name);
SELECT * FROM users WHERE name = '苏三'; -- 通过索引快速定位

1.2 索引的工作原理

索引的底层结构(B+树)

二、索引的10个常见问题

1.为什么我加了索引,查询还是慢?

场景还原

sql 复制代码
CREATE INDEX idx_name ON users(name);
SELECT * FROM users WHERE name LIKE '%苏三%'; -- 还是很慢!

原因分析

  1. 前导通配符LIKE '%苏三% 导致索引失效
  2. 索引选择性差:如果name字段大量重复,索引效果不佳
  3. 回表代价高:索引覆盖不全,需要回表查询

解决方案

sql 复制代码
-- 方案1:避免前导通配符
SELECT * FROM users WHERE name LIKE '苏三%';

-- 方案2:使用覆盖索引
CREATE INDEX idx_name_covering ON users(name, id, email);
SELECT name, id, email FROM users WHERE name LIKE '苏三%'; -- 不需要回表

-- 方案3:使用全文索引(对于文本搜索)
CREATE FULLTEXT INDEX ft_name ON users(name);
SELECT * FROM users WHERE MATCH(name) AGAINST('苏三');

2.索引是不是越多越好?

绝对不是! 索引需要维护代价:

sql 复制代码
-- 每个索引都会影响写性能
INSERT INTO users (name, email, age) VALUES ('苏三', 'susan@example.com', 30);
-- 需要更新: 
-- 1. 主键索引
-- 2. idx_name索引(如果存在)
-- 3. idx_email索引(如果存在)
-- 4. idx_age索引(如果存在)

索引的代价

  1. 存储空间:每个索引都需要额外的磁盘空间
  2. 写操作变慢:INSERT/UPDATE/DELETE需要维护所有索引
  3. 优化器负担:索引太多会增加查询优化器的选择难度

黄金法则:一般建议表的索引数量不超过5-7个

3.联合索引的最左前缀原则是什么?

最左前缀原则:联合索引只能从最左边的列开始使用

sql 复制代码
-- 创建联合索引
CREATE INDEX idx_name_age ON users(name, age);

-- 能使用索引的查询
SELECT * FROM users WHERE name = '苏三'; -- √ 使用索引
SELECT * FROM users WHERE name = '苏三' AND age = 30; -- √ 使用索引
SELECT * FROM users WHERE age = 30 AND name = '苏三'; -- √ 优化器会调整顺序

-- 不能使用索引的查询
SELECT * FROM users WHERE age = 30; -- × 不符合最左前缀

联合索引结构

4.如何选择索引字段的顺序?

选择原则

  1. 高选择性字段在前:选择性高的字段能更快过滤数据
  2. 经常查询的字段在前:优先满足常用查询场景
  3. 等值查询在前,范围查询在后
sql 复制代码
-- 计算字段选择性
SELECT 
    COUNT(DISTINCT name) / COUNT(*) as name_selectivity,
    COUNT(DISTINCT age) / COUNT(*) as age_selectivity,
    COUNT(DISTINCT city) / COUNT(*) as city_selectivity
FROM users;

-- 根据选择性决定索引顺序
CREATE INDEX idx_name_city_age ON users(name, city, age); -- name选择性最高

5.什么是覆盖索引?为什么重要?

覆盖索引:索引包含了查询需要的所有字段,不需要回表查询

sql 复制代码
-- 不是覆盖索引(需要回表)
CREATE INDEX idx_name ON users(name);
SELECT * FROM users WHERE name = '苏三'; -- 需要回表查询其他字段

-- 覆盖索引(不需要回表)
CREATE INDEX idx_name_covering ON users(name, email, age);
SELECT name, email, age FROM users WHERE name = '苏三'; -- 所有字段都在索引中

覆盖索引的优势

  1. 避免回表:减少磁盘IO
  2. 减少内存占用:只需要读取索引页
  3. 提升性能:查询速度更快

6.NULL值对索引有什么影响?

NULL值的问题

sql 复制代码
-- 创建索引
CREATE INDEX idx_email ON users(email);

-- 查询NULL值
SELECT * FROM users WHERE email IS NULL; -- 可能不使用索引
SELECT * FROM users WHERE email IS NOT NULL; -- 可能不使用索引

NULL值可能不使用索引。

解决方案

  1. 避免NULL值:设置默认值
  2. 使用函数索引(MySQL 8.0+)
sql 复制代码
-- 使用函数索引处理NULL值
CREATE INDEX idx_email_null ON users((COALESCE(email, '')));
SELECT * FROM users WHERE COALESCE(email, '') = '';

7.索引对排序和分组有什么影响?

索引优化排序和分组

sql 复制代码
-- 创建索引
CREATE INDEX idx_age_name ON users(age, name);

-- 索引优化排序
SELECT * FROM users ORDER BY age, name; -- √ 使用索引避免排序

-- 索引优化分组
SELECT age, COUNT(*) FROM users GROUP BY age; -- √ 使用索引优化分组

-- 无法使用索引排序的情况
SELECT * FROM users ORDER BY name, age; -- × 不符合最左前缀
SELECT * FROM users ORDER BY age DESC, name ASC; -- × 排序方向不一致

最近为了帮助大家找工作,专门建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。加苏三的微信:li_su223,备注:掘金+所在城市,即可进群。

8.如何发现索引失效的场景?

常见索引失效场景

  1. 函数操作WHERE YEAR(create_time) = 2023
  2. 类型转换WHERE phone = 13800138000(phone是varchar)
  3. 数学运算WHERE age + 1 > 30
  4. 前导通配符WHERE name LIKE '%苏三'

使用EXPLAIN分析

sql 复制代码
EXPLAIN SELECT * FROM users WHERE name = '苏三';

-- 查看关键指标:
-- type: const|ref|range|index|ALL(性能从好到坏)
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- Extra: Using index(覆盖索引)| Using filesort(需要排序)| Using temporary(需要临时表)

9.如何维护和优化索引?

定期索引维护

sql 复制代码
-- 查看索引使用情况(MySQL)
SELECT * FROM sys.schema_index_statistics 
WHERE table_schema = 'your_database' AND table_name = 'users';

-- 重建索引(优化索引碎片)
ALTER TABLE users REBUILD INDEX idx_name;

-- 分析索引使用情况
ANALYZE TABLE users;

索引监控

sql 复制代码
-- 开启索引监控(Oracle)
ALTER INDEX idx_name MONITORING USAGE;

-- 查看索引使用情况
SELECT * FROM v$object_usage WHERE index_name = 'IDX_NAME';

10.不同数据库的索引有什么差异?

MySQL vs PostgreSQL索引差异

特性 MySQL PostgreSQL
索引类型 B+Tree, Hash, Fulltext B+Tree, Hash, GiST, SP-GiST
覆盖索引 支持 支持(使用INCLUDE)
函数索引 8.0+支持 支持
部分索引 支持 支持
索引组织表 聚簇索引 堆表

PostgreSQL示例

sql 复制代码
-- 创建包含索引(Covering Index)
CREATE INDEX idx_users_covering ON users (name) INCLUDE (email, age);

-- 创建部分索引(Partial Index)
CREATE INDEX idx_active_users ON users (name) WHERE is_active = true;

-- 创建表达式索引(Expression Index)
CREATE INDEX idx_name_lower ON users (LOWER(name));

三、索引设计最佳实践

3.1 索引设计原则

  1. 按需创建:只为经常查询的字段创建索引
  2. 选择合适类型:根据场景选择B-Tree、Hash、全文索引等
  3. 考虑复合索引:使用复合索引减少索引数量
  4. 避免过度索引:每个索引都有维护成本
  5. 定期维护:重建索引,优化索引碎片

3.2 索引设计检查清单

四、实战案例:电商系统索引设计

4.1 用户表索引设计

sql 复制代码
-- 用户表结构
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    phone VARCHAR(20),
    age INT,
    city VARCHAR(50),
    created_at TIMESTAMP,
    is_active BOOLEAN
);

-- 推荐索引
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_city_age ON users(city, age);
CREATE INDEX idx_users_created ON users(created_at) WHERE is_active = true;

4.2 订单表索引设计

sql 复制代码
-- 订单表结构
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    status VARCHAR(20),
    amount DECIMAL(10,2),
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);

-- 推荐索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
CREATE INDEX idx_orders_created_amount ON orders(created_at, amount);

总结

  1. 理解原理:掌握B+树索引的工作原理和特性。
  2. 合理设计:遵循最左前缀原则,选择合适的索引顺序。
  3. 避免失效:注意索引失效的常见场景。
  4. 覆盖索引:尽可能使用覆盖索引减少回表。
  5. 定期维护:监控索引使用情况,定期优化重建。
  6. 权衡利弊:索引不是越多越好,要权衡查询性能和写成本。

好的索引设计是数据库性能的基石。

不要盲目添加索引,要基于实际查询需求和数据分布来科学设计。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙关注一下我的同名公众号:苏三说技术,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:进大厂,可以免费获取我最近整理的10万字的面试宝典,好多小伙伴靠这个宝典拿到了多家大厂的offer。

相关推荐
j***48541 小时前
idea创建SpringBoot自动创建Lombok无效果(解决)
spring boot·后端·intellij-idea
MaxHua1 小时前
彻底搞懂Spring AOP:概念与实战
java·后端·架构
aiopencode1 小时前
Fiddler抓包与接口调试实用指南,HTTPS配置、代理设置、API测试与性能优化全解析
后端
Y***89081 小时前
Spring Boot的项目结构
java·spring boot·后端
j***82702 小时前
【MyBatisPlus】MyBatisPlus介绍与使用
android·前端·后端
小华同学ai2 小时前
终于有人帮你整理好了,火爆的“系统级提示词”支持ChatGPT、Claude、Gemini、xAI的
前端·后端·github
z***89712 小时前
Flask框架中SQLAlchemy的使用方法
后端·python·flask
s***4532 小时前
Springboot-配置文件中敏感信息的加密:三种加密保护方法比较
java·spring boot·后端
HashTang2 小时前
一个人就是一支队伍:从 Next.js 到显示器,聊聊我的“全栈续航”方案
前端·后端·程序员