当前位置: 首页 > news >正文

MySQL学习笔记05:MySQL 索引原理与优化实战指南

🚀 学习系列:MySQL数据库深度学习实战
📅 更新时间:2025年9月25日
🎯 课时内容:MySQL索引原理与优化
💡 学习重点:B+树结构、索引类型、查询优化、性能调优
难度等级:⭐⭐⭐⭐☆

🎯 前言

索引是数据库性能优化的核心技术,也是MySQL面试中的高频考点。一个设计良好的索引可以将查询性能提升几十倍甚至上百倍,而不当的索引设计则可能拖累整个系统的性能。

本文将深入剖析MySQL索引的底层原理,从B+树存储结构到查询优化策略,为你构建完整的索引知识体系,助力面试和实战项目。


📚 本文内容

  1. B+树索引结构深度解析
  2. 索引类型详解
  3. 索引设计最佳实践
  4. 性能分析与优化
  5. 面试高频问题解析
  6. 实战优化案例
  7. 总结与最佳实践

🌳 B+树索引结构深度解析

为什么选择B+树?

在众多数据结构中,MySQL选择B+树作为索引结构绝非偶然,而是综合考虑了磁盘IO特性、查询效率和存储空间的最优解。

数据结构性能对比
性能提升
磁盘优化
范围查询优化
二叉查找树
O(log n)
IO次数: n
适合: 内存小数据
平衡二叉树
O(log n)
IO次数: log n
适合: 内存数据
B树
O(log n)
IO次数: log_m n
适合: 磁盘存储
B+树
O(log n)
IO次数: log_m n
适合: 数据库索引
哈希表
O(1)
IO次数: 1
适合: 等值查询

B+树的核心优势

1. 磁盘友好性
B+树 vs 二叉树的磁盘IO对比二叉树查找100万条数据:
┌─────────────────────────────────────────────┐
│  树的高度:log₂(1,000,000) ≈ 20层           │
│  磁盘IO次数:20次                           │
│  查询耗时:20 × 10ms = 200ms               │
└─────────────────────────────────────────────┘B+树查找100万条数据(假设每页存储1000个键):
┌─────────────────────────────────────────────┐
│  树的高度:log₁₀₀₀(1,000,000) ≈ 3层        │
│  磁盘IO次数:3次                            │
│  查询耗时:3 × 10ms = 30ms                 │
└─────────────────────────────────────────────┘性能提升:200ms → 30ms,提升约6.7倍!
2. 范围查询优化
范围查询过程
B+树范围查询优化
包含15
1. 定位到起始节点
包含15的节点
2. 沿链表顺序扫描
直到包含25的节点
3. 无需重复遍历树
根节点
10 | 20 | 30 | 40
叶子节点1
1-9
叶子节点2
10-19
叶子节点3
20-29
叶子节点4
30-39
3. 顺序访问性能
B+树扫描方式
B树扫描方式
中序遍历
回到上层
跳转
继续遍历
内部节点
只存索引
叶子节点1
存储数据
叶子节点2
存储数据
叶子节点3
存储数据
内部节点1
存储数据
内部节点2
存储数据
叶子节点1
叶子节点2

B+树优势:只需扫描叶子节点链表,顺序IO效率更高

B+树存储容量计算

三层B+树存储分析
-- 假设条件
页面大小:16KB = 16,384字节
主键:bigint = 8字节  
指针:6字节
叶子节点记录:假设平均1KB-- 非叶子节点(索引页)
每个索引项:8字节(主键) + 6字节(指针) = 14字节
每页索引项数量:16,384 / 141,170-- 叶子节点(数据页)  
每页记录数量:16,384 / 1,02416条记录-- 三层B+树存储容量计算
根节点:1页,1,170个指针
第二层:1,170页,每页1,170个指针 = 1,170 × 1,170 = 1,368,900个指针  
第三层:1,368,900页,每页16条记录 = 1,368,900 × 162,190万条记录

结论:三层B+树可以存储约2000万条记录,且只需要3次磁盘IO!

B+树分裂与合并

页面分裂过程
插入导致的页面分裂示例原始页面(已满):
┌─────────────────────────────────────────────┐
│  [1] [3] [5] [7] [9] [11] [13] [15] [17]    │
└─────────────────────────────────────────────┘插入新值:6
┌─────────────────────────────────────────────┐
│ 1. 页面已满,无法直接插入                   │
│ 2. 创建新页面                              │
│ 3. 将一半数据移动到新页面                   │
│ 4. 在父节点中添加新页面的索引               │
└─────────────────────────────────────────────┘分裂后:
页面1:[1] [3] [5] [6] [7]
页面2:[9] [11] [13] [15] [17]
父节点更新:添加指向页面2的指针,键值为9
页面合并过程
删除导致的页面合并示例删除前:
页面1:[1] [3] [5]     (利用率:60%)
页面2:[9] [11]        (利用率:40%,低于合并阈值)合并触发条件:页面利用率低于50%合并后:
页面1:[1] [3] [5] [9] [11]
页面2:被删除
父节点更新:删除指向页面2的指针

🔍 索引类型详解

聚簇索引 vs 非聚簇索引

聚簇索引(主键索引)
CREATE TABLE users (id INT PRIMARY KEY,          -- 聚簇索引name VARCHAR(50),age INT,email VARCHAR(100)
);

存储结构:

聚簇索引页面结构
┌─────────────────────────────────────────────┐
│              叶子节点                        │
├─────────────────────────────────────────────┤
│ ┌─────┬──────┬─────┬─────────────────────┐  │
│ │ ID  │ Name │ Age │       Email         │  │
│ ├─────┼──────┼─────┼─────────────────────┤  │
│ │  1  │ 张三 │ 25  │ zhang@email.com     │  │
│ │  2  │ 李四 │ 30  │ li@email.com        │  │
│ │  3  │ 王五 │ 28  │ wang@email.com      │  │
│ └─────┴──────┴─────┴─────────────────────┘  │
└─────────────────────────────────────────────┘特点:叶子节点包含完整的行数据
非聚簇索引(二级索引)
CREATE INDEX idx_name ON users(name);     -- 非聚簇索引
CREATE INDEX idx_age ON users(age);       -- 非聚簇索引

存储结构:

非聚簇索引页面结构
┌─────────────────────────────────────────────┐
│         idx_name 索引叶子节点                │
├─────────────────────────────────────────────┤
│ ┌──────┬─────────────┐                     │
│ │ Name │ Primary Key │                     │
│ ├──────┼─────────────┤                     │
│ │ 李四 │      2      │                     │
│ │ 王五 │      3      │                     │
│ │ 张三 │      1      │                     │
│ └──────┴─────────────┘                     │
└─────────────────────────────────────────────┘特点:叶子节点只包含索引列值和主键值
查询过程对比

主键查询(聚簇索引):

SELECT * FROM users WHERE id = 2;执行步骤:
1. 在聚簇索引中定位主键为2的记录
2. 直接返回完整行数据
总IO次数:2-3

二级索引查询(需要回表):

SELECT * FROM users WHERE name = '李四';执行步骤:
1. 在name索引中找到'李四'对应的主键值2
2. 使用主键值2在聚簇索引中查找完整行数据(回表)
3. 返回完整行数据
总IO次数:4-6

复合索引(联合索引)

最左前缀原则
CREATE INDEX idx_name_age_email ON users(name, age, email);

索引存储结构:

复合索引键值排序
┌─────────────────────────────────────────────┐
│ Name    │ Age │ Email           │ Primary Key│
├─────────┼─────┼─────────────────┼────────────┤
│ 李四    │ 25  │ li25@email.com  │     4      │
│ 李四    │ 30  │ li30@email.com  │     2      │
│ 王五    │ 28  │ wang@email.com  │     3      │
│ 张三    │ 25  │ zhang@email.com │     1      │
│ 张三    │ 35  │ zhang35@email.com│     5      │
└─────────┴─────┴─────────────────┴────────────┘排序规则:先按name排序,name相同时按age排序,age相同时按email排序
最左前缀原则应用
-- ✅ 可以使用索引的查询
SELECT * FROM users WHERE name = '张三';                    -- 使用 name
SELECT * FROM users WHERE name = '张三' AND age = 25;       -- 使用 name, age  
SELECT * FROM users WHERE name = '张三' AND age = 25 AND email = 'zhang@email.com'; -- 使用 name, age, email-- ❌ 无法使用索引的查询  
SELECT * FROM users WHERE age = 25;                         -- 跳过了name
SELECT * FROM users WHERE email = 'zhang@email.com';        -- 跳过了name, age
SELECT * FROM users WHERE name = '张三' AND email = 'zhang@email.com'; -- 跳过了age,只能使用name部分
索引优化案例
-- 场景:用户搜索功能
-- 需要支持按姓名、年龄、城市进行筛选-- ❌ 不良设计:每个字段单独建索引
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_age ON users(age);  
CREATE INDEX idx_city ON users(city);-- 问题:
SELECT * FROM users WHERE name = '张三' AND age = 25 AND city = '北京';
-- MySQL只能选择其中一个索引,其他条件需要在存储引擎层过滤-- ✅ 优化设计:根据查询频率建立复合索引
CREATE INDEX idx_name_age_city ON users(name, age, city);-- 优势:
-- 1. 一次索引查找解决所有条件
-- 2. 减少回表次数
-- 3. 提升查询性能数倍

覆盖索引

概念和优势

覆盖索引是指索引包含了查询所需的所有列,查询时无需回表访问数据页。

-- 创建覆盖索引
CREATE INDEX idx_name_age ON users(name, age);-- ✅ 使用覆盖索引的查询(无需回表)
SELECT name, age FROM users WHERE name = '张三';执行计划:
┌─────────────────────────────────────────────┐
│ 1. 在idx_name_age索引中查找name='张三'      │
│ 2. 直接从索引页返回name和age字段            │
│ 3. 无需访问聚簇索引(避免回表)             │
│ Total IO: 2-3次                            │
└─────────────────────────────────────────────┘-- ❌ 需要回表的查询
SELECT name, age, email FROM users WHERE name = '张三';执行计划:
┌─────────────────────────────────────────────┐
│ 1. 在idx_name_age索引中查找name='张三'      │
│ 2. 获取主键值                              │
│ 3. 根据主键在聚簇索引中查找完整记录(回表) │
│ 4. 返回name、age、email字段                │
│ Total IO: 4-6次                            │
└─────────────────────────────────────────────┘
覆盖索引优化案例
-- 业务场景:用户列表页面,显示用户名、年龄、状态
SELECT name, age, status FROM users WHERE status = 'active' LIMIT 20;-- ❌ 普通索引
CREATE INDEX idx_status ON users(status);
-- 问题:需要回表获取name和age字段-- ✅ 覆盖索引优化
CREATE INDEX idx_status_name_age ON users(status, name, age);
-- 优势:查询时直接从索引返回所有需要的字段,避免回表性能对比:
- 普通索引:需要回表20次,总IO约40-60- 覆盖索引:无需回表,总IO约3-5- 性能提升:10-20

索引下推优化

传统查询流程
SELECT * FROM users WHERE name LIKE '张%' AND age = 25;-- 假设有复合索引:INDEX(name, age)传统流程(无索引下推):
┌─────────────────────────────────────────────┐
│ 1. 存储引擎:在索引中找到name LIKE '张%'的记录│
│ 2. 存储引擎:返回主键给MySQL服务层          │
│ 3. MySQL服务层:根据主键回表获取完整记录    │
│ 4. MySQL服务层:检查age = 25条件            │
│ 5. MySQL服务层:返回符合条件的记录          │
└─────────────────────────────────────────────┘问题:即使索引包含age字段,也需要回表后才能过滤age条件
索引下推优化流程
-- 相同查询,启用索引下推优化后流程(Index Condition Pushdown):
┌─────────────────────────────────────────────┐
│ 1. 存储引擎:在索引中找到name LIKE '张%'的记录│
│ 2. 存储引擎:直接在索引中检查age = 25条件    │
│ 3. 存储引擎:只对同时满足两个条件的记录回表  │
│ 4. 存储引擎:返回完整记录给MySQL服务层      │
└─────────────────────────────────────────────┘优势:
- 减少回表次数
- 降低数据传输量  
- 提升查询性能
索引下推示例
-- 查看索引下推状态
SHOW VARIABLES LIKE 'optimizer_switch';
-- 确认index_condition_pushdown=on-- 示例查询
EXPLAIN SELECT * FROM users WHERE name LIKE '张%' AND age BETWEEN 20 AND 30;-- 执行计划中显示"Using index condition"表示使用了索引下推

⚙️ 索引设计最佳实践

索引设计原则

1. 选择性原则

索引的选择性越高,过滤效果越好。选择性 = 不重复值数量 / 总记录数

-- 计算字段选择性
SELECT COUNT(DISTINCT name) / COUNT(*) as name_selectivity,COUNT(DISTINCT age) / COUNT(*) as age_selectivity,COUNT(DISTINCT gender) / COUNT(*) as gender_selectivity
FROM users;结果示例:
┌─────────────────┬────────────────┬──────────────────┐
│ name_selectivity│ age_selectivity│ gender_selectivity│
├─────────────────┼────────────────┼──────────────────┤
│     0.950.150.5         │
└─────────────────┴────────────────┴──────────────────┘建议:
- name字段选择性高(0.95),适合建索引
- age字段选择性一般(0.15),可作为复合索引的一部分
- gender字段选择性低(0.5),不适合单独建索引
2. 频率原则

优先为高频查询字段建立索引

-- 分析慢查询日志,识别高频查询模式
SELECT sql_text, exec_count, avg_timer_wait 
FROM performance_schema.events_statements_summary_by_digest 
ORDER BY exec_count DESC LIMIT 10;-- 根据查询频率设计索引
-- 高频查询:SELECT * FROM users WHERE email = ?
CREATE INDEX idx_email ON users(email);-- 中频查询:SELECT * FROM users WHERE city = ? AND age > ?  
CREATE INDEX idx_city_age ON users(city, age);
3. 长度原则

对于字符串字段,使用前缀索引可以节省空间

-- 分析字符串字段的前缀长度选择性
SELECT COUNT(DISTINCT LEFT(email, 5)) / COUNT(*) as prefix_5,COUNT(DISTINCT LEFT(email, 10)) / COUNT(*) as prefix_10,COUNT(DISTINCT LEFT(email, 15)) / COUNT(*) as prefix_15,COUNT(DISTINCT email) / COUNT(*) as full_column
FROM users;-- 根据分析结果选择合适的前缀长度
CREATE INDEX idx_email_prefix ON users(email(10));优势:
- 减少索引存储空间
- 提升索引页面利用率
- 降低维护成本注意:前缀索引无法用于ORDER BYGROUP BY

索引维护策略

1. 索引监控
-- 查看索引使用情况
SELECT table_schema,table_name, index_name,cardinality,sub_part,nullable
FROM information_schema.statistics 
WHERE table_schema = 'your_database';-- 查看未使用的索引
SELECT s.table_schema,s.table_name,s.index_name 
FROM information_schema.statistics s
LEFT JOIN performance_schema.table_io_waits_summary_by_index_usage t ON s.table_schema = t.object_schema AND s.table_name = t.object_name AND s.index_name = t.index_name
WHERE t.index_name IS NULL AND s.index_name != 'PRIMARY';
2. 索引重建
-- 检查索引碎片率
SELECT table_name,index_name,ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) AS size_mb
FROM mysql.innodb_index_stats 
WHERE stat_name = 'size';-- 重建索引
ALTER TABLE users DROP INDEX idx_name, ADD INDEX idx_name (name);-- 或者使用optimize table(会锁表)
OPTIMIZE TABLE users;
3. 在线DDL操作
-- MySQL 5.6+支持在线添加索引
ALTER TABLE users ADD INDEX idx_phone (phone), ALGORITHM=INPLACE, LOCK=NONE;-- 参数说明:
-- ALGORITHM=INPLACE:就地修改,不创建临时表
-- LOCK=NONE:不锁表,允许并发读写

📊 性能分析与优化

索引性能分析工具

1. EXPLAIN分析
EXPLAIN SELECT * FROM users WHERE name = '张三' AND age = 25;关键字段解释:
┌─────────────┬────────────────────────────────────────┐
│    字段     │                 含义                   │
├─────────────┼────────────────────────────────────────┤
│ select_type │ SIMPLE/PRIMARY/SUBQUERY等              │
│ table       │ 涉及的表名                             │
│ type        │ 访问类型:const > eq_ref > ref > range │
│ possible_keys│ 可能使用的索引                        │
│ key         │ 实际使用的索引                         │
│ key_len     │ 索引长度                               │
│ ref         │ 索引比较的列                           │
│ rows        │ 估算扫描行数                           │
│ Extra       │ 额外信息                               │
└─────────────┴────────────────────────────────────────┘
2. 性能监控指标
-- 查看索引统计信息
SHOW INDEX FROM users;-- 查看表的IO统计
SELECT * FROM performance_schema.table_io_waits_summary_by_table 
WHERE object_schema = 'your_database';-- 关键指标:
-- 1. 索引选择性:cardinality值
-- 2. 索引大小:index_length
-- 3. 查询频率:count_read, count_write
-- 4. 平均延迟:avg_timer_wait

常见性能问题及解决方案

1. 索引失效问题
-- ❌ 导致索引失效的情况-- 函数操作
SELECT * FROM users WHERE UPPER(name) = 'ZHANG';  
-- 解决:CREATE INDEX idx_name_upper ON users((UPPER(name)));-- 类型不匹配
SELECT * FROM users WHERE phone = 123456789;  -- phone字段是varchar
-- 解决:SELECT * FROM users WHERE phone = '123456789';-- 前导模糊查询
SELECT * FROM users WHERE name LIKE '%张%';
-- 解决:使用全文索引或ElasticSearch-- 负向查询
SELECT * FROM users WHERE age != 25;
-- 解决:改写为正向查询或使用其他条件-- OR连接不同字段
SELECT * FROM users WHERE name = '张三' OR age = 25;
-- 解决:拆分为两个查询或使用UNION
2. 索引选择问题
-- 当表有多个可用索引时,MySQL的选择策略EXPLAIN SELECT * FROM users WHERE name = '张三' AND age = 25;-- 如果存在索引:
-- INDEX idx_name (name)
-- INDEX idx_age (age)  
-- INDEX idx_name_age (name, age)-- MySQL会选择最优的idx_name_age索引-- 强制使用特定索引(一般不推荐)
SELECT * FROM users USE INDEX(idx_name) WHERE name = '张三' AND age = 25;
3. 大表索引优化
-- 对于千万级数据表的索引策略-- 1. 分区表索引
CREATE TABLE user_logs (id BIGINT PRIMARY KEY,user_id INT,create_time DATETIME,content TEXT,INDEX idx_user_time (user_id, create_time)
) PARTITION BY RANGE (YEAR(create_time)) (PARTITION p2020 VALUES LESS THAN (2021),PARTITION p2021 VALUES LESS THAN (2022),PARTITION p2022 VALUES LESS THAN (2023)
);-- 2. 读写分离索引策略
-- 主库:只保留必要的写入相关索引
-- 从库:根据查询需求建立更多的查询索引-- 3. 异步索引维护
-- 在业务低峰期进行索引重建和优化

🎯 面试高频问题解析

核心面试题汇总

1. MySQL的索引类型有哪些?

标准答案:

  • 按存储结构分:B+树索引(InnoDB默认)、哈希索引(Memory引擎)、全文索引
  • 按应用层次分:主键索引(聚簇索引)、普通索引、唯一索引、复合索引
  • 按键值特性分:主键索引、唯一索引、普通索引、前缀索引
2. 为什么MySQL选择使用B+树作为索引结构?

详细解答:

B+树的四大优势:1. 磁盘友好性:- 高扇出度,减少树的高度- 减少磁盘IO次数(关键因素)2. 范围查询效率:- 叶子节点链表连接- 支持高效的范围扫描3. 稳定的查询性能:- 所有数据都在叶子节点- 查询路径长度相同4. 顺序访问优化:- 叶子节点有序链表- 适合数据库的顺序读取场景
3. MySQL索引的最左前缀匹配原则是什么?

原理解释:

-- 复合索引:INDEX(a, b, c)
-- 索引内部按 a -> b -> c 的顺序排序-- ✅ 能使用索引的查询
WHERE a = 1                    -- 使用a
WHERE a = 1 AND b = 2          -- 使用a,b  
WHERE a = 1 AND b = 2 AND c = 3 -- 使用a,b,c
WHERE a = 1 AND c = 3          -- 使用a(跳过b,c无法使用)-- ❌ 无法使用索引的查询
WHERE b = 2                    -- 跳过了a
WHERE c = 3                    -- 跳过了a,b
WHERE b = 2 AND c = 3          -- 跳过了a
4. MySQL中的回表是什么?

概念和影响:

回表过程:
1. 在二级索引中找到符合条件的记录
2. 获取记录中的主键值
3. 根据主键值到聚簇索引中查找完整行数据
4. 返回查询结果性能影响:
- 增加磁盘IO次数
- 降低查询效率
- 在大结果集时影响更明显避免回表的方法:
- 使用覆盖索引
- 优化查询只返回必要字段
- 合理设计复合索引
5. MySQL中使用索引一定有效吗?

详细分析:

-- 索引可能无效的场景-- 1. 数据量太小
SELECT * FROM small_table WHERE id = 1;
-- 全表扫描可能比索引查找更快-- 2. 选择性太低
SELECT * FROM users WHERE gender = 'male';  
-- 如果男性占90%,索引效果很差-- 3. 查询结果集太大
SELECT * FROM users WHERE age > 18;
-- 如果返回90%的记录,全表扫描更高效-- 4. 索引维护成本
-- 大量的INSERT/UPDATE/DELETE操作
-- 索引维护开销可能超过查询收益

进阶面试题

MySQL三层B+树能存多少数据?

计算过程:

假设:
- 页面大小:16KB
- 主键:bigint(8字节)
- 指针:6字节  
- 行数据:1KB计算:
非叶子节点每页索引项:16KB ÷ 14字节 ≈ 1170个
叶子节点每页记录数:16KB ÷ 1KB = 16条三层B+树容量:
第一层:1个根节点
第二层:1170个节点
第三层:1170 × 1170 = 1,368,900个叶子节点
总记录数:1,368,900 × 16 ≈ 2190万条结论:三层B+树约可存储2000万条记录

🚀 实战优化案例

案例一:电商订单查询优化

业务场景
-- 订单表结构
CREATE TABLE orders (id BIGINT PRIMARY KEY,user_id INT,order_status TINYINT,create_time DATETIME,total_amount DECIMAL(10,2),-- 其他字段...
);-- 高频查询
-- 1. 用户查看自己的订单
SELECT * FROM orders WHERE user_id = ? ORDER BY create_time DESC;-- 2. 管理员查看某状态的订单
SELECT * FROM orders WHERE order_status = ? AND create_time > ?;-- 3. 统计查询
SELECT COUNT(*), SUM(total_amount) FROM orders WHERE create_time BETWEEN ? AND ?;
索引设计方案
-- 方案一:按查询频率设计
CREATE INDEX idx_user_time ON orders(user_id, create_time);      -- 用户订单查询
CREATE INDEX idx_status_time ON orders(order_status, create_time); -- 管理查询
CREATE INDEX idx_create_time ON orders(create_time);             -- 统计查询-- 方案二:覆盖索引优化
CREATE INDEX idx_user_time_status_amount ON orders(user_id, create_time, order_status, total_amount);优化效果:
- 减少回表操作
- 支持多种查询模式
- 一个索引覆盖多个业务场景

案例二:社交媒体动态查询优化

业务场景
-- 动态表结构
CREATE TABLE posts (id BIGINT PRIMARY KEY,user_id INT,content TEXT,like_count INT DEFAULT 0,create_time DATETIME,is_deleted TINYINT DEFAULT 0,INDEX idx_user_time (user_id, create_time),INDEX idx_create_time (create_time)
);-- 核心查询:获取用户关注的人的最新动态
SELECT p.* FROM posts p 
WHERE p.user_id IN (SELECT following_id FROM user_follows WHERE user_id = ?)AND p.is_deleted = 0AND p.create_time > DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY p.create_time DESC 
LIMIT 20;
优化策略
-- 1. 索引优化
CREATE INDEX idx_user_deleted_time ON posts(user_id, is_deleted, create_time);-- 2. 查询重写
-- 原查询使用子查询,改为JOIN
SELECT p.* FROM posts p
INNER JOIN user_follows f ON p.user_id = f.following_id
WHERE f.user_id = ?AND p.is_deleted = 0  AND p.create_time > DATE_SUB(NOW(), INTERVAL 7 DAY)
ORDER BY p.create_time DESC
LIMIT 20;-- 3. 分页优化
-- 避免深度分页的LIMIT offset问题
SELECT p.* FROM posts p
WHERE p.id < ?  -- 使用上次查询的最小IDAND p.user_id IN (...)AND p.is_deleted = 0
ORDER BY p.id DESC
LIMIT 20;

📈 总结与最佳实践

索引设计核心原则

  1. 理解业务查询模式:分析高频查询,针对性设计索引
  2. 遵循最左前缀原则:复合索引字段顺序要符合查询条件
  3. 考虑覆盖索引:减少回表操作,提升查询性能
  4. 控制索引数量:过多索引影响写入性能
  5. 定期监控和优化:清理无用索引,重建碎片索引

性能优化策略

优化维度策略效果
查询优化使用覆盖索引、避免回表减少IO,提升2-5倍性能
索引设计复合索引、最左前缀一个索引支持多种查询
存储优化前缀索引、合理字段类型节省空间,提升缓存命中率
维护优化定期重建、清理无用索引保持索引健康度

面试准备要点

  1. 原理理解:B+树结构、索引类型、查询过程
  2. 实战经验:优化案例、性能分析、问题解决
  3. 最佳实践:设计原则、维护策略、监控方法
  4. 工具使用:EXPLAIN分析、性能监控、优化技巧

通过深入理解MySQL索引的原理和实践,你将能够设计出高效的数据库方案,解决复杂的性能问题,并在面试中展现出扎实的技术功底。


🚀 下期预告:MySQL事务与锁机制 - ACID特性到死锁处理的完整解析

http://www.dtcms.com/a/409941.html

相关文章:

  • 【提示工程】Ch2(续)-提示技术(Prompt Technique)
  • 嵌入式软件知识点汇总(day2)
  • QT中QStackedWidget控件功能及应用
  • 网络爬虫(上)
  • 论文精读(六):微服务系统服务依赖发现技术综述
  • 农业推广网站建设企业商城网站建设价格
  • 教师做班级网站手机网站打开微信号
  • 司法审计师:在数字与法律之间行走的“侦探”
  • google drive 怎么断点续传下载?
  • 基于STM32单片机的温湿度臭氧二氧化碳检测OneNET物联网云平台设计
  • LeetCode 面试经典 150_哈希表_快乐数(45_202_C++_简单)(哈希表;快慢指针)
  • K8S部署的ELK分片问题解决,报错:unexpected error while indexing monitoring document
  • Atlas Mapper 教程系列 (7/10):单元测试与集成测试
  • 众智FlagOS 1.5发布:统一开源大模型系统软件栈,更全面、AI赋能更高效
  • 理解 mvcc
  • 【网络编程】TCP 粘包处理:手动序列化反序列化与报头封装的完整方案
  • 数据库MVCC
  • 如何用AI工具开发一个轻量化CRM系统(七):AI生成pytest测试脚本
  • qData:一站式开源数据中台
  • 国外中文网站排行在线图片编辑网站源码
  • [数据结构]优先级队列
  • ARM内部寄存器
  • Laravel + UniApp AES加密/解密
  • 5G开户时切片配置参数详解
  • 面向新质生产力,职业院校“人工智能”课程教学解决方案
  • wap网站如何做福建外贸网站
  • ElasticSearch-提高篇
  • 第6篇、Flask 表单处理与用户认证完全指南:从零到实战
  • Visual Studio 2013 Update 4 中文版安装步骤(带TFS支持)附安装包​
  • 珠海 网站建设注册安全工程师题库