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

4.2.2 MySQL索引原理以及SQL优化

文章目录

  • 4.2.2 MySQL索引原理以及SQL优化
    • 1. 索引与约束
      • 1. 索引是什么
      • 2. 索引的目的
      • 3. 几种索引
      • 4. 约束
        • 1.外键
        • 2. 约束 vs 索引的区别
      • 5. 索引实现
        • 1. 索引存储
        • 2. 页
        • 3. B+树
        • 4. B+树层高问题
        • 5. 自增id
        • 6. 聚集索引
        • 7. 辅助索引
      • 8. innnodb体系结构
        • 1. buffer pool
        • 2. change buffer
      • 9. 最左匹配原则
      • 10. 覆盖索引
      • 11. 索引下推
      • 12. 索引失效
      • 13. 索引原则
    • 2. sql比较慢怎么办
      • 1. 慢查询日志

4.2.2 MySQL索引原理以及SQL优化

1. 索引与约束

1. 索引是什么

  1. 索引是一种有序的数据结构,MySQL 中主要使用 B+ 树(InnoDB 引擎)来组织索引
  2. 它通过加快数据检索速度来提升数据库的查询效率
  3. 可理解为:数据库中的“目录”或“书的页码”
  4. 按照单个或者多个进行排序

2. 索引的目的

提升搜索效率

3. 几种索引

  1. 主键索引
    • 表的唯一标识
    • 不能为空 (NOT NULL) 且唯一 (UNIQUE)
    • 每张表只能有一个主键
    • 创建主键索引时,MySQL自动建立索引(底层通常是B+树)
-- 创建表时指定主键
CREATE TABLE users (id INT PRIMARY KEY,name VARCHAR(50)
);-- 或者先创建表,再加主键
ALTER TABLE users ADD PRIMARY KEY (id);
  1. 唯一索引
    • 保证列值唯一,但允许为空(特殊场景除外)
    • 不作为主键,可以有多个唯一索引
    • 通常用于:手机号、邮箱、身份证号等
-- 创建唯一索引
CREATE TABLE employees (emp_id INT,email VARCHAR(100) UNIQUE,phone VARCHAR(20),PRIMARY KEY (emp_id)
);-- 或者后期添加唯一索引
ALTER TABLE employees ADD UNIQUE (email);
  1. 普通索引
    • 仅加速查询速度
    • 没有唯一性要求,key可以重复
    • 可以为经常用作条件查询(WHERE)的列加普通索引
-- 创建普通索引
CREATE TABLE articles (id INT PRIMARY KEY,title VARCHAR(200),content TEXT
);-- 给 title 添加普通索引
CREATE INDEX idx_title ON articles(title);-- 或者这样
ALTER TABLE articles ADD INDEX (title);
  1. 组合索引
    • 一个索引包含多个列
    • 适合多列联合查询的场景
    • 遵循最左前缀原则(查询时条件必须从索引的最左列开始)
-- 创建组合索引
CREATE TABLE orders (order_id INT PRIMARY KEY,user_id INT,product_id INT,order_date DATE
);-- 给 (user_id, product_id) 建组合索引
CREATE INDEX idx_user_product ON orders(user_id, product_id);-- 查询时如果条件是 user_id,或 user_id + product_id,则可以用到索引
SELECT * FROM orders WHERE user_id = 123;SELECT * FROM orders WHERE user_id = 123 AND product_id = 456;-- 但如果单查 product_id,是用不了这个组合索引的
  1. 全文索引
    • 用于全文搜索
    • 适合大文本内容的搜索
    • 通常用于:文章、博客、产品描述等
-- 1. 创建表
CREATE TABLE blog_posts (id INT PRIMARY KEY AUTO_INCREMENT,title VARCHAR(255),content TEXT,FULLTEXT(title, content)
);-- 2. 插入数据
INSERT INTO blog_posts (title, content) VALUES
('MySQL Tutorial', 'Learn how to use MySQL database.'),
('Fulltext Search', 'Learn about fulltext search in MySQL.');-- 3. 搜索
SELECT * FROM blog_posts
WHERE MATCH(title, content) AGAINST('MySQL');
  1. 主键选择
    innodb 中表是索引组织表,每张表有且仅有一个主键;
    1. 如果显示设置 PRIMARY KEY ,则该设置的 key 为该表的主键;
    2. 如果没有显示设置,则从非空唯一索引中选择;
    3. 只有一个非空唯一索引,则选择该索引为主键;
    4. 有多个非空唯一索引,则选择声明的第一个为主键;
    5. 没有非空唯一索引,则自动生成一个 6 字节的 _rowid 作为主键;

4. 约束

InnoDB 本身提供对这些约束(PRIMARY KEY(主键约束),UNIQUE(唯一约束),NOT NULL(非空约束),FOREIGN KEY(外键约束),CHECK(检查约束))的支持,保证数据的正确性、安全性

1.外键
  1. 一个表中的字段依赖于另一个表的主键/唯一键;
  2. 保证两张表数据的关联完整性;
  3. 可以设置 级联操作(如删除/更新时一起变化)
-- 创建班级表
CREATE TABLE classes (class_id INT PRIMARY KEY,class_name VARCHAR(100)
);-- 创建学生表,并设置外键关联到班级表
CREATE TABLE students (student_id INT PRIMARY KEY,name VARCHAR(100),class_id INT,FOREIGN KEY (class_id) REFERENCES classes(class_id)
);
2. 约束 vs 索引的区别
项目约束索引
定义整性、合法性加速数据查询效率
主要目的保证正确性(不插错、不留空、不重复)提升性能(更快查找)
本质规则数据结构(如B+树)
关系主键约束、唯一约束会自动生成对应索引!索引不一定带有约束,单纯为了提速
示例NOT NULL、UNIQUE、PRIMARY KEY、FOREIGN KEYCREATE INDEX idx_name ON table(col)

5. 索引实现

1. 索引存储
  1. 索引存储的数据结构通常是 B+树,而不是哈希表
  2. 索引是磁盘上的有序结构,不是存在内存中的
2. 页
  1. innoDB 的数据存储以 页(Page) 为最小单位,每一页大小通常是 16KB。
  2. 一棵 B+ 树的每个节点对应一个或多个磁盘页
  3. 数据读写以页为单位进行(减少磁盘 I/O 次数)
3. B+树
  1. B+树是数据库默认的索引结构
  2. 每个节点存放有序的数据键值+指向子节点的指针
  3. 所有数据都存放在叶子节点
  4. 叶子节点之间有链表连接(范围查询快)
4. B+树层高问题
  1. 理想状态下,B+树的高度很低,一般在 2-4层
  2. 为什么?
    因为一页(16KB)能存很多索引项(假设一项占 16字节,1页能存1024项);所以即使存百万条数据,只要 2-3 次磁盘IO 就能找到,非常快!
5. 自增id
  1. 很多表喜欢用 自增ID(auto_increment) 作为主键。
  2. 自增ID的好处:
    插入数据总是追加到B+树的最右边;
    避免频繁分裂、重排;
    插入性能最好
    放心用,根本用不完
6. 聚集索引
  1. InnoDB 的每张表数据文件本身就是一棵 B+树,称为聚集索引。
  2. 主键索引就是数据本身
  3. 特点:
    按主键顺序存储;
    查找主键非常快;
    非主键(普通索引)存储的是【主键值】作为指针
7. 辅助索引
  1. 除了主键外,创建的其他索引,都是辅助索引
  2. 辅助索引的叶子节点,不直接存储数据行,而是存储【主键值】
  3. 查询时,先通过辅助索引找到主键,再通过主键去聚集索引找完整数据(回表)

总之,索引信息和数据信息的分层管理,便于高效的组织磁盘数据,快速实现单点和范围查询

8. innnodb体系结构

1. buffer pool

Buffer Pool 是 InnoDB 把磁盘上的数据页、索引页、插入缓冲(Change Buffer)、自适应哈希索引等缓存到内存中的区域。
目的是:减少磁盘 I/O,提高数据库访问速度。

特点

  1. 查询数据时优先从 Buffer Pool 取(命中则速度很快)
  2. 如果没有命中,才从磁盘读入,并加入 Buffer Pool(可能引发淘汰机制,比如 LRU)
  3. 包括脏页管理(数据被修改但未刷盘)机制
2. change buffer

Change Buffer 是 InnoDB 中专门为二级索引的插入、更新、删除操作设置的缓存区域,延迟将二级索引变更写入磁盘,从而减少磁盘 I/O。

**原理:

  1. 对于二级索引的插入/修改,不直接去磁盘更新,而是先记录到 Change Buffer。
  2. 之后在一定条件(比如页被读取进内存,或系统空闲时)才真正合并到磁盘上的二级索引页。

为什么只针对二级索引(非主键索引)?

  1. 因为主键索引(聚集索引)必须保证实时一致性。
  2. 二级索引允许延迟一致,所以可以先缓存在 Change Buffer。
CREATE TABLE user (id INT PRIMARY KEY,       -- 主键,主索引name VARCHAR(50),age INT,email VARCHAR(50),INDEX idx_name (name)      -- 二级索引
);
--id 是 主索引(一级索引):--叶子节点:存的是整行数据,比如 {id=1, name="张三", age=20, email="xx@xx.com"}--name 是 辅助索引(二级索引):--叶子节点:只存 {name="张三", id=1}--如果通过 name 查找,还需要根据 id 再去主索引回表拿到完整那一行。

一级索引(主索引):叶子节点存整行

二级索引(辅助索引):叶子节点存主键id,查询时需要回主键索引再拿数据

9. 最左匹配原则

组合索引在查询时,会优先用最左边的列开始匹配,从左到右连续匹配才能用上索引

CREATE INDEX idx_user_name_age ON users(name, age);SELECT * FROM users WHERE name = 'Tom';      --  用上索引
SELECT * FROM users WHERE name = 'Tom' AND age = 18; --  用上索引
SELECT * FROM users WHERE age = 18;           -- 用不了索引(跳过了最左的 name)

10. 覆盖索引

查询的数据只需要索引里的字段,不用回表到原表,因此速度更快

CREATE INDEX idx_name_age ON users(name, age);SELECT name, age FROM users WHERE name = 'Tom'; -- 覆盖索引
--因为 name 和 age 都在 idx_name_age 这个索引里,不需要回表SELECT name FROM users WHERE name = 'Tom';     -- 回表了

11. 索引下推

在索引遍历阶段就尽量筛选数据,减少回表次数,提升查询性能

SELECT * FROM users WHERE name LIKE 'Tom%' AND age = 18;
-- 假设没有索引下推,会先根据 name LIKE 'Tom%' 找到满足条件的索引项,然后再根据 age = 18 进行过滤。-- 而索引下推则是在索引遍历阶段就对 WHERE 条件进行筛选,减少回表次数。
--少了很多不必要的回表

12. 索引失效

一些不合理的 SQL 写法,会导致原本能用的索引失效,导致全表扫描

13. 索引原则

原则内容
最优选择尽量选择区分度高的列建立索引
组合优先多条件查询,建组合索引,遵循最左匹配
覆盖优先尽可能做到查询只用索引(覆盖索引)
更新慎用索引太多,更新、插入性能会变差
合理选择小表不建索引,大表必须优化索引
防止失效避免在索引列上做函数、运算、隐式转换

2. sql比较慢怎么办

1. 慢查询日志

  1. 慢查询日志是 MySQL 提供的一种日志记录机制,用于记录执行时间超过阈值的 SQL 语句。
  2. 可以通过配置来开启慢查询日志,设置阈值,然后查看日志文件来分析哪些 SQL 语句耗时。

相关文章:

  • LangChain构建大模型应用之RAG
  • java-mybatis01
  • 大模型时代的具身智能:从虚拟到现实的智能体进化革命
  • 用Python做有趣的AI项目 2:用 Python 和 NLTK 构建一个聊天机器人
  • 深度解析如何将图像帧和音频片段特征高效存储到向量数据库 Milvus
  • 深入浅出JVM - Java架构师面试实战
  • 有源晶振输出匹配电阻选择与作用详解
  • Spring security详细上手教学(三)密码管理
  • 如何把握邮件发送的黄金时间?
  • 北京工业大学25计专上岸经验分享
  • Qt 中 QSQLITE 和 QODBC 数据库连接的区别
  • Java 构造器
  • 基于Pytest接口自动化的requests模块项目实战以及接口关联方法详解
  • 稳扎稳打,25西电生命科学技术学院(考研录取情况)
  • Git命令(Gitee)
  • 10 DPSK原始对话记录
  • spring项目rabbitmq es项目启动命令
  • Node.js 应用部署:镜像体积优化与安全的多阶段构建探索
  • 神经编译革命:如何用脑机接口直接编程量子计算机?
  • Java求职者面试:从Spring Boot到微服务的技术深度探索
  • 湖州通告13批次不合格食品,盒马1批次多宝鱼甲硝唑超标
  • 网络游戏用户规模和市场销售创新高,知识产权保护面临哪些挑战?
  • 谢震业、梁小静等名将在列,世界田联接力赛中国队名单出炉
  • 强政神鸟——故宫里的乌鸦
  • 蜀道考古调查阶段性成果发布,新发现文物遗存297处
  • 咖啡与乳腺健康之间,究竟有着怎样的复杂关系?