MySQL进阶2 - 索引

news2024/11/16 15:43:05

MySQL进阶1 - 索引

  • 1. 索引概述
  • 2. 索引结构
    • 2.1 二叉树
    • 2.2 B-Tree(多路平衡查找树)
    • 2.3 B+Tree
    • 2.4 Hash
  • 3. 索引分类
  • 4. 索引语法
  • 5. SQL性能分析
    • 5.1 SQL执行频率
    • 5.2 慢查询日志
    • 5.3 profile
    • 5.4 explain执行计划
      • 5.3.1 EXPLAIN执行计划各字段含义:
  • 6. 索引使用
    • 6.1 最左前缀法则
    • 6.2 范围查询
    • 6.3 索引列运算
    • 6.4 字符串不加引号
    • 6.5 模糊匹配
    • 6.6 or连接的条件
    • 6.7 数据分布影响
    • 6.8 SQL提示
    • 6.9 覆盖索引
    • 6.10 前缀索引
    • 6.11 单列索引与联合索引
  • 7. 索引设计原则

1. 索引概述

介绍

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

优缺点

优势劣势
提高数据检索的效率,降低数据库的IO成本索引列也是要占用空间的
通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗索引大大提高了查询效率,同时也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低

2. 索引结构

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

索引结构描述
B + Tree结构最常见的索引类型,大部分引擎都支持B+Tree索引
Hash索引底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
R+tree(空间索引)空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full - text(全文索引)是一种通过建立倒排索引,快速匹配文档的方式,类似于Lucene,Solr,ES
索引InnoDBMyISAMMemory
B + Tree索引支持支持支持
Hash索引不支持不支持支持
R - tree索引不支持支持不支持
Full - text5.6版本之后支持支持不支持

我们平常说的索引,如果没有特别指明,都是指B+tree结构组织的索引

2.1 二叉树

在这里插入图片描述

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

红黑树:数据量大的情况下,层级较深,检索速度慢

2.2 B-Tree(多路平衡查找树)

以一棵最大度数(max-degree)为5(5阶)的B-Tree为例(每个节点最多存储4个key,5个指针):

在这里插入图片描述
Ps:树的度数指的是一个节点的子节点的个数

上图中有4个key,分别是20、30、62、89,其对应5个指针,小于20的对应第一个指针,20-30之间对应第二个指针,30-62之间对应第三个指针,62-89之间对应第四个指针,大于89的对应第五个指针

具体可参考B-Tree Visualization,在该网站中选择Max. Degree = 5,插入以下数据:

100 65 169 368 900 556 780 35 215 1200 234 888 158 90 1000 88 120 268 250

效果如下:

在这里插入图片描述

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

2.3 B+Tree

以一棵最大度数(max-degree)为4(4阶)的B+Tree为例:

在这里插入图片描述
在B+ Tree Visualization中选择5阶,同样插入以上数据,效果如下:

在这里插入图片描述

相对于B-Tree区别:

① 所有的数据都会出现在叶子节点
② 叶子节点形成一个单向链表

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

在这里插入图片描述

2.4 Hash

哈希索引就是采用一定的Hash算法,将键值换算成新的Hash值,映射到对应的槽位上,然后存储在Hash表中。如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了Hash冲突(也称为Hash碰撞),可以通过链表来解决问题

• Hash索引特点

  1. Hash索引只能用于对等比较(=, in),不支持范围查询(between, >, <, …)
  2. 无法利用索引完成排序操作
  3. 查询效率高,通常只需要一次检索就行了,效率通常要高于B+Tree索引

• 存储引擎支持

在MySQL中,支持Hash索引的是Memory引擎,而InnoDB中具有自适应Hash功能,Hash索引是存储引擎根据B+Tree索引在指定条件下自动构建的

3. 索引分类

分类含义特点关键字
主键索引针对于表中主键创建的索引默认自动创建,只能有一个PRIMARY
唯一索引避免同一个表中某数据列中的值重复可以有多个UNIQUE
常规索引快速定位特定数据可以有多个
全文索引全文索引查找得是文本中得关键词,而不是比较索引中的值可以有多个FULLTEXT

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

分类含义特点
聚集索引(Clustered Index)将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据必须有,且只有一个
二级索引(Secondary Index)将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键可以存在多个

聚集索引选取规则:

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

4. 索引语法

• 创建索引

CREATE [UNIQUE | FULLTEXT] INDEX index_name ON table_name (index_col_name, …);

•  查看索引

SHOW INDEX FROM table_name;

• 删除索引

DROP INDEX index_name ON table_name;

• 案例演示

-- 创建表tb_user
create table tb_user(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '姓名',
    phone char(11) comment '手机号',
    email varchar(30) comment '邮箱',
    profession varchar(20) comment '专业',
    age int check ( age > 0 and age < 100 ) comment '年龄',
    gender int check ( gender = 1 or gender = 2 ) comment '性别',
    status char(1) comment '状态',
    createtime datetime comment '创建时间'
) comment '信息表';

-- 添加数据
insert into tb_user(name, phone, email, profession, age, gender, status, createtime)
values('安景辉', 13080354567, 'anjinghui@example.com', '软件工程', 23, 1, 6, '2001-02-02 00:00:00'),
      ('白佳颖', 13097742368, 'baijiaying@example.com', '通讯工程', 26, 2, 0, '2001-03-05 00:00:00'),
      ('柏凡', 13123456789, 'bofan@example.com', '英语', 25, 1, 2, '2002-03-02 00:00:00'),
      ('陈梦洁', 13245678901, 'chenmengjie@example.com', '工程造价', 27, 2, 0, '2001-07-02 00:00:00'),
      ('曹宇涛', 13356789012, 'caoyutao@example.com', '软件工程', 22, 1, 1, '2001-04-22 00:00:00'),
      ('董碧云', 13467890123, 'dongbiyun@example.com', '舞蹈', 22, 2, 0, '2001-02-07 00:00:00'),
      ('单景瑞', 13578901234, 'shanjingrui@example.com', '应用数学', 27, 1, 0, '2001-02-08 00:00:00'),
      ('冯彩云', 13689012345, 'fengcaiyun@example.com', '化工', 28, 2, 5, '2001-05-23 00:00:00'),
      ('范石磊', 13790123456, 'fanshilei@example.com', '金属材料', 30, 1, 0, '2001-09-18-00:00:00'),
      ('关海燕', 13801234567, 'guanhaiyan@example.com', '机械工程及其自动化', 27, 2, 2, '2001-08-16 00:00:00'),
      ('高少杰', 13912345678, 'gaoshaojie@example.com', '无机非金属材料工程', 26, 1, 0, '2001-06-12 00:00:00'),
      ('韩楚仪', 13112345678, 'hanchuyi@example.com', '会计', 23, 2, 0, '2001-05-11 00:00:00'),
      ('郝飞羽', 13212345678, 'haofeiyu@example.com', '工程造价', 32, 1, 1, '2001-04-09 00:00:00'),
      ('贺冰倩', 13312345678, 'hebingqian@example.com', '应用数学', 25, 2, 2, '2001-04-10 00:00:00'),
      ('黄浩然', 13412345678, 'huanghaoran@example.com', '软件工程', 24, 1, 3, '2001-02-12 00:00:00'),
      ('黄欣茹', 13512345678, 'huangxinru@example.com', '软件工程', 24, 2, 0, '2001-01-30 00:00:00'),
      ('江逸舟', 13612345678, 'jiangyizhou@example.com', '工业经济', 35, 1, 0, '2000-05-03 00:00:00'),
      ('李佳妮', 13712345678, 'lijiani@example.com', '化工', 38, 2, 1, '2001-08-08 00:00:00'),
      ('康正乐', 13812345678, 'kangzhengle@example.com', '国际贸易', 30, 1, 0, '2007-03-12 00:00:00'),
      ('刘可颐', 13987654321, 'liukeyi@example.com', '城市规划', 39, 2, 0, '2001-08-15 00:00:00'),
      ('李海洋', 13012345678, 'lihaiyang@example.com', '城市规划', 40, 1, 0, '2000-04-12 00:00:00'),
      ('马一鸣', 13187654321, 'mayiming@example.com', '土木工程', 21, 1, 3, '2002-07-18 00:00:00'),
      ('林泽洋', 13287654321, 'linzeyang@example.com', '城市园林', 20, 1, 0, '2002-03-10 00:00:00'),
      ('秦芷妍', 13387654321, 'qinzhiyan@example.com', '工程造价', 29, 2, 4, '2003-05-26 00:00:00');

-- 查询索引
show index from tb_user;
-- 1.name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
create index idx_user_name on tb_user(name);
-- 2. phone手机号字段的值是非空且唯一的,为该字段创建唯一索引
create unique index idx_user_phone on tb_user(phone);
-- 3. 为profession、age、status创建联合索引
create index idx_user_pro_age_sta on tb_user(profession, age, status);
-- 4. 为email建立合适的索引来提升查询效率
create index idx_user_email on tb_user(email);

-- 删除索引
drop index idx_user_name on tb_user;
drop index idx_user_pro_age_sta on tb_user;
drop index idx_user_phone on tb_user;
drop index idx_user_email on tb_user;

查询结果如下:

在这里插入图片描述

5. SQL性能分析

5.1 SQL执行频率

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

SHOW GLOBAL STATUS LIKE ‘Com_______’;

其中,show session status是查看当前会话的状态信息,show global status是查看全局的状态信息,如下:

在这里插入图片描述

5.2 慢查询日志

慢查询日志记录了所有执行时间超过指定参数(long _query_time,单位:秒,默认10秒) 的所有SQL语句的日志。MySQL的慢查询日志默认没有开启,查询慢查询日志开关是否开启的指令如下:

SHOW VARIABLES LIKE ‘slow_query_log’;
在这里插入图片描述

而查询慢查询日志时间的指令如下:

SHOW VARIABLES LIKE ‘long_query_time’;
在这里插入图片描述

Linux(centOS)中需在MySQL的配置文件(etc/my.cnf)中配置如下信息以开启慢查询日志开关:

# 开启MySQL慢查询日志查询开关
slow_query_log=1
# 设置慢查询日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

详细配置及操作如下:

① 首先输入以下指令对/etc/my.cnf进行编辑

vi /etc/my.cnf

② 按’i’键进行编辑,并在文档末尾添加如下语句:

# 开启慢查询日志开关
slow_query_log=1
# 设置慢查询日志时间为2秒
long_query_time=2

③ 按’Esc’键退出编辑,输入’:x’或’:wq’或’:wq!'保存并退出

④ 重启MySQL服务器

systemctl restart mysqld

⑤ 慢查询日志文件存路径:/var/lib/mysql/localhost-slow.log

在这里插入图片描述
如该位置没有该文件可在MySQL中通过命令show varaiables like ‘%slow_query_log_file%’查看慢查询日志存放位置

⑥ 查看localhost-slow.log内容

cat localhost-slow.log
在这里插入图片描述
从中可以看到,除了MySQL版本号、端口号等信息外并无其他多余信息

⑦ 查看慢查询日志文件尾部实时输出的内容的命令(单独新建个窗口以便查看实时输出)

tail -f localhost-slow.log
在这里插入图片描述

⑧ 在另一窗口执行MySQL指令,如查询tb_user表的数据:

select * from tb_user;
在这里插入图片描述

可以看到,由于查询时间未超过2秒,所以慢查询日志实时输出并没有出现数据。接下来使数据库睡眠3秒,再查看慢日志查询实时输出:

select sleep(3);
在这里插入图片描述

此时,睡眠的指令可以在慢查询日志中看到,而Windows端的慢查询日志开启请参考https://www.cnblogs.com/taozihua/p/10331897.html

5.3 profile

show profiles能够在做SQL优化时帮助我们了解时间都花费在哪里。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling;

profiling默认是关闭的,可以通过以下指令查看profiling是否开启:

SELECT @@profiling;
若为0,则为关闭状态,若为1,则为开启状态

若profiling处于关闭状态,可通过set语句开启profiling:

set [global | session] profiling=1

开启profiling后,执行一系列的业务SQL操作,然后通过以下指令查看指令的执行耗时:

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

案例演示

-- 查询profiling是否打开
select @@profiling;

-- 开启profiling
set profiling = 1;

-- 执行SQL操作
select * from tb_user;
select * from tb_user where id = 15;
select * from tb_user where name = '单景瑞';
select count(*) from tb_user;
select * from tb_user where gender = 1;
select sleep(3);

-- 查看每一条sql的耗时基本情况
show profiles;

此时,运行结果如下:
在这里插入图片描述

-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query 11;

此时,运行结果如下:
在这里插入图片描述

-- 查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query 11;

在这里插入图片描述

5.4 explain执行计划

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

语法:

# 直接在select语句之前加上关键字explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件;

演示

explain select * from tb_user where id = 8;

结果如下:
在这里插入图片描述

5.3.1 EXPLAIN执行计划各字段含义:

id

select 查询的序列号,表示查询中执行select子句或是操作表的顺序(id相同,执行顺序从上到下;id不同,值越大,越先执行)

select_type

表示SELECT的类型,常见的取值有 SIMPLE (简单表,即不使用表连接或子查询)、PRIMARY (主查询,即外层的查询)、UNION (UNION 中的第二个或者后面的查询语句),SUBQUERY ( SELECT / WHERE 之后包含了子查询)等

type

表示连接类型,性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all

possible_key

显示可能应用在这张表上的索引,一个或多个

key

实际使用的索引,如果为NULL,则没有使用索引

key_len

表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好

rows

MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是精确的

filtered

表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

案例演示

案例中所演示的表分别为students、courses、student_course以及tb_user,在MySQL基础4-多表查询以及本文中可以找到对应的表

id

explain select s.* from students s where s.id in (select sc.student_id from student_course sc where sc.sourse_id = (select c.id from courses c where c.name = 'Python'));

运行结果如下:

在这里插入图片描述

从图中可以看到,SQL语句编写顺序是students、student_course、courses,但从执行结果来看,id最大的为3,

对应为courses表,这是最先执行的,其次是id为2的student_course表,然后id相同的则由上而下顺序执行,也就

是先执行<subquery 2>(第二个子查询),最后才执行students表的查询。

select_type

select_type参考意义不大,它只是说明当前SQL语句的查询类型

type

type代表访问的类型,如:

explain select * from tb_user where id = 1;

在这里插入图片描述

在该查询中,连接类型为const,而type访问类型中,性能由好至差分别为 NULL、system、const、eq_ref、ref、range、

index、all。其中 system 只有访问系统表的时候才会出现,根据主键、唯一索引进行访问,一般会出现const,如:

show index from tb_user;
select * from tb_user;
explain select * from tb_user where phone = '13689012345';

在这里插入图片描述

在这里插入图片描述

如果使用非唯一性的索引进行查询时,就会出现ref,如:

explain * from tb_user where name = '黄浩然';

在这里插入图片描述

在优化SQL语句的时候,尽量把type往前优化,尽量不要出现all,如果出现all的话,代表全表扫描,性能会比较低

如果出现index,代表用了索引,但也会对索引进行扫描,虽然比all快,但性能也比较低

extra代表额外的信息,也就是说,在执行查询的过程当中,在前面几个字段中没有展现出来的值,将会在extra进行展示

关于posssible_key、key、key_len、rows 在此不多做演示

重点关注:  type、possible_key、key、ken_len

6. 索引使用

6.1 最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)。

案例演示

本案例中所用的表为本文中创建的tb_user表

explain select * from tb_user where profession = '软件工程' and age = 24 and status = '0';

运行结果如下:

在这里插入图片描述

该查询用到了联合索引,索引长度为93,profession、age、status这三个字段的索引全用上了,符合最左前缀法则,且中间并未跳过任何一列

explain select * from tb_user where profession = '软件工程' and age = 24;

运行结果如下:

在这里插入图片描述

该查询索引长度为88,意味着status字段的索引长度为5,该查询也满足最左前缀法则,中间并未跳过某一列

explain select * from tb_user where profession = '软件工程';

运行结果如下:

在这里插入图片描述

该查询语句只查询了profession字段,并未跳过某一列,所以也满足最左前缀法则,且可由前两条查询结果推断出age 的索引长度为5

select * from tb_user where profession = '软件工程' and status = '0';

运行结果如下:

在这里插入图片描述

该查询索引长度为83,意味着profession用到索引,而status没用到,所以索引长度和单独查询profession字段的索引长度是一样的。原因是profession和status间还有一个字段age被跳过,这导致了索引部分失效,也就是status字段的索引失效

explain select * from tb_user where age = 24 and status ='0';

运行结果如下:

在这里插入图片描述

该查询用到的是全表扫描而不是索引,因为最左边的profession字段并未存在,所以该查询不满足最左前缀法则,导致索引失效

explain select * from tb_user where age = 24 and status = '0' and profession = '软件工程';

运行结果如下:

在这里插入图片描述

该查询也用到了索引,因为最左前缀法则要求的是最左列必须存在,但并不意味着最左列的顺序必须是最左边,所以该查询语句满足最左前缀法则

总结: 最左前缀法则要求包含索引中最左边的列,如果最左边的列不存在,索引失效,如果跳过了其中某一列,该列后面的索引将会失效

6.2 范围查询

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

案例演示

explain select * from tb_user where profession = '软件工程' and age > 23 and status = '0';

运行结果如下:

在这里插入图片描述

结合前文来看,该查询满足最左前缀法则,但索引长度为88,说明age后面的那一列,也就是status索引失效。而在业务允许的情况下,尽量用 >= 或 <= 来代替,以规避索引失效的情况

explain select * from tb_user where profession = '软件工程' and age >= 23 and status = '0';

运行结果如下:

在这里插入图片描述

6.3 索引列运算

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

案例演示

-- 查询手机尾号为56的用户信息
select * from tb_user where substring(phone,10,2) = '56';

-- 查看执行信息
explain select * from tb_user where substring(phone,10,2) = '56';

运行结果如下:

在这里插入图片描述

可以看到,在索引列phone上引用函数运算substring后,索引失效,所以在业务中应尽量减少对索引列进行运算的操作

6.4 字符串不加引号

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

案例演示

select * from tb_user where phone = 13912345678;

explain select * from tb_user where phone = 13912345678;

运行结果如下:

在这里插入图片描述

在该查询语句中,由于phone字段作为字符串在查询的时候未加引号,导致索引失效

6.5 模糊匹配

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

案例演示

-- 尾部模糊匹配
select * from tb_user where profession like '软件%';

explain select * from tb_user where profession like '软件%';

-- 头部模糊匹配
select * from tb_user where profession like '%工程';

explain select * from tb_user where profession like '%工程';

运行结果如下:

在这里插入图片描述

从中可以看到,在进行尾部模糊匹配时索引并未失效,但对头部进行模糊匹配时索引失效了

6.6 or连接的条件

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

案例演示

explain eslect * from tb_user where id = 15 or age = 24;

explain select * from tb_user whre phone = '13412345678' or age = 24;

运行结果如下:

在这里插入图片描述

由于age字段没有索引,只有联合索引,而如果仅仅只对age字段进行查询,不满足最左前缀法则,联合索引不会用到。而正由于联合索引用不到,所以即使id、phone有索引,索引也会失效,所以需要针对age字段也建立索引

-- 建立age字段索引
create index idx_user_age on tb_user(age);

-- 再次查询
explain select * from tb_user where id = 15 or age = 24;

explain select * from tb_user where phone = '13412345678' or age = 24;

运行结果如下:

在这里插入图片描述

此时可以看到,在建立age字段的索引后,用到的是phone字段及age字段的索引,而非联合索引,所以该查询索引未失效

6.7 数据分布影响

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

** 案例演示**

explain select * from tb_user where phone >= '13612345678';

explain select * from tb_user where phone >= '13112345678';

explain select * from tb_user where phone >= '13012345678';

运行结果如下:
在这里插入图片描述

从中可以看到,在查询phone >= '13612345678’时用到了索引,这是因为MySQL在查询时评估该表中大部分phone值都小于该值,用索引查询比全表扫描快。但将phone改为’13112345678’或‘13012345678’时,MySQL评估全表大部分phone值都比这俩值大,用全表扫描会比用索引快,所以才进行全表扫描,同理还有:

explain select * from tb_user where profession is null;

explain select * from tb_user where profession is not null;

在该查询中,由于tb_user表中的profession字段没有null值,所以第一个查询语句用到的是索引,而第二个查询语句用到的则是全表扫描,因为MySQL评估当查询时需要比较大部分值时,会优先使用全表扫描,而如果只是一小部分的话,则优先使用索引。当将profession字段的值都设置为null后,结果将相反,这里不做演示

6.8 SQL提示

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

在查询时,如果一个字段有多个索引,MySQL查询优化器会自动选择索引,如下:

-- 创建tb_user表中profession字段的索引
create index idx_user_pro on tb_user(profession);

-- 查询
explain select * from tb_user where profession = '软件工程';

运行结果如下:

在这里插入图片描述

可以看到,在该查询中可能用到的索引分别为’idx_user_pro_age_sta’及’idx_user_pro’,而实际用到的索引为’idx_user_pro_age_sta’,这就是MySQL查询优化器自动选择的结果,如果想指定查询索引,可参考以下三条查询语句:

use  index(告诉数据库用哪个索引):

explain select * from tb_user use index(idx_user_pro) where profession = ‘软件工程’;

在这里插入图片描述

该查询语句只是给MySQL建议用指定查询,至于查询结果,取决于MySQL比较所指定的索引效率快不快后所给出的

ignore index(告诉数据库不用哪个索引):

explain select * from tb_user ignore index(idx_user_pro) where profession = ‘软件工程’;

在这里插入图片描述

force index(告诉数据库必须走哪个索引):

explain select * from tb_user force index(idx_user_pro) where profession = ‘软件工程’;

在这里插入图片描述

6.9 覆盖索引

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

案例演示

-- 删除多余索引,以减少干扰
drop index idx_user_age on tb_user;

drop index idx_user_email on tb_user;

drop index idx_user_pro on tb_user;

-- 测试
explain select id, profession from tb_user where profession = '软件工程' and age = 24 and status = '0';

explain select id, profession, age from tb_user where profession = '软件工程' and age = 24 and status = '0';

explain select id, profession, age, status from tb_user where profession = '软件工程' and age = 24 and status = '0';

explain select id, profession, age, status, name from tb_user where profession = '软件工程' and age = 24 and status = '0';

explain select * from tb_user where profession = '软件工程' and age = 24 and status = '0';

运行结果如下:

在这里插入图片描述

从中可以看到Extra中的信息出现不同的地方,而其中’Using where; Using index’的语句的性能要高于’Using index contition’,还需注意的是,如果使用的MySQL版本不同Extra中出现的值,其展示的形式也是不同的

Ps:

using index condition:查找使用了索引,但是需要回表查询数据
using where; using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

6.10 前缀索引

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

创建前缀索引的语法

create index idx_xxx_xxx on table_name(column(n));

前缀长度

可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的

选择性计算演示

select count(distinct email) / count(*) from tb_user;

select count(substring(email, 1, 5)) / count(*) from tb_user;

运行结果如下:

在这里插入图片描述

创建前缀索引演示

create index idx_email_5 on tb_user(email(5));

show index from tb_user;

运行结果如下:
在这里插入图片描述

6.11 单列索引与联合索引

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

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引

单列索引情况

explain select id, phone, name from tb_user where phone = '13012345678' and name = '李海洋';

在这里插入图片描述

多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询

联合索引情况

-- 创建联合索引
create unique index idx_user_phone_name on tb_user(phone, name);

explain select id, phone, name from tb_user where phone = '13012345678' and name = '李海洋';

-- 强制使用联合索引
explain select id, phone, name from tb_user force index(idx_user_phone_name) where phone = '13012345678' and name = '李海洋';

运行结果如下:
在这里插入图片描述

7. 索引设计原则

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

Ⅱ. 针对常作为查询条件(where)、排序(ordder by)、分组(group by)操作的字段建立索引

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1309847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue实现滑动验证

效果图&#xff1a; 源码地址&#xff1a;github文档地址&#xff1a; https://github.com/monoplasty/vue-monoplasty-slide-verify 使用步骤&#xff1a;1&#xff0c;安装插件&#xff1a; npm install --save vue-monoplasty-slide-verify 在main.js中使用一下&#xff…

记一次跨入smartKettle大门随即转身就走的简单体验过程

目录 &#x1f4da;第一章 背景&#x1f4d7;目的&#x1f4d7;总体方向 &#x1f4da;第二章 源码解读&#x1f4d7;官方说明&#x1f4d7;controller接口&#x1f4d5;swagger&#x1f4d5;源码 &#x1f4d7;Kettle API &#x1f4da;第三章 总结⁉️问题记录❓问题一&#…

使用CFimagehost源码自建无需数据库支持的PHP图片托管服务

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

SpringCloud系列(六)| 聊聊负载均衡

一、负载均衡概述 上一篇文章中&#xff0c;我们在集成OpenFeign的过程中提示我们需要加入了一个依赖就是&#xff1a; spring-cloud-starter-loadbalancer。 顾名思义&#xff0c;这个包的作用就是用来做负载均衡的。 简单解释一下什么是负载均衡&#xff0c;就是当我们的服…

Python面经【11】- Python可迭代对象一网打尽专题

Python面经【11】- Python可迭代对象一网打尽专题 可迭代对象Python的迭代器、生成器1) 迭代器2) 生成器 可迭代对象、迭代器的区别12. 生成器、迭代器的区别什么是装饰器&#xff1f;函数装饰器有什么作用一句话解释什么样的语言能够使用装饰器Python中的作用域&#xff1f;什…

节流防抖:提升前端性能的秘密武器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

我常用的几个经典Python模块

Python常用的模块非常多&#xff0c;主要分为内置模块和第三方模块两大类&#xff0c;且不同模块应用场景不同又可以分为文本类、数据结构类、数学运算类、文件系统类、爬虫类、网络通讯类等多个类型。 大家常用的内置模块比如&#xff1a;math、re、datetime、urllib、os、ra…

自动化测试流程详解

最近很多小伙伴问我自动化测试到底该怎么做&#xff1f;流程是什么样的&#xff1f;在每个阶段都需要注意什么&#xff1f;本文也就主要从自动化测试的基本流程入手&#xff0c;对面试自动化测试工程师的同学会有不少帮助。对于在职的朋友&#xff0c;也可以参考此流程&#xf…

AI扩图哪家强?我们实地测试了5款扩图工具

AI扩图的命运齿轮开始转动了。 近日&#xff0c;“AI扩图”在各个社交平台上频频出圈&#xff0c;#AI扩图#话题&#xff0c;在抖音平台累计播放数超7.8亿次。 何为AI扩图&#xff1f; AI 扩图功能给我们带来了一个观察世界的新角度。在 AI 的加持下&#xff0c;我们可以看到…

门窗企业网站建设作用是什么

门窗作为市场重要的组合部分&#xff0c;其应用广泛使得众多商家入局经营&#xff0c;无论大型建筑还是家庭应用&#xff0c;都有较高需求度&#xff0c;尤其对品牌商来说&#xff0c;无论直售还是加盟都可以获得不菲效益。 但对门窗企业来说&#xff0c;也需要解决几个痛点&a…

【计算思维】第14届蓝桥杯省赛计算思维U8组真题试卷

选择题 第 1 题 单选题 要把下面 4 张图片重新排列成蜗牛的画像&#xff0c;该如何排列这些图片?( ) A. B. C. D. 第 2 题 单选题 下图的几张牌&#xff0c;每次可以交换任意 2 张。 如将它们按照下面的顺序排列&#xff0c;最少需要交换( )次。 A.4 B.5 C.6 D.7 …

YOLOv8改进 | 2023Neck篇 | BiFPN双向特征金字塔网络(附yaml文件+代码)

一、本文介绍 本文给大家带来的改进机制是BiFPN双向特征金字塔网络&#xff0c;其是一种特征融合层的结构&#xff0c;也就是我们本文改进YOLOv8模型中的Neck部分&#xff0c;它的主要思想是通过多层级的特征金字塔和双向信息传递来提高精度。本文给大家带来的结构可以让大家自…

2023年贺岁电影:一眼多,二眼好多

如果从11月末开始统计&#xff0c;今年贺岁档共有72部贺岁片&#xff0c;平均一天就有2部电影上映&#xff0c;看完总计需要花费7400分钟。 这个数量几乎快赶上2021年到2022年贺岁片的总和。 今年电影市场快速回暖以来&#xff0c;多部爆款作品接力上映&#xff0c;持续刺激市…

项目篇 | 图书管理系统 | 图像加载与绘制

项目篇 | 图书管理系统 | 图像加载与绘制 基本介绍 首先解释清楚什么叫图像加载与绘制,意思就是说项目中需要用到一些图片资源(各种图标),我们要在图书管理系统中展示这些图片,就需要先导入图片到项目中,再加载图片资源(通过资源路径)、绘制图片(即展示)。 注:如果…

如何将Word中的表格图片转换为可编辑格式?

我们都知道&#xff0c;Word中的表格是一个非常有用的工具&#xff0c;可以让我们在文档中轻松添加和编辑各种数据。但有时候我们可能会遇到一个问题&#xff1a;当表格作为图片插入时&#xff0c;我们就不能直接编辑它了。这可怎么办呢&#xff1f; 别担心&#xff0c;我们有…

Python语言学习笔记之八(文件IO)

本课程对于有其它语言基础的开发人员可以参考和学习&#xff0c;同时也是记录下来&#xff0c;为个人学习使用&#xff0c;文档中有此不当之处&#xff0c;请谅解。 1、什么是文件I/O 在Python中&#xff0c;文件IO&#xff08;输人/输出)是指与文件进行交互的过程。这包括读…

翻译: 生成式人工智能项目的生命周期 Lifecycle of a generative AI project

我将分享一下构建生成式AI软件应用程序的过程。首先&#xff0c;我们会确定项目范围&#xff0c;决定软件要实现的功能。例如&#xff0c;你可能决定建立一个餐厅声誉监控系统。接下来是实际的实施阶段。由于生成式AI使构建应用程序变得容易&#xff0c;你通常可以很快构建出一…

【Fastapi框架】Fastapi的使用和进阶

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、项目启动1.安装2.示例3.启动4.路由 二、进阶1.请求数据2.静态文件加载3.路由管理4.跨域配置5.自定义中间件6.使用jwt中间件 前言 一、…

轻松学习视频剪辑:视频转码批量转为序列图片技巧,从新手到高手

随着视频剪辑的普及&#xff0c;越来越多的爱好者开始尝试自己动手进行视频剪辑。对于新手来说&#xff0c;掌握一些实用的技巧是很有必要的。在视频剪辑中&#xff0c;有时候要将视频片段转换为序列图片&#xff0c;以便进行更灵活的编辑和处理。本文详解云炫AI智剪的一种简单…

Python爬虫实战(基础篇)—9获取某个城市天气(附完整代码)

文章目录 专栏导读背景1、网址(请求URL)2、查看请求方法、参数3、请求初步测试代码请求成功 4、数据清洗(lxmlxpath)【城市实时天气】【日期】lxmlxpath 代码【室外温度】【体感温度】lxmlxpath 代码【天晴情况】【全天气温】lxmlxpath 代码【气压值】【降水量】【风力情况】【…