MySQL进阶(二)- -索引

目录

二、索引

1.索引概述

(1)介绍

(2)优缺点

2.索引结构

(1)二叉树

(2)红黑树

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

(4)B+Tree

(5)Hash索引

         1)介绍

2)Hash索引特点

3.索引分类

4.索引语法

(1)创建索引

(2)查看索引

(3)删除索引

5.SQL性能分析

(1)SQL执行频率

(2)慢查询日志

(3)profile详情

(4)explain执行计划

6.索引使用

(1)最左前缀法则

(2)范围查询

(3)索引列运算

(4)字符串不加引号

(5)模糊查询

(6)or连接的条件

(7)数据分布影响

(8)SQl提示

(9)覆盖索引

(10)前缀索引

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

7.索引设计原则


二、索引

1.索引概述

(1)介绍

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

(2)优缺点

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

2.索引结构

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

MySQL进阶(二)- -索引_第1张图片 索引在不同引擎中的支持情况如下:

MySQL进阶(二)- -索引_第2张图片

注意:平常所说的索引,未特别指明,指B+Tree结构组织索引

(1)二叉树

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

(2)红黑树

大数据量情况下,层级较深,检索速度较慢

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

以一棵最大度数(树的度数指一个节点的子节点个数)为5(5阶)的b-tree为例(每个节点最多存储4个key,5个指针):

MySQL进阶(二)- -索引_第3张图片

 蓝色表示一个节点最多存储4个key,而一个节点最多有5个指针,即5个分叉,在小于20的范围有一个指针,在20到30之间有一个指针,以此类推,若有n个Key,则有n+1个指针;在蓝色节点下方的绿色代表数据

B+Tree动态形成参考网站:B-Tree Visualization

(4)B+Tree

MySQL进阶(二)- -索引_第4张图片

 所有元素出现在叶子节点,且形成一个单向链表,而上部分非叶子节点起到索引作用,叶子节点用来存放数据

数据结构可视化网站;

Data Structure Visualization (usfca.edu)

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

MySQL进阶(二)- -索引_第5张图片

(5)Hash索引

     1)介绍:

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

举例:

MySQL进阶(二)- -索引_第6张图片

先计算每行数据的hash值(表左侧),再通过hash函数算出name字段的槽位值,与哈希表的槽位(蓝色)对应,再在name字段旁记录hash值,当两个人的name字段的槽位相同(金庸与杨逍),则在后面增加链表,记录name字段以及hash值,若有多个,则继续向后延长链表

2)Hash索引特点

 ①Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,<,…) 
 ②无法利用索引完成排序操作

 ③查询效率高,通常只需要一次检索就可以了(哈希碰撞除外,此情况检索要在链表中对比查找对应元素),效率通常要高于B+tree索引

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

思考:为什么InnoDB存储引擎选择使用B+tree索引结构?
①相对于二叉树,层级更少,搜索效率高;
②对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;

③相对Hash索引,B+Tree支持范围匹配及排序操作

3.索引分类

MySQL进阶(二)- -索引_第7张图片

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

  聚集索引选取规则: 

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

eg.根据表构建索引:

 MySQL进阶(二)- -索引_第8张图片

 MySQL进阶(二)- -索引_第9张图片

以id即主键为索引,构建的是主键索引,也就是聚集索引,叶子节点存放的是行数据

MySQL进阶(二)- -索引_第10张图片

以name字段建立的索引为二级索引(因为聚集索引有且只有一个),叶子节点存放的是id

回表查询:先根据二级索引找到主键值,再根据主键值在聚集索引中拿到行数据

eg.select * from user where name=’Arm’.

先在二级索引里找到Arm的id,再根据id在聚集索引里找到Arm的行数据,即Arm的所有数据

4.索引语法

(1)创建索引

CREATE [UNIQUE | FULLTEXT] INDEX 索引名称(一般形式为index_表名_字段1名_字段2名_...) ON 表名 (字段1名 排序方式,字段2名 排序方式...);

排序方式可省略,默认为升序排序

索引名称处的字段名若太长,可简写

一个索引关联一个字段称为单链索引,关联多个字段称为联合索引或组合索引

eg1.为表tb_user的phone字段创建唯一索引

create unique index idx_user_phone on tb_user(phone);

eg2.为表tb_user的name字段创建常规索引

create index idx_user_name on tb_user(name);

eg3.为表tb_user的profession、age、status字段创建联合索引

create index idx_user_pro_age_sta on tb_user(profession,age,status);

(pro,sta均为简写)

eg4.将age按升序,phone按倒序建立联合索引

create index idx_user_age_pho_ad on tb_user(age asc,phone desc);

(2)查看索引

SHOW INDEX FROM tablename;(以行的形式查看表的索引)
SHOW INDEX FROM tablename\G;(以列的形式查看表的索引)

(3)删除索引

DROP INDEX indexname ON tablename;

eg.删除表tb_user的索引idx_user_phone

drop index idx_user_phone on tb_user;

5.SQL性能分析

(1)SQL执行频率

      1)查看服务器状态信息

show [session | global] status;

      2)查看当前数据库insert、update、delete、select的访问次数

show global status like‘com_______’;(7个下划线代表7个字符)

(2)慢查询日志

     1)介绍:慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒,即执行的SQL执行耗时超过10s就会被认为是慢查询,然后记录在慢查询日志中)的所有SQL语句的日志。

      2)查询慢日志的开启状态:

show variables like ‘slow_query_log’;

MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)最后配置如下信息:

①开启MySQL慢日志查询开关

slow_query_log=1

②设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会被视为慢查询,并被记录在慢查询日志

long_query_time=2

配置完成后,重启MySQL进行测试,查看慢日志文件中记录的信息:/var/lib/mysql/localhost-slow.log

(3)profile详情

      1)查询当前MySQL是否支持profile操作(YES为支持)

select @@have_profiling;

       2)默认profiling是关闭的,可通过set语句在session/global级别开启profiling

select @@profiling;     # 查询profilig的开关情况,0表示关闭
set profiling=1;

      3)查询耗时情况

         ①查询每条SQL语句的耗时情况

 show profiles;

         ②查询指定query_id的SQL语句各个阶段的耗时情况

show profile for query query_id;

(query_id指的是通过show profiles;命令所得到的SQL语句对应的query_id)

        ③查询指定query_id的SQL语句cpu的使用情况

shoq profile cpu for query query_id;

(4)explain执行计划

explain或desc命令获取MySQL如何执行select语句信息,包括在select语句执行过程中表如何连接和连接顺序

语法:

explain/desc select 字段列表 from 表名 where 条件;

结果如下:

表中各字段含义:

①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。

一般来说,不访问任何表才为null,访问系统表为system,根据主键和唯一索引访问表为const,使用非唯一索引访问表为ref,
possible_key 
显示可能应用在这张表上的索引,一个或多个。

Key 
实际使用的索引,如果为NULL,则没有使用索引。
Key_len 
表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好。
⑦rows 
MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。
⑧filtered 
表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。

⑨Extra

额外信息,展示前面字段未展示出的信息

6.索引使用

(可提高查询的效率,大大缩短时间消耗)

(1)最左前缀法则

(主要针对联合索引)查询从索引的最左列开始,并且不跳过索引中的列。若跳过某一列,此后的索引将失效。(索引的顺序可调换)

eg.有一个联合索引idx_user_pro_age_sta,在表tb_user中查询profession=’软件工程’,age=31,status=’0’的人的信息,可通过命令

select * from tb_user where profession=’软件工程’ and age=31 and status=’0’;

此时通过explain执行计划,可查询到该命令使用了索引idx_user_pro_age_sta

根据最左前缀法则,将条件改为 profession=’软件工程’或 profession=’软件工程’ and age=31,通过explain执行计划查询到都使用了索引idx_user_pro_age_sta,而将条件改为age=31 and status=’0’或status=’0’等,查询到没有使用索引,将条件改为profession=’软件工程’ and status=’0’,查询到使用了索引idx_user_pro_age_sta,而此时,使用了索引的只有profession,status失效了。

(2)范围查询

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

,但是使用>=或<=不会

eg.select * from tb_user where profession=’软件工程’ and age>31 and status=’0’;

此时,因为age使用了范围查询,故status使用索引失效

但是,将>改为>=不会导致status失效

(3)索引列运算

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

eg.已知phone已建立了索引,现查询手机号后两位为15的人:

select * from tb_user where substring(phone,10,2)=’15’;

此时对phone进行了截取,导致索引失效,即该语句未使用索引,SQL性能低

(4)字符串不加引号

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

(5)模糊查询

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

eg.select * from tb_user where phone like ‘%15’;

(6)or连接的条件

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

eg.已知id有索引,age没有索引

select * from tb_user where id=10 or age=23;

该语句不会用到索引

(7)数据分布影响

如果MySQL评估使用索引比全表慢,则不使用索引(若满足要求的结构占表的大多数,则不使用索引)

eg.表tb_user中绝大部分人的年龄都大于等于20,且age建立了索引

select * from tb_user where age>=20;

该语句中,由于绝大部分人的年龄都大于等于20,故MySQL选择全表扫描,而不使用索引

(8)SQl提示

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

  • 告诉数据库要用的索引:use index(索引名)(仅仅是建议,MySQL可能不采纳)

select * from tb_user use index(idx_user_age) where age=25;

  • 告诉数据库不用的索引:ignore index(索引名)
  • 告诉数据库必须用的索引:force index(索引名)

(9)覆盖索引

尽量使用覆盖索引(查询使用了索引,并且需要查询的字段在该索引中已经全部能找到),减少使用select *(因为除非所有字段都建立了索引,否则存在要查询的字段在索引中找不到,需要回表查询,性能相对较低)。

eg2.已知有id,name,age,gender字段,且id为主键(为聚集索引,叶子节点为行数据),name建立了辅助索引(叶子节点为id),

①select * from tb_user where id=2;

②select * from tb_user where name=2;

③select id,name,gender from tb_user where name=’Mike’;

③中通过name辅助索引返回id和name,但gender需要根据id返回聚集索引返回gender,即回表查询

①比②的性能高,因为①使用聚集索引,而聚集索引的叶子存放的是行数据,而②中返回的是id,还需回表查询

enplain执行计划最后一个extra结果:

using index condition:查找使用了索引,但需要回表查询数据

using where,using index:查找使用了索引,但是需要的数据都在索引列中能找到,不用回表查询数据(性能更高)

(10)前缀索引

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

语法:

create index idx_name on tablename(column(n));

n为前缀长度:

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

eg.email为一个索引,它的选择性计算语句如下:

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

得出选择性为1.0000

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

得出选择性为0.9583

创建email索引:

create idx_email_5 on tb_user(email(5));

此时若查询email为‘[email protected]’的人的信息

select * from tb_user where email=‘[email protected]’;

前缀索引查询流程:

先截取目标email的前5个字符,再通过辅助索引返回id,最后根据id通过聚集索引返回行信息

  MySQL进阶(二)- -索引_第11张图片

MySQL进阶(二)- -索引_第12张图片

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

    1)单列索引:一个索引值包含单个列

    2)联合索引(组合索引):一个索引包含多个列

7.索引设计原则

1)针对于数据量较大,且查询比较频繁的表建立索引。
2)针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
3)尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
4)如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5)尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
6)要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
7)如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

你可能感兴趣的:(mysql,数据库,学习)