·索引概述
索引(index)是帮助MySQL高效获取数据的有序数据结构。
数据库系统中维护这满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法快速查找数据,这种数据结构就是索引。
无索引时:
select * from user where age = 45;
它会把整张表都查找一遍,执行效率很低
索引:
它就像二分查找或者红黑树一样,会极大的增大我们的查询效率。
优缺点:
优点 | 缺点 |
提高数据检索的效率,降低数据库的IO成本 | 索引列也要占用空间(不怕,磁盘比较便宜) |
通过索隐裂对数据进行排序,降低数据排序的成本 | 索引大大提高了查询效率,但也降低了表的更新速度,对表进行INSERT,UPDATE,DELETE效率变慢(不会太多) |
·索引结构
介绍:
索引是在第三层存储引擎层实现的,所以存储引擎不同,索引的结构也不同。
索引结构 | 描述 |
B+Tree索引 | 最常见的索引,大部分引擎都支持B+树索引 |
Hash索引 | 底层数据结构通过哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询 |
R-tree(空间索引) | 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少 |
Full-text(全文索引) | 是一种通过建立倒排索引,快速匹配文档的方式。 |
索引 | InnoDB | MyISAM | Memory |
B+tree索引 | 支持 | 支持 | 支持 |
Hash索引 | 不支持 | 不支持 | 支持 |
R-tree索引 | 不支持 | 支持 | 不支持 |
Full-text索引 | 5.6以后支持 | 支持 | 不支持 |
通常不说的话我们说的索引结构都是B+tree索引
二叉树:
二叉树是通过插入数据,然后和根节点比较,比根节点大放右边,比根节点小放左边。
二叉树的缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量的情况下,层级越深,检索速度越慢。
B-Tree(多路平衡查找树):
以一颗最大度数为5的B-tree为例(每个节点最多存放4个key,5个value):
树的度数指的是一个节点的子节点个数
下面这个网站可以查看数据结构的变化:
当数据达到5个之后,中间元素会向上裂变。然后剩下4个元素裂变成2,2的两组。
如果是颗度为4的B-Tree,那么它添加到第4个数据时,会把从前往后第2个数据裂变出去
经典B+Tree
以一颗最大度数为4的B+Tree为例:
1、所有的数都存储在叶子节点上,前面的都是索引
2、每一个节点都通过一个指针指向下一个节点,最终形成单向列表
MySQL中B+树
MySQL索引数据结构对经典的B+Tree进行了优化,在原B+Tree的基础上,增加了一个指向相邻叶子节点的链表指针,形成了带顺序的指针B+Tree,提高了区间访问性能。
绿色的块是存放数据用的,里面存放的是这一行的数据,如6的下面绿色存放的就是索引为6的那一行的全部数据姓名,年龄,身高,性别等。。。
Hash索引
采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上。
如果发生hash冲突,可以通过链表解决
hash索引的特点:
1、Hash索引只能用于对等比较(=,in),不支持范围查找(between,>,<,...)
2、无法利用索引完成排序操作
3、查询效率高,通常只需要检索一次,效率要高于B+Tree索引
存储引擎支持:
在MySQL中,支持hash索引的是Memory引擎,而InnoDB中在某些情况下会自动把B+Tree构建成hash索引。
为什么InnoDB存储选择使用B+Tree索引结构?
1、相对二叉树,层级更少,搜索效率高。
2、相对B-Tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键减少,指针跟着减少,要保存大量数据是,只能增加数的高度,导致性能降低。
3、相对Hash索引,B+Tree支持范围查找和排序。
·索引分类
分类 | 含义 | 特点 | 关键字 |
主键索引 | 针对表中主键创建的索引 | 默认自动创建,只能有一个 | PRIMARY |
唯一索引 | 避免表中某些数据列中的值重复(添加唯一约束的时候就是给它创建唯一索引) | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定位置 | 可以有多个 | |
全文索引 | 全文索引是查找文本中的关键字,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
在、中,根据存储形式,又可以分为以下两种
分类 | 含义 | 特点 |
聚集索引 | 将数据存储与索引放在了一起,索引结构的叶子节点保存了行数据 | 必须有,且只能有一个(主键) |
二级索引 | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |
聚集索引选取规则:
如果存在主键,逐渐就是聚集索引
如果没有主键,则第一个唯一索引就是聚集索引
如果都没有,InnoDB会自动生成一个rowid作为隐藏的聚集索引
展示:
聚集索引下面存放的是一行的数据,而二级索引下面存放的是这个数据所在的id。
执行过程:
select * from user where name = 'Arm';
他会先根据Arm这个名字,在名字的二级索引里查找Arm,找到之后获得id,再根据id进行聚集索引,获得完成的数据。
这种线查找二级索引,再查找聚集索引的行为称之为回表查询。
·索引语法
1、创建索引
create [unique | fulltext] index index_name on table_name(index_col_name,...)
unique:唯一索引
fulltext:全文索引
两个都不选就是普通索引
2、查看索引
show index from table_name;
查看一个表里的所有索引
3、删除索引
drop index index_name on table_name;
删除一个表里的索引,index_name是索引名
实例:
tb_user是表名,索引的名字规范:idx_+其他
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);
在表的括号里写需要进行联合索引创建的列名。
像profession,age,status的顺序是有影响的,写的时候要注意。
4、为email建立合适的索引来提高查询效率。
create index idx_user _email on tb_user(email);
·SQL性能分析
为什么要分析SQL性能分析?
为了做SQL优化。
我们优化的是哪一类SQL呢
主要是查询语句,而索引就占了很大一部分。
SQL执行频率:
可以查看一个数据库增删改查的访问频率,然后判断这是查询为主还是增删改为主,增删改为主的话优化后效率也不高。
可以通过下面这条语句判断当前数据库的INSERT\UPDATE\DELETE\DELECT访问频次
SHOW GLOBAL(SESSION) STATUS LIKE 'Com_______'; (7个下划线,代表7个字母)
global:查看全局信息
session:查看对话的信息
清晰明了
慢查询日志:
慢查询日志记录里所有的执行时间超过指定参数(long_query_time,默认为10秒)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
开启慢查询日志的查询开关
slow_query_log = 1
设置慢日志的时间为2,执行超过2秒就被记录
long_query_time = 2.
慢查询日志文件中记录信息/var/lib/mysql/localhost-slow.log。
然后重启mysql
systemctl restart mysqld
profile耗时详情:
show profiles; 查看每条SQL的耗时情况
show profile for query query_id; 查看指定query_id的SQL各个阶段的耗时情况
show profile cpu for query query_id; 查看指定query_id的SQL语句CPU使用情况
慢查询日志中记录的都是执行时间超过预设时间的SQL语句。
但如果有的SQL业务很简单,但是执行达到了1.9几秒,对这部分SQL也要进行优化。
为此:
show profiles能够在做SQL优化时帮我们了解时间都耗费到哪了。通过hava_profiling参数,能够看到当前MySQL是否支持profile操作:
select @@have_profiling; YES是支持
select @@profiling; 可以查看profiling开启的情况 0 是没有开启
set profiling = 1;
是打开profiling。
当我们打开了profile之后,我们才能通过:show profiles;来查看当前会话中,所有SQL语句的耗时情况。
各个阶段的耗时情况
explain执行计划:(性能分析一般用这个)
explain或者desc命令获取MySQL如何执行select语句的信息,包括在执行过程中表如何连接,和连接的顺序。
语法:直接在select语句前加关键字explain/desc
---------------------------------------------------------------------------------------------------------------------------------补充
---------------------------------------------------------------------------------------------------------------------------------
·索引使用
索引对查询效率的提升:
查询一个没有索引的数据需要的时间非常久,我们可以通过对比查询相同的数据有无索引的耗时来判断索引对一个数据查询效率的帮助。
create index 索引名 on 表名(列名)。
10000000的数据,不用索引查询需要20s左右,用来索引能达到0.01s。
最左前缀法则:
如果索引了多列(联合索引),要遵循最左前缀法则。
指的是:索引最左边的列在select语句中一定要存在,位置在哪无所谓。索引的中间列要存在,如果某一列不存在,那么索引中的该列后面那部分索引就会失效。
这里profession,age,statue三个组成了联合索引,使用时就需要先查profession,,然后是age,最后是status。
select * from tb_user where profession = '软件工程' and age = 31 and status = '0';
查询执行过程:
explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';
索引失效的情况:
范围查询:
联合索引中,出现范围查询(<,>),范围查询右侧的列索引失效。
索引列运算:
不要在索引列上进行运算操作,索引将失效。
字符串不加引号:
字符串类型字段使用时,不加引号,索引将失效。
or连接的条件:
用or分隔开的条件,如果or前面的条件中的列有索引,而后面列中没有索引,那么涉及的索引都不会被用到。
数据分布影响:
如果mysql在进行select时使用索引比全表扫描还慢,那就不使用索引,直接全表扫描。
基本是当查询的结果大部分数据都满足,这个时候会自动走all全表扫描,但查询的数据比较少就会走索引。
SQL提示:
当满足联合索引(最左前缀),而且也有一个单独的索引时,mysql自动判断使用联合索引。
也可以使用SQL提示让数据库根据你设置的索引进行查询。
有三种:
user index:
告诉数据库用哪个索引,这个主要是建议,系统有可能不接受
ignore index:
告诉数据库不用那个索引
force index:
告诉数据库必须用那个索引
使用方法:
在from 数据库后面添加
explain select * from tb_user user(idx_user_pro) where ......
覆盖索引:
在我们查询时尽量使用覆盖索引(查询时是用来索引,并且需要返回的列,在该索引中已经能全部找到),减少使用select *。
例子:
假设有个数据是这样的,分析三条语句:
select * from tb_user where id = 2;
select id,name from tb_user where name = 'Doc';
select id,name,gender from tb_user where name = 'Doc';
第一条:id是主键,根据聚集索引查询,可以获得全部信息,不用二次查询(回表)
第二条:name有索引,name是二级索引,存放的数据是id,索引返回值是name
和id都可以在name的索引中找到,所以第二条SQL也不用进行二次查询(回表)
第三条:和第二点不一样的是gender是没办法在name的索引中得到的,需要根据
id再在主键索引中进行查找,这个过程称之为回表。
没有回表查询的操作就是覆盖索引
前缀索引:
当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘l0,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。
语法:create index idx_索引名 on 表名(列名(提取前多少个字符做前缀索引,写数字))
n的求法:
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
用这两个公式:
select count(distinct email)/count(*) from tb_user; #email是列名
select count(distinct substring(email,1,5))/count(*) from tb_user; #这是判断前多少个字符的选择性,是1就是没有重复,就是最好的。
然后根据得到的结果,使用create index idx_索引名 on 表名(列名(n))
单列索引和联合索引的选择:
单列索引只包含一个索引,联合索引包含多个索引
涉及多个查询条件,建议使用联合索引。效率高,并且可以避免回表查询。
·索引设计原则
1.针对于数据量较大,且查询比较频繁的表建立索引。
#数据量超过1000000。
2针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
3.尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
4.如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
5.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
6、要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
7.如果索引列不能存储NULL值,请在创建表时使用NOTNULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个
索引最有效地用于查询。