目录

  • 一、索引概述
  • 二、索引结构
    • (1) 不同类型的索引结构
    • (2) 二叉树和红黑树
    • (3) B 树
    • (4) B+树
    • (5) Hash
    • (6) 为什么InnoDB存储引擎选择使用B+tree索引结构相对于二叉树,层级更少,搜索效率高;
  • 三、索引的分类
    • (1) 聚集索引和二级索引
    • (2) 思考题
  • 四、索引 SQL 语法
    • (1) 表
    • (2) 索引 SQL
  • 五、SQL 性能分析(查看执行频次)
  • 六、SQL 性能分析(慢查询日志)
  • 七、SQL 性能分析(profiles )
  • 八、SQL 性能分析(explain)
  • 九、最左前缀法则和范围查询
    • (1) 最左前缀法则
    • (2) 范围查询
  • 十、索引失效情况
    • (1) 索引列运算
    • (2) 字符串不加单引号
    • (3) 头部模糊匹配查询
    • (4) or 连接的条件
    • (5) 数据分布影响
  • 十一、索引使用原则
    • (1) 索引提示
    • (2) 覆盖索引
    • (3) 前缀索引
    • (4) 单列索引与联合索引
  • 十二、索引设计原则

一、索引概述

  • 索引(index) 是帮助 MySQL 高效获取数据的数据结构(有序)

  • 在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据, 这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引


二、索引结构

(1) 不同类型的索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的索引结构,主要包含以下几种:

以下是不同的存储引擎对于索引结构的支持情况:


(2) 二叉树和红黑树

  • 顺序插入时,会形成一个链表,查询性能大大降低。
  • 大数据量情况下,层级较深,检索速度慢

为解决 顺序插入时,会形成一个链表,查询性能大大降低 的情况,可以选择红黑树(红黑树是一颗自平衡二叉树,即使是顺序插入数据,最终形成的数据结构也是一颗平衡的二叉树)

由于红黑树也是一颗二叉树,所以也会存在大数据量情况下,层级较深,检索速度慢的缺点。

(3) B 树

  • B 树是一种多路平衡查找树,相对于二叉树,B树每个节点可以有多个分支,即多叉。

  • 以一颗最大度数(max-degree)为5的 B 树为例(这个B树每个节点最多存储4个key,5个指针)

  • 5阶的B树,每一个节点最多存储4个key,对应5个指针。
  • 一旦节点存储的key数量到达5,就会裂变,中间元素向上分裂。
  • 在B树中,非叶子节点和叶子节点都会存放数据。

(4) B+树

  • B+Tree是B树的变种

  • 绿色框框起来的部分,是索引部分,仅仅起到索引数据的作用,不存储数据
  • 红色框框起来的部分,是数据存储部分,在其叶子节点中要存储具体的数据

  • 所有的数据都会出现在叶子节点
  • 叶子节点形成一个单向链表。
  • 非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的

MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序。

(5) Hash

  • MySQL中除了支持B+Tree索引,还支持一种索引类型 — Hash索引

  • 哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中

  • Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,…)
  • 无法利用索引完成排序操作
  • 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索引

(6) 为什么InnoDB存储引擎选择使用B+tree索引结构相对于二叉树,层级更少,搜索效率高;

  • 相对于二叉树,层级更少,搜索效率高
  • 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
  • 相对Hash索引,B+tree支持范围匹配及排序操作

三、索引的分类

(1) 聚集索引和二级索引

  • 在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。

  • 在 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种:

聚集索引选取规则:

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
  • 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引

(2) 思考题

以下两条SQL语句,那个执行效率高" />A.select * from user where id = 10 ;B.select * from user where name = 'Arm' ;# id为主键,name字段创建的有索引

  • A 语句的执行性能要高于B 语句
  • 因为A语句直接走聚集索引,直接返回数据。 而B语句需要先查询name字段的二级索引,然后再查询聚集索引,也就是需要进行回表查询

四、索引 SQL 语法

(1) 表

drop table tb_user;create table tb_user(id int primary key auto_increment comment '主键',name varchar(50) not null comment '用户名',phone varchar(11) not null comment '手机号',email varchar(100) comment '邮箱',profession varchar(11) comment '专业',age tinyint unsigned comment '年龄',gender char(1) comment '性别 , 1: 男, 2: 女',status char(1) comment '状态',createtime datetime comment '创建时间') comment '系统用户表';INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('吕布', '17799990000', 'lvbu666@163.com', '软件工程', 23, '1', '6', '2001-02-02 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('曹操', '17799990001', 'caocao666@qq.com', '通讯工程', 33, '1', '0', '2001-03-05 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('赵云', '17799990002', '17799990@139.com', '英语', 34, '1', '2', '2002-03-02 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('孙悟空', '17799990003', '17799990@sina.com', '工程造价', 54, '1', '0', '2001-07-02 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('花木兰', '17799990004', '19980729@sina.com', '软件工程', 23, '2', '1', '2001-04-22 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('大乔', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('露娜', '17799990006', 'luna_love@sina.com', '应用数学', 24, '2', '0', '2001-02-08 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38, '1', '5', '2001-05-23 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('项羽', '17799990008', 'xiaoyu666@qq.com', '金属材料', 43, '1', '0', '2001-09-18 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('白起', '17799990009', 'baiqi666@sina.com', '机械工程及其自动化', 27, '1', '2', '2001-08-16 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('韩信', '17799990010', 'hanxin520@163.com', '无机非金属材料工程', 27, '1', '0', '2001-06-12 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('荆轲', '17799990011', 'jingke123@163.com', '会计', 29, '1', '0', '2001-05-11 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('兰陵王', '17799990012', 'lanlinwang666@126.com', '工程造价', 44, '1', '1', '2001-04-09 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狂铁', '17799990013', 'kuangtie@sina.com', '应用数学', 43, '1', '2', '2001-04-10 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('貂蝉', '17799990014', '84958948374@qq.com', '软件工程', 40, '2', '3', '2001-02-12 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('妲己', '17799990015', '2783238293@qq.com', '软件工程', 31, '2', '0', '2001-01-30 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('芈月', '17799990016', 'xiaomin2001@sina.com', '工业经济', 35, '2', '0', '2000-05-03 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1', '1', '2001-08-08 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狄仁杰', '17799990018', 'jujiamlm8166@163.com', '国际贸易', 30, '1', '0', '2007-03-12 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('安琪拉', '17799990019', 'jdodm1h@126.com', '城市规划', 51, '2', '0', '2001-08-15 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('典韦', '17799990020', 'ycaunanjian@163.com', '城市规划', 52, '1', '2', '2000-04-12 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('廉颇', '17799990021', 'lianpo321@126.com', '土木工程', 19, '1', '3', '2002-07-18 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('后羿', '17799990022', 'altycj2000@139.com', '城市园林', 20, '1', '0', '2002-03-10 00:00:00');INSERT INTO study_index.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');

(2) 索引 SQL

1.name 字段为姓名字段,该字段的值可能会重复,为该字段创建索引

CREATE INDEX idx_user_name ON tb_user ( `name` );SHOW INDEX FROM tb_user;

2.phone 手机号字段的值是非空,且唯一的,为该字段创建唯一索引

CREATE UNIQUE INDEX idx_user_phone ON tb_user ( phone );

3.为profession、age、status创建联合索引

CREATE INDEX idx_user_profession_age_status ON tb_user ( profession, age, `status` );

4.为email建立合适的索引来提升查询效率

CREATE INDEX idx_user_email ON tb_user ( email );

五、SQL 性能分析(查看执行频次)

MySQL 客户端连接成功后,可通过 show [session|global] status 命令查看服务器状态信息。可查看当前数据库的 INSERT、UPDATE、DELETE、SELECT 的执行频次

-- session 是查看当前会话 ;-- global 是查询全局数据 ;SHOW GLOBAL STATUS LIKE 'Com_______';


假如数据库主要执行的是【查询】,则可为该数据库创建索引,进而优化查询。

六、SQL 性能分析(慢查询日志)

  • 慢查询日志记录了所有执行时间超过指定参数值(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志

  • MySQL的慢查询日志默认没有开启

--- 查看是否开启了慢查询日志SHOW VARIABLES LIKE 'slow_query_log';


开启慢查询日志,需要在MySQL的配置文件 /etc/my.cnf 中配置如下信息:

Windows 中启动慢查询日志
// 开启MySQL慢日志查询开关
slow_query_log=1
// 设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
slow_query_log_file = “D:\Dev\MySQL\MySQLData\slow_query.txt”
① 一定要记得重启 MySQL 服务

配置完毕之后,通过以下指令重新启动MySQL服务

systemctl restart mysqld

Linux 中可在 /var/lib/mysql/localhost-slow.log 文件中查看慢日志文件中记录的信息

七、SQL 性能分析(profiles )

  • show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了
  • 通过have_profiling参数,能够看到当前MySQL是否支持profile操作
# 查看当前数据库是否支持 profileSELECT @@have_profiling;


  • MySQL是支持 profile操作的,但是开关是关闭的。可以通过set语句在session/global级别开启profiling

-- 查看每一条 SQL 的耗时基本情况show profiles;-- 查看指定 query_id 的 SQL 语句各个阶段的耗时情况show profile for query query_id;-- 查看指定 query_id 的 SQL 语句 CPU 的使用情况show profile cpu for query query_id;

八、SQL 性能分析(explain)

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息【执行计划】,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。

Explain 执行计划中各个字段的含义:

九、最左前缀法则和范围查询

(1) 最左前缀法则

① 如果索引了多列(联合索引),要遵守最左前缀法则

② 最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。

③ 如果跳跃某一列,索引将会部分失效(后面的字段索引失效)

④ 如果最左边的列不存在,索引全部失效



(2) 范围查询

  • 联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效

十、索引失效情况

(1) 索引列运算

不要在索引列上进行运算操作, 索引将失效

函数运算操作之后,索引失效

(2) 字符串不加单引号

字符串类型字段使用时,不加引号,索引将失效

(3) 头部模糊匹配查询

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

(4) or 连接的条件

用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

or 连接的条件必须都有索引,索引才有效

(5) 数据分布影响

如果MySQL评估使用索引比全表更慢,则不使用索引

十一、索引使用原则

(1) 索引提示

SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的

(2) 覆盖索引

  • 尽量使用覆盖索引,减少select *
  • 覆盖索引是指 查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到


(3) 前缀索引

当字段类型为字符串(varchar,text,longtext等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO, 影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率

(4) 单列索引与联合索引

单列索引:即一个索引只包含单个列
联合索引:即一个索引包含了多个列

十二、索引设计原则

  • 针对于数据量较大,且查询比较频繁的表建立索引。

  • 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索
    引。

  • 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。

  • 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

  • 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

  • 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。

  • 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询


推荐三本书,如下图所示:


共送3本书,请你在评论区回复【ZGQ ~ 我要赠书】
我根据Java程序随机选择2人送书(每人可发布3个评论)
截止时间:2023/06/15

618,清华社 IT BOOK 多得图书活动开始啦!活动时间为 2023 年 6 月 7 日至 6 月 18 日,清华
社为您精选多款高分好书,涵盖了 C++、Java、Python、前端、后端、数据库、算法与机器学习等多
个 IT 开发领域,适合不同层次的读者。全场 5 折,扫码领券更有优惠哦!快来京东点击链接 IT BOOK 多得
(或扫描京东二维码)查看详情吧