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

开始改变第六天 MySQL(2)

MySQL索引深度解析:从原理到实战优化

一、为什么需要索引?

想象一下,如果没有索引,数据库查询就像在一本没有目录的厚书中查找特定内容,只能一页页翻看。而索引就像书的目录,让我们能够快速定位到需要的数据。

二、MySQL索引底层原理

B+树索引结构

B+树示例:
根节点: [指针1 | 键值20 | 指针2 | 键值40 | 指针3]/        |         \/         |          \
非叶节点       非叶节点       非叶节点
[5|10|15]    [25|30|35]    [45|50|55]/  \         /   \         /   \
叶子节点     叶子节点      叶子节点
[1,2,3,4] [6,7,8,9] [11,12,13,14]...

B+树的核心优势

  1. 所有数据都在叶子节点,查询路径长度固定
  2. 叶子节点形成双向链表,便于范围查询
  3. 非叶子节点只存索引,可以缓存更多数据
  4. 树高更低,减少磁盘I/O次数

三、索引类型详解

1. 聚簇索引(Clustered Index)

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

特点:

  • 叶子节点存储完整行数据
  • 每张表只能有一个聚簇索引
  • 物理存储顺序与索引顺序一致

2. 二级索引(Secondary Index)

-- 创建二级索引
CREATE INDEX idx_name ON users(name);
CREATE INDEX idx_age ON users(age);

特点:

  • 叶子节点存储主键值
  • 查询时需要"回表"操作
  • 每张表可以有多个二级索引

四、索引创建实战指南

单列索引 vs 复合索引

-- 单列索引:适合单个条件查询
CREATE INDEX idx_email ON users(email);-- 复合索引:适合多条件查询
CREATE INDEX idx_name_age ON users(name, age);
CREATE INDEX idx_name_age_city ON users(name, age, city);

复合索引的最左前缀原则

-- 索引: (name, age, city)-- 使用索引的情况:
SELECT * FROM users WHERE name = '张三';
SELECT * FROM users WHERE name = '张三' AND age = 25;
SELECT * FROM users WHERE name = '张三' AND age = 25 AND city = '北京';-- 不使用索引的情况:
SELECT * FROM users WHERE age = 25;                    -- 违反最左前缀
SELECT * FROM users WHERE age = 25 AND city = '北京';   -- 违反最左前缀
SELECT * FROM users WHERE city = '北京';                -- 违反最左前缀

索引选择性原则

-- 计算索引选择性
SELECT COUNT(DISTINCT gender) / COUNT(*) as gender_selectivity,    -- 选择性低COUNT(DISTINCT email) / COUNT(*) as email_selectivity       -- 选择性高
FROM users;-- 选择性 > 0.1 的列适合建立索引

五、回表查询与覆盖索引

回表查询过程

-- 假设有二级索引 idx_name
SELECT * FROM users WHERE name = '张三';-- 执行流程:
-- 1. 在idx_name索引树中找到'张三'对应的主键id
-- 2. 用主键id到聚簇索引中查找完整数据行
-- 3. 返回完整数据
-- 这个过程就是"回表"

覆盖索引优化

-- 创建覆盖索引
CREATE INDEX idx_name_age ON users(name, age);-- 覆盖索引查询:直接从索引获取数据,避免回表
SELECT name, age FROM users WHERE name = '张三';-- 检查是否使用覆盖索引
EXPLAIN SELECT name, age FROM users WHERE name = '张三';
-- 在Extra列看到: Using index

覆盖索引扩展应用

-- 创建包含更多字段的覆盖索引
CREATE INDEX idx_name_age_city ON users(name, age, city);-- 以下查询都可以使用覆盖索引
SELECT name FROM users WHERE name = '张三';
SELECT name, age FROM users WHERE name = '张三';
SELECT name, age, city FROM users WHERE name = '张三';
SELECT COUNT(*) FROM users WHERE name = '张三';

六、索引失效的完整场景分析

1. 违反最左前缀原则

-- 索引: (name, age, city)
SELECT * FROM users WHERE age = 25;                    -- ❌ 失效
SELECT * FROM users WHERE city = '北京';                -- ❌ 失效
SELECT * FROM users WHERE name = '张三' AND city = '北京'; -- ✅ 部分使用

2. 在索引列上使用函数或计算

-- 索引: (create_time)
SELECT * FROM users WHERE YEAR(create_time) = 2023;    -- ❌ 失效
SELECT * FROM users WHERE create_time + 1 > NOW();     -- ❌ 失效-- 优化方案:
SELECT * FROM users WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01'; -- ✅

3. 类型转换导致失效

-- 假设phone是varchar类型
SELECT * FROM users WHERE phone = 13800138000;         -- ❌ 失效(数字转字符串)
SELECT * FROM users WHERE phone = '13800138000';       -- ✅ 使用索引-- 假设id是int类型
SELECT * FROM users WHERE id = '100';                  -- ✅ 使用索引(字符串转数字)

4. LIKE模糊查询

-- 索引: (name)
SELECT * FROM users WHERE name LIKE '%张三%';           -- ❌ 失效
SELECT * FROM users WHERE name LIKE '%张三';            -- ❌ 失效
SELECT * FROM users WHERE name LIKE '张三%';            -- ✅ 使用索引

5. 使用不等于条件

SELECT * FROM users WHERE status != 1;                 -- ❌ 可能全表扫描
SELECT * FROM users WHERE status <> 1;                 -- ❌ 可能全表扫描-- 优化方案:
SELECT * FROM users WHERE status IN (0, 2, 3);         -- ✅ 使用索引
SELECT * FROM users WHERE status > 1 OR status < 1;    -- ✅ 使用索引

6. OR条件处理

-- 假设name有索引,age无索引
SELECT * FROM users WHERE name = '张三' OR age = 25;   -- ❌ 索引失效-- 优化方案:
SELECT * FROM users WHERE name = '张三'
UNION ALL
SELECT * FROM users WHERE age = 25 AND name != '张三'; -- ✅ 分别使用索引

7. 范围查询后的索引列失效

-- 索引: (name, age, city)
SELECT * FROM users WHERE name = '张三' AND age > 20 AND city = '北京';
-- ✅ name使用索引,✅ age使用索引,❌ city无法使用索引-- 优化方案:调整索引顺序或拆分为多个查询
CREATE INDEX idx_name_city_age ON users(name, city, age);

七、索引设计最佳实践

1. 选择合适的索引列

-- 优先为以下列创建索引:
-- 1. WHERE条件中的列
-- 2. JOIN关联条件的列  
-- 3. ORDER BY排序的列
-- 4. GROUP BY分组的列

2. 复合索引设计技巧

-- 设计原则:等值查询列在前,范围查询列在后
CREATE INDEX idx_status_create_time ON orders(status, create_time);-- 查询:等值 + 范围
SELECT * FROM orders WHERE status = 1 AND create_time > '2023-01-01';

3. 前缀索引优化

-- 对于长文本列,使用前缀索引
CREATE INDEX idx_content ON articles(content(100));  -- 只索引前100个字符-- 计算合适的前缀长度
SELECT COUNT(DISTINCT LEFT(content, 50)) / COUNT(*) as selectivity_50,COUNT(DISTINCT LEFT(content, 100)) / COUNT(*) as selectivity_100,COUNT(DISTINCT LEFT(content, 200)) / COUNT(*) as selectivity_200
FROM articles;

八、索引性能监控与分析

使用EXPLAIN分析查询

EXPLAIN SELECT * FROM users WHERE name = '张三' AND age = 25;-- 重点关注:
-- type: const > ref > range > index > ALL
-- key: 实际使用的索引
-- rows: 预估扫描行数
-- Extra: Using index(覆盖索引), Using where, Using filesort, Using temporary

索引使用情况统计

-- 查看索引使用频率
SELECT OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME,COUNT_READ,COUNT_FETCH
FROM performance_schema.table_io_waits_summary_by_index_usage
ORDER BY COUNT_READ DESC;-- 查找未使用的索引
SELECT * FROM sys.schema_unused_indexes;

九、实际案例分析

电商系统索引设计

-- 订单表索引设计
CREATE TABLE orders (id BIGINT PRIMARY KEY,user_id BIGINT,status TINYINT,create_time DATETIME,total_amount DECIMAL(10,2),-- 其他字段...INDEX idx_user_status (user_id, status),INDEX idx_create_time (create_time),INDEX idx_status_create (status, create_time)
);-- 商品表索引设计  
CREATE TABLE products (id BIGINT PRIMARY KEY,category_id INT,price DECIMAL(10,2),name VARCHAR(200),-- 其他字段...INDEX idx_category_price (category_id, price),INDEX idx_name (name)
);

总结

索引是数据库性能优化的核心,正确的索引设计可以提升查询性能几个数量级。记住以下要点:

  1. 理解B+树原理:知道索引如何工作才能更好使用
  2. 掌握最左前缀原则:复合索引设计的基石
  3. 善用覆盖索引:避免回表,提升性能
  4. 警惕索引失效:避免常见的索引使用陷阱
  5. 持续监控优化:根据实际查询模式调整索引策略

索引不是越多越好,合适的索引才是最好的索引。在实际应用中,要根据具体的业务场景和查询模式来制定索引策略。

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

相关文章:

  • 如何使用wordpress搭建网站广告咨询
  • 网站建设那个好北京大兴专业网站建设公司
  • 基于 GEE 的融合 MODIS 地表反射率、MCD12Q1 土地覆盖与 TERRACLIMATE 气候数据的研究区净初级生产力(NPP)计算方法研究
  • markdown-it
  • vector 的扩容机制
  • part1~2 神经网络基础
  • SQL注入过滤绕过fuzz字典
  • CH32 WCH-LINK -Error: Failed to Open WCH-Link.
  • 构建AI智能体:七十九、从SVD的理论到LoRA的实践:大模型低秩微调的内在逻辑
  • Blackwell GPU提供LLVM和MLIR支持的相关工作 报告
  • 宁波网站开发建设网上做娱乐广告的网站
  • 浙江制造品牌建设网站做微信网站公司名称
  • Babylon.js中ArcRotateCamera.interpolateTo 方法使用备忘
  • 【OD刷题笔记】- CPU算力分配
  • iOS 抓包工具有哪些,开发者的选型与实战指南
  • 测试过程涉及python自动化及其他相关面试问题汇总
  • 免费网站建设讯息全站加速 wordpress
  • 哪里网站建设公司比较好网站建设销售工作职责
  • 推荐一款免费的语音识别网站,上传音频即可
  • 笔记C++语言,太焦虑了
  • 分公司一般做网站吗音乐网站建设目标
  • Java 21 虚拟线程 vs 缓存线程池与固定线程池
  • 在线开发培训网站建设小型餐饮店面设计
  • ZYNQ USB按键读写操作详解:从裸机到Linux系统的完整实现
  • 如何在Windows桌面实现自由悬浮计时?
  • BEV环视感知算法从环境部署开始
  • 看上去高端的网站深圳培训学校
  • 狂飙与重构:机器人IPO浪潮背后的系统焦虑与感知进化
  • 21.静态NAT
  • 做头像的网站wordpress拖拽式