• 售前

  • 售后

热门帖子
入门百科

Mysql利用索引的精确方法及索引原理详解

[复制链接]
朲賤芿嗳2017 显示全部楼层 发表于 2021-10-26 12:51:35 |阅读模式 打印 上一主题 下一主题
一 、先容
为何要有索引?
一样平常的应用系统,读写比例在10:1左右,而且插入操纵和一样平常的更新操纵很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,照旧一些复杂的查询操纵,因此对查询语句的优化显然是重中之重。提及加速查询,就不得不提到索引了。
什么是索引?
索引在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据布局。索引对于精良的性能

非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发紧张。

索引优化应该是对查询性能优化最有效的手段了。索引可以大概容易将查询性能进步好几个数量级。

索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则必要从几百页中逐页去查。
  1. 30
  2. 10 40
  3. 5 15 35 66
  4. 1 6 11 19 21 39 55 100
复制代码
你是否对索引存在误解?
索引是应用步伐计划和开发的一个紧张方面。若索引太多,应用步伐的性能大概会受到影响。而索引太少,对查询性能又会产生影响,要找到一个均衡点,这对应用步伐的性能至关紧张。一些开发职员总是在过后才想起添加索引----我一直认为,这源于一种错误的开发模式。如果知道数据的使用,从一开始就应该在必要处添加索引。开发职员每每对数据库的使用停顿在应用的层面,好比编写SQL语句、存储过程之类,他们乃至大概不知道索引的存在,或认为过后让干系DBA加上即可。DBA每每不敷相识业务的数据流,而添加索引必要通过监控大量的SQL语句进而从中找到问题,这个步骤所需的时间肯定是远大于初始添加索引所需的时间,而且大概会遗漏一部分的索引。当然索引也并不是越多越好,我曾经遇到过如许一个问题:某台MySQL服务器iostat显示磁盘使用率一直处于100%,经太过析后发现是由于开发职员添加了太多的索引,在删除一些不须要的索引之后,磁盘使用率马上降落为20%。可见索引的添加也是非常有技能含量的。
二 、索引的原理
一 、索引原理
索引的目标在于进步查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个末节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等
本质都是:通过不停地缩小想要获取数据的范围来筛选出终极想要的效果,同时把随机的变乱酿成次序的变乱,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定命据。
数据库也是一样,但显然要复杂的多,由于不光面临着等值查询,还有范围查询(>、<、between、in)、含糊查询(like)、并集查询(or)等等。数据库应该选择怎么样的方式来应对全部的问题呢?我们追念字典的例子,能不能把数据分成段,然后分段查询呢?最简朴的如果1000条数据,1到100分成第一段,101到200分成第二段,201到300分成第三段......如许查第250条数据,只要找第三段就可以了,一下子去除了90%的无效数据。但如果是1千万的记录呢,分成几段比力好?稍有算法根本的同砚会想到搜刮树,其匀称复杂度是lgN,具有不错的查询性能。但这里我们忽略了一个关键的问题,复杂度模型是基于每次相同的操纵本钱来考虑的。而数据库实现比力复杂,一方面数据是生存在磁盘上的,另外一方面为了进步性能,每次又可以把部分数据读入内存来盘算,由于我们知道访问磁盘的本钱大概是访问内存的十万倍左右,以是简朴的搜刮树难以满意复杂的应用场景。
二 、磁盘IO与预读
前面提到了访问磁盘,那么这里先简朴先容一下磁盘IO和预读,磁盘读取数据靠的是机器运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所必要的时间,主流磁盘一样平常在5ms以下;旋转延迟就是我们常常听说的磁盘转速,好比一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一样平常在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约即是5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS(Million Instructions Per Second)的机器每秒可以实验5亿条指令,由于指令依靠的是电的性子,换句话说实验一次IO的时间可以实验约450万条指令,数据库动辄十万百万以致千万级数据,每次9毫秒的时间,显然是个灾难。下图是盘算机硬件延迟的对比图,供各人参考:

考虑到磁盘IO是非常高昂的操纵,盘算机操纵系统做了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,由于局部预读性原理告诉我们,当盘算机访问一个地址的数据的时间,与其相邻的数据也会很快被访问到。每一次IO读取的数据我们称之为一页(page)。具体一页有多大数据跟操纵系统有关,一样平常为4k或8k,也就是我们读取一页内的数据时间,实际上才发生了一次IO,这个理论对于索引的数据布局计划非常有帮助。
三 、索引的数据布局
前面讲了索引的根本原理,数据库的复杂性,又讲了操纵系统的干系知识,目标就是让各人相识,任何一种数据布局都不是凭空产生的,一定会有它的配景和使用场景,我们如今总结一下,我们必要这种数据布局可以大概做些什么,实在很简朴,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜刮树是否能满意需求呢?就如许,b+树应运而生(B+树是通过二叉查找树,再由均衡二叉树,B树演化而来)。

如上图,是一颗b+树,关于b+树的定义可以拜见B+树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包罗几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包罗数据项17和35,包罗指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜刮方向的数据项,如17、35并不真实存在于数据表中。
###b+树的查找过程

如图所示,如果要查找数据项29,那么起首会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只必要三次IO,性能进步将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么统共必要百万次的IO,显然本钱非常非常高。
###b+树性子

1.索引字段要只管的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的巨细 / 数据项的巨细,磁盘块的巨细也就是一个数据页的巨细,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要只管的小,好比int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度降落,导致树增高。当数据项即是1时将会退化成线性表。

2.索引的最左匹配特性:当b+树的数据项是复合的数据布局,好比(name,age,sex)的时间,b+数是按照从左到右的次序来建立搜刮树的,好比当(张三,20,F)如许的数据来检索的时间,b+树会优先比力name来确定下一步的所搜方向,如果name相同再依次比力age和sex,末了得到检索的数据;但当(20,F)如许的没有name的数据来的时间,b+树就不知道下一步该查哪个节点,由于建立搜刮树的时间name就是第一个比力因子,必须要先根据name来搜刮才气知道下一步去哪里查询。好比当(张三,F)如许的数据来检索时,b+树可以用name来指定搜刮方向,但下一个字段age的缺失,以是只能把名字即是张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常紧张的性子,即索引的最左匹配特性。
四 、聚集索引与辅助索引
在数据库中,B+树的高度一样平常都在2~4层,这也就是说查找某一个键值的行记录时最多只必要2到4次IO,这倒不错。由于当前一样平常的机器硬盘每秒至少可以做100次IO,2~4次的IO意味着查询时间只必要0.02~0.04秒。
数据库中的B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),
聚集索引与辅助索引相同的是:不管是聚集索引照旧辅助索引,其内部都是B+树的情势,即高度是均衡的,叶子结点存放着全部的数据。
聚集索引与辅助索引差别的是:叶子结点存放的是否是一整行的信息
1、聚集索引
  1. #InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,也将聚集索引的叶子结点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。
  2. 同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。 #如果未定义主键,MySQL取第一个唯一索引(unique)而且只含非空列(NOT NULL)作为主键,InnoDB使用它作为聚簇索引。 #如果没有这样的列,InnoDB就自己产生一个这样的ID值,它有六个字节,而且是隐藏的,使其作为聚簇索引。#由于实际的数据页只能按照一棵B+树进行排序,因此每张表只能拥有一个聚集索引。在多少情况下,查询优化器倾向于采用聚集索引。
  3. 因为聚集索引能够在B+树索引的叶子节点上直接找到数据。此外由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值得查询。
复制代码

聚集索引的好处之一:它对主键的排序查找和范围查找速率非常快,叶子节点的数据就是用户所要查询的数据。如用户必要查找一张表,查询末了的10位用户信息,由于B+树索引是双向链表,以是用户可以快速找到末了一个数据页,并取出10条记录
聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可
2、辅助索引
表中除了聚集索引外其他索引都是辅助索引(Secondary Index,也称为非聚集索引),与聚集索引的区别是:辅助索引的叶子节点不包罗行记录的全部数据。
叶子节点除了包罗键值以外,每个叶子节点中的索引行中还包罗一个书签(bookmark)。该书签用来告诉InnoDB存储引擎去哪里可以找到与索引相对应的行数据。
由于InnoDB存储引擎是索引构造表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。如下图

辅助索引的存在并不影响数据在聚集索引中的构造,因此每张表上可以有多个辅助索引,但只能有一个聚集索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶子级别的指针获得只想主键索引的主键,然后再通过主键索引来找到一个完备的行记录。
举例来说,如果在一棵高度为3的辅助索引树种查找数据,那必要对这个辅助索引树遍历3次找到指定主键,如果聚集索引树的高度同样为3,那么还必要对聚集索引树进行3次查找,终极找到一个完备的行数据地点的页,因此一共必要6次逻辑IO访问才气得到终极的一个数据页。

五 、MySQL索引管理
一 、功能
1. 索引的功能就是加速查找
2. mysql中的primary key,unique,连合唯一也都是索引,这些索引除了加速查找以外,还有束缚的功能
二 、MySQL常用的索引
普通索引INDEX:加速查找
唯一索引: -主键索引PRIMARY KEY:加速查找+束缚(不为空、不能重复) -唯一索引UNIQUE:加速查找+束缚(不能重复)
连合索引: -PRIMARY KEY(id,name):连合主键索引 -UNIQUE(id,name):连合唯一索引 -INDEX(id,name):连合普通索引
三 、索引的两大类型hash与btree
  1. #我们可以在创建上述索引的时候,为其指定索引类型,分两类
  2. hash类型的索引:查询单条快,范围查询慢
  3. btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)
  4. #不同的存储引擎支持的索引类型也不一样
  5. InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  6. MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
  7. Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
  8. NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
  9. Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
复制代码
四 、创建/删除索引的语法
  1. #方法一:创建表时
  2.   CREATE TABLE 表名 (
  3. 字段名1 数据类型 [完整性约束条件…],
  4. 字段名2 数据类型 [完整性约束条件…],
  5. [UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY
  6. [索引名] (字段名[(长度)] [ASC |DESC])
  7. );
  8. #方法二:CREATE在已存在的表上创建索引
  9. CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
  10. ON 表名 (字段名[(长度)] [ASC |DESC]) ;
  11. #方法三:ALTER TABLE在已存在的表上创建索引
  12. ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
  13. 索引名 (字段名[(长度)] [ASC |DESC]) ;
  14. #删除索引:DROP INDEX 索引名 ON 表名字;
复制代码
  1. #方式一
  2. create table t1(
  3. id int,
  4. name char,
  5. age int,
  6. sex enum('male','female'),
  7. unique key uni_id(id),
  8. index ix_name(name) #index没有key
  9. );
  10. #方式二
  11. create index ix_age on t1(age);
  12. #方式三
  13. alter table t1 add index ix_sex(sex);
  14. #查看
  15. mysql> show create table t1;
  16. | t1 | CREATE TABLE `t1` (
  17. `id` int(11) DEFAULT NULL,
  18. `name` char(1) DEFAULT NULL,
  19. `age` int(11) DEFAULT NULL,
  20. `sex` enum('male','female') DEFAULT NULL,
  21. UNIQUE KEY `uni_id` (`id`),
  22. KEY `ix_name` (`name`),
  23. KEY `ix_age` (`age`),
  24. KEY `ix_sex` (`sex`)
  25. ) ENGINE=InnoDB DEFAULT CHARSET=latin1
复制代码
六 、测试索引
一 、预备
  1. #1. 准备表
  2. create table s1(
  3. id int,
  4. name varchar(20),
  5. gender char(6),
  6. email varchar(50)
  7. );
  8. #2. 创建存储过程,实现批量插入记录
  9. delimiter $$ #声明存储过程的结束符号为$$
  10. create procedure auto_insert1()
  11. BEGIN
  12. declare i int default 1;
  13. while(i<3000000)do
  14. insert into s1 values(i,'duoduo','male',concat('duoduo',i,'@oldboy'));
  15. set i=i+1;
  16. end while;
  17. END$$ #$$结束
  18. delimiter ; #重新声明分号为结束符号
  19. #3. 查看存储过程
  20. show create procedure auto_insert1\G
  21. #4. 调用存储过程
  22. call auto_insert1();
  23. #等到时间长短,看机器性能
复制代码
提示:创建表的时间长短,看机器的性能,请耐心等待!
二 、在没有索引的条件下测试查询速率
  1. #无索引:mysql根本就不知道到底是否存在id等于333333333的记录,只能把数据表从头到尾扫描一遍,此时有多少个磁盘块就需要进行多少IO操作,所以查询速度很慢
  2. mysql> select * from s1 where id=333333333;
  3. Empty set (0.33 sec)
复制代码
三 、在表中已经存在大量数据的条件下,为某个字段段建立索引,建立速率会很慢

四 、在索引建立完毕后,以该字段为查询条件时,查询速率提升明显

PS:
1. mysql先去索引表里根据b+树的搜刮原理很快搜刮到id即是333333333的记录不存在,IO大大降低,因而速率明显提升
2. 我们可以去mysql的data目录下找到该表,可以看到占用的硬盘空间多了
3. 必要注意,如下图

五 、总结
  1. #1. 一定是为搜索条件的字段创建索引,比如select * from s1 where id = 333;就需要为id加上索引
  2. #2. 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,建完后查询速度加快
  3. 比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。
  4. 建完以后,再查询就会很快了。
  5. #3. 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI
  6. MySAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。
  7. 而在innodb中,表数据文件本身就是按照B+Tree(BTree即Balance True)组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。
  8. 这个索引的key是数据表的主键,因此innodb表数据文件本身就是主索引。
  9. 因为inndob的数据文件要按照主键聚集,所以innodb要求表必须要有主键(Myisam可以没有),
  10. 如果没有显式定义,则mysql系统会自动选择一个可以唯一标识数据记录的列作为主键,
  11. 如果不存在这种列,则mysql会自动为innodb表生成一个隐含字段作为主键,这字段的长度为6个字节,类型为长整型.
复制代码
七 、准确使用索引
一 、索引未掷中
并不是说我们创建了索引就一定会加速查询速率,若想使用索引达到预想的进步查询速率的效果,我们在添加索引时,必须遵循以下问题

1 、范围问题,或者说条件不明白,条件中出现这些符号或关键字:>、>=、<、<=、!= 、between...and...、like、
大于号、小于号

不即是!=

between ...and...

like

2只管选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段大概在大数据眼前区分度就是0,那大概有人会问,这个比例有什么履历值吗?使用场景差别,这个值也很难确定,一样平常必要join的字段我们都要求是0.1以上,即匀称1条扫描10条记录
  1. #先把表中的索引都删除,让我们专心研究区分度的问题
  2. mysql> desc s1;
  3. +--------+-------------+------+-----+---------+-------+
  4. | Field | Type | Null | Key | Default | Extra |
  5. +--------+-------------+------+-----+---------+-------+
  6. | id | int(11) | YES | MUL | NULL | |
  7. | name | varchar(20) | YES | | NULL | |
  8. | gender | char(5) | YES | | NULL | |
  9. | email | varchar(50) | YES | MUL | NULL | |
  10. +--------+-------------+------+-----+---------+-------+
  11. rows in set (0.00 sec)
  12. mysql> drop index a on s1;
  13. Query OK, 0 rows affected (0.20 sec)
  14. Records: 0 Duplicates: 0 Warnings: 0
  15. mysql> drop index d on s1;
  16. Query OK, 0 rows affected (0.18 sec)
  17. Records: 0 Duplicates: 0 Warnings: 0
  18. mysql> desc s1;
  19. +--------+-------------+------+-----+---------+-------+
  20. | Field | Type | Null | Key | Default | Extra |
  21. +--------+-------------+------+-----+---------+-------+
  22. | id | int(11) | YES | | NULL | |
  23. | name | varchar(20) | YES | | NULL | |
  24. | gender | char(5) | YES | | NULL | |
  25. | email | varchar(50) | YES | | NULL | |
  26. +--------+-------------+------+-----+---------+-------+
  27. rows in set (0.00 sec)
复制代码
先把表中的索引都删除,让我们专心研究区分度的问题
  1. 我们编写存储过程为表s1批量添加记录,name字段的值均为duoduo,也就是说name这个字段的区分度很低(gender字段也是一样的,我们稍后再搭理它)
  2. 回忆b+树的结构,查询的速度与树的高度成反比,要想将树的高低控制的很低,需要保证:在某一层内数据项均是按照从左到右,从小到大的顺序依次排开,即左1<左2<左3<...
  3. 而对于区分度低的字段,无法找到大小关系,因为值都是相等的,毫无疑问,还想要用b+树存放这些等值的数据,只能增加树的高度,字段的区分度越低,则树的高度越高。极端的情况,索引字段的值都一样,那么b+树几乎成了一根棍。本例中就是这种极端的情况,name字段所有的值均为'duoduo'
  4. #现在我们得出一个结论:为区分度低的字段建立索引,索引树的高度会很高,然而这具体会带来什么影响呢???
  5. #1:如果条件是name='xxxx',那么肯定是可以第一时间判断出'xxxx'是不在索引树中的(因为树中所有的值均为'duoduo'),所以查询速度很快
  6. #2:如果条件正好是name='duoduo',查询时,我们永远无法从树的某个位置得到一个明确的范围,只能往下找,往下找,往下找。。。这与全表扫描的IO次数没有多大区别,所以速度很慢
  7. 分析
复制代码
3、 =和in可以乱序,好比a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以恣意次序,mysql的查询优化器会帮你优化成索引可以识别的情势
4、 索引列不能到场盘算,保持列“干净”,好比from_unixtime(create_time) = '2014-05-29'就不能使用到索引,缘故原由很简朴,b+树中存的都是数据表中的字段值,但进行检索时,必要把全部元素都应用函数才气比力,显然本钱太大。以是语句应该写成create_time = unix_timestamp('2014-05-29')

5、 and/or
  1. #1、and与or的逻辑
  2. 条件1 and 条件2:所有条件都成立才算成立,但凡要有一个条件不成立则最终结果不成立
  3. 条件1 or 条件2:只要有一个条件成立则最终结果就成立
  4. #2、and的工作原理
  5. 条件:
  6. a = 10 and b = 'xxx' and c > 3 and d =4
  7. 索引:
  8. 制作联合索引(d,a,b,c)
  9. 工作原理:
  10. 对于连续多个and:mysql会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样便可以快速锁定很小的范围),加速查询,即按照d—>a->b->c的顺序
  11. #3、or的工作原理
  12. 条件:
  13. a = 10 or b = 'xxx' or c > 3 or d =4
  14. 索引:
  15. 制作联合索引(d,a,b,c)
  16. 工作原理:
  17. 对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d
复制代码

在左边条件成立但是索引字段的区分度低的情况下(name,加速查询)
6最左前缀匹配原则,非常紧张的原则,对于组合索引mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就制止匹配(指的是范围大了,有索引速率也慢),好比a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)次序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的次序可以恣意调整。
7、 其他情况
  1. - 使用函数
  2. select * from tb1 where reverse(email) = 'duoduo';
  3. - 类型不一致
  4. 如果列是字符串类型,传入条件是必须用引号引起来,不然...
  5. select * from tb1 where email = 999;
  6. #排序条件为索引,则select字段必须也是索引字段,否则无法命中
  7. - order by
  8. select name from s1 order by email desc;
  9. 当根据索引排序时候,select查询的字段如果不是索引,则速度仍然很慢
  10. select email from s1 order by email desc;
  11. 特别的:如果对主键排序,则还是速度很快:
  12. select * from tb1 order by nid desc;
  13. - 组合索引最左前缀
  14. 如果组合索引为:(name,email)
  15. name and email -- 命中索引
  16. name -- 命中索引
  17. email -- 未命中索引
  18. - count(1)或count(列)代替count(*)在mysql中没有差别了
  19. - create index xxxx on tb(title(19)) #text类型,必须制定长度
复制代码
其他注意事项
  1. - 避免使用select *
  2. - count(1)或count(列) 代替 count(*)
  3. - 创建表时尽量时 char 代替 varchar
  4. - 表的字段顺序固定长度的字段优先
  5. - 组合索引代替多个单列索引(经常使用多个条件查询时)
  6. - 尽量使用短索引
  7. - 使用连接(JOIN)来代替子查询(Sub-Queries)
  8. - 连表时注意条件类型需一致
  9. - 索引散列值(重复少)不适合建索引,例:性别不适合
复制代码
八、 连合索引与覆盖索引
一 、连合索引
连合索引时指对表上的多个列合起来做一个索引。连合索引的创建方法与单个索引的创建方法一样,差别之处在仅在于有多个索引列,如下
  1. mysql> create table t(
  2. -> a int,
  3. -> b int,
  4. -> primary key(a),
  5. -> key idx_a_b(a,b)
  6. -> );
  7. Query OK, 0 rows affected (0.11 sec)
复制代码
那么何时必要使用连合索引呢?在讨论这个问题之前,先来看一下连合索引内部的效果。从本质上来说,连合索引就是一棵B+树,差别的是连合索引的键值得数量不是1,而是>=2。接着来讨论两个整型列组成的连合索引,假定两个键值得名称分别为a、b如图

可以看到这与我们之前看到的单个键的B+树并没有什么差别,键值都是排序的,通过叶子结点可以逻辑上次序地读出全部数据,就上面的例子来说,即(1,1),(1,2),(2,1),(2,4),(3,1),(3,2),数据按(a,b)的次序进行了存放。
因此,对于查询select * from table where a=xxx and b=xxx, 显然是可以使用(a,b) 这个连合索引的,对于单个列a的查询select * from table where a=xxx,也是可以使用(a,b)这个索引的。
但对于b列的查询select * from table where b=xxx,则不可以使用(a,b) 索引,实在你不难发现缘故原由,叶子节点上b的值为1、2、1、4、1、2显然不是排序的,因此对于b列的查询使用不到(a,b) 索引
连合索引的第二个好处是在第一个键相同的情况下,已经对第二个键进行了排序处置惩罚,比方在许多情况下应用步伐都必要查询某个用户的购物情况,并按照时间进行排序,末了取出最近三次的购买记录,这时使用连合索引可以帮我们制止多一次的排序操纵,由于索引本身在叶子节点已经排序了,如下
  1. #===========准备表==============
  2. create table buy_log(
  3. userid int unsigned not null,
  4. buy_date date
  5. );
  6. insert into buy_log values
  7. (1,'2009-01-01'),
  8. (2,'2009-01-01'),
  9. (3,'2009-01-01'),
  10. (1,'2009-02-01'),
  11. (3,'2009-02-01'),
  12. (1,'2009-03-01'),
  13. (1,'2009-04-01');
  14. alter table buy_log add key(userid);
  15. alter table buy_log add key(userid,buy_date);
  16. #===========验证==============
  17. mysql> show create table buy_log;
  18. | buy_log | CREATE TABLE `buy_log` (
  19. `userid` int(10) unsigned NOT NULL,
  20. `buy_date` date DEFAULT NULL,
  21. KEY `userid` (`userid`),
  22. KEY `userid_2` (`userid`,`buy_date`)
  23. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
  24. #可以看到possible_keys在这里有两个索引可以用,分别是单个索引userid与联合索引userid_2,但是优化器最终选择了使用的key是userid因为该索引的叶子节点包含单个键值,所以理论上一个页能存放的记录应该更多
  25. mysql> explain select * from buy_log where userid=2;
  26. +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
  27. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  28. +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
  29. | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid | 4 | const | 1 | |
  30. +----+-------------+---------+------+-----------------+--------+---------+-------+------+-------+
  31. row in set (0.00 sec)
  32. #接着假定要取出userid为1的最近3次的购买记录,用的就是联合索引userid_2了,因为在这个索引中,在userid=1的情况下,buy_date都已经排序好了
  33. mysql> explain select * from buy_log where userid=1 order by buy_date desc limit 3;
  34. +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+
  35. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  36. +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+
  37. | 1 | SIMPLE | buy_log | ref | userid,userid_2 | userid_2 | 4 | const | 4 | Using where; Using index |
  38. +----+-------------+---------+------+-----------------+----------+---------+-------+------+--------------------------+
  39. row in set (0.00 sec)
  40. #ps:如果extra的排序显示是Using filesort,则意味着在查出数据后需要二次排序
  41. #对于联合索引(a,b),下述语句可以直接使用该索引,无需二次排序
  42. select ... from table where a=xxx order by b;
  43. #然后对于联合索引(a,b,c)来首,下列语句同样可以直接通过索引得到结果
  44. select ... from table where a=xxx order by b;
  45. select ... from table where a=xxx and b=xxx order by c;
  46. #但是对于联合索引(a,b,c),下列语句不能通过索引直接得到结果,还需要自己执行一次filesort操作,因为索引(a,c)并未排序
  47. select ... from table where a=xxx order by c;
复制代码
二、 覆盖索引
InnoDB存储引擎支持覆盖索引(covering index,或称索引覆盖),即从辅助索引中就可以得到查询记录,而不必要查询聚集索引中的记录。
使用覆盖索引的一个好处是:辅助索引不包罗整行记录的全部信息,故其巨细要远小于聚集索引,因此可以淘汰大量的IO操纵
注意:覆盖索引技能最早是在InnoDB Plugin中完成并实现,这意味着对于InnoDB版本小于1.0的,或者MySQL数据库版本为5.0以下的,InnoDB存储引擎不支持覆盖索引特性
对于InnoDB存储引擎的辅助索引而言,由于其包罗了主键信息,因此其叶子节点存放的数据为(primary key1,priamey key2,...,key1,key2,...)。比方
  1. select age from s1 where id=123 and name = 'duoduo'; #id字段有索引,但是name字段没有索引,该sql命中了索引,但未覆盖,需要去聚集索引中再查找详细信息。
  2. 最牛逼的情况是,索引字段覆盖了所有,那全程通过索引来加速查询以及获取结果就ok了
  3. mysql> desc s1;
  4. +--------+-------------+------+-----+---------+-------+
  5. | Field | Type | Null | Key | Default | Extra |
  6. +--------+-------------+------+-----+---------+-------+
  7. | id | int(11) | NO | | NULL | |
  8. | name | varchar(20) | YES | | NULL | |
  9. | gender | char(6) | YES | | NULL | |
  10. | email | varchar(50) | YES | | NULL | |
  11. +--------+-------------+------+-----+---------+-------+
  12. rows in set (0.21 sec)
  13. mysql> explain select name from s1 where id=1000; #没有任何索引
  14. +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
  15. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  16. +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
  17. | 1 | SIMPLE | s1 | NULL | ALL | NULL | NULL | NULL | NULL | 2688336 | 10.00 | Using where |
  18. +----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+
  19. row in set, 1 warning (0.00 sec)
  20. mysql> create index idx_id on s1(id); #创建索引
  21. Query OK, 0 rows affected (4.16 sec)
  22. Records: 0 Duplicates: 0 Warnings: 0
  23. mysql> explain select name from s1 where id=1000; #命中辅助索引,但是未覆盖索引,还需要从聚集索引中查找name
  24. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
  25. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  26. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
  27. | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | NULL |
  28. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
  29. row in set, 1 warning (0.08 sec)
  30. mysql> explain select id from s1 where id=1000; #在辅助索引中就找到了全部信息,Using index代表覆盖索引
  31. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
  32. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  33. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
  34. | 1 | SIMPLE | s1 | NULL | ref | idx_id | idx_id | 4 | const | 1 | 100.00 | Using index |
  35. +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
  36. row in set, 1 warning (0.03 sec)
复制代码
覆盖索引的另外一个好处是对某些统计问题而言的。基于上一小结创建的表buy_log,查询筹划如下
  1. mysql> explain select count(*) from buy_log;
  2. +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+
  5. | 1 | SIMPLE | buy_log | index | NULL | userid | 4 | NULL | 7 | Using index |
  6. +----+-------------+---------+-------+---------------+--------+---------+------+------+-------------+
  7. row in set (0.00 sec)
复制代码
innodb存储引擎并不会选择通过查询聚集索引来进行统计。由于buy_log表有辅助索引,而辅助索引远小于聚集索引,选择辅助索引可以淘汰IO操纵,故优化器的选择如上key为userid辅助索引
对于(a,b)情势的连合索引,一样平常是不可以选择b中所谓的查询条件。但如果是统计操纵,而且是覆盖索引,则优化器照旧会选择使用该索引,如下
  1. #联合索引userid_2(userid,buy_date),一般情况,我们按照buy_date是无法使用该索引的,但特殊情况下:查询语句是统计操作,且是覆盖索引,则按照buy_date当做查询条件时,也可以使用该联合索引
  2. mysql> explain select count(*) from buy_log where buy_date >= '2011-01-01' and buy_date < '2011-02-01';
  3. +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+
  4. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  5. +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+
  6. | 1 | SIMPLE | buy_log | index | NULL | userid_2 | 8 | NULL | 7 | Using where; Using index |
  7. +----+-------------+---------+-------+---------------+----------+---------+------+------+--------------------------+
  8. row in set (0.00 sec)
复制代码
九、查询优化神器-explain
关于explain下令信任各人并不生疏,具体用法和字段寄义可以参考官网explain-output,这里必要夸大rows是核心指标,绝大部分rows小的语句实验一定很快(有例外,下面会讲到)。以是优化语句根本上都是在优化rows。
  1. 执行计划:让mysql预估执行操作(一般正确)
  2. all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const
  3. id,email
  4. 慢:
  5. select * from userinfo3 where name='alex'
  6. explain select * from userinfo3 where name='alex'
  7. type: ALL(全表扫描)
  8. select * from userinfo3 limit 1;
  9. 快:
  10. select * from userinfo3 where email='alex'
  11. type: const(走索引)
复制代码
参考文中:https://www.jb51.net/article/140759.htm
十 、慢查询优化的根本步骤
  1. 0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
  2. 1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
  3. 2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
  4. 3.order by limit 形式的sql语句让排序的表优先查
  5. 4.了解业务方使用场景
  6. 5.加索引时参照建索引的几大原则
  7. 6.观察结果,不符合预期继续从0分析
复制代码
十一、 慢日记管理
  1. 慢日志
  2.             - 执行时间 > 10
  3.             - 未命中索引
  4.             - 日志文件路径
  5.            
  6.         配置:
  7.             - 内存
  8.                 show variables like '%query%';
  9.                 show variables like '%queries%';
  10.                 set global 变量名 = 值
  11.             - 配置文件
  12.                 mysqld --defaults-file='E:\wupeiqi\mysql-5.7.16-winx64\mysql-5.7.16-winx64\my-default.ini'
  13.                
  14.                 my.conf内容:
  15.                     slow_query_log = ON
  16.                     slow_query_log_file = D:/....
  17.                    
  18.                 注意:修改配置文件之后,需要重启服务
复制代码
MySQL日记管理
  1. ========================================================
  2. 错误日志: 记录 MySQL 服务器启动、关闭及运行错误等信息
  3. 二进制日志: 又称binlog日志,以二进制文件的方式记录数据库中除 SELECT 以外的操作
  4. 查询日志: 记录查询的信息
  5. 慢查询日志: 记录执行时间超过指定时间的操作
  6. 中继日志: 备库将主库的二进制日志复制到自己的中继日志中,从而在本地进行重放
  7. 通用日志: 审计哪个账号、在哪个时段、做了哪些事件
  8. 事务日志或称redo日志: 记录Innodb事务相关的如事务执行时间、检查点等
  9. ========================================================
复制代码
一、bin-log

1. 启用
  1. # vim /etc/my.cnf
  2. [mysqld]
  3. log-bin[=dir\[filename]]
  4. # service mysqld restart
复制代码
2. 暂停
  1. //仅当前会话
  2. SET SQL_LOG_BIN=0;
  3. SET SQL_LOG_BIN=1;
复制代码
3. 查察

查察全部:
  1. # mysqlbinlog mysql.000002
  2. 按时间:
  3. # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56"
  4. # mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54"
  5. # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54"
复制代码
按字节数:
  1. # mysqlbinlog mysql.000002 --start-position=260
  2. # mysqlbinlog mysql.000002 --stop-position=260
  3. # mysqlbinlog mysql.000002 --start-position=260 --stop-position=930
复制代码
4. 截断bin-log(产生新的bin-log文件)

a. 重启mysql服务器

b. # mysql -uroot -p123 -e 'flush logs'

5. 删除bin-log文件
  1. # mysql -uroot -p123 -e 'reset master'
复制代码
二、查询日记

启用通用查询日记
  1. # vim /etc/my.cnf
  2. [mysqld]
  3. log[=dir\[filename]]
  4. # service mysqld restart
复制代码
三、慢查询日记

启用慢查询日记
  1. # vim /etc/my.cnf
  2. [mysqld]
  3. log-slow-queries[=dir\[filename]]
  4. long_query_time=n
  5. # service mysqld restart
复制代码
MySQL 5.6:
  1. slow-query-log=1
  2. slow-query-log-file=slow.log
  3. long_query_time=3
复制代码
查察慢查询日记

测试:
  1. BENCHMARK(count,expr)
  2. SELECT BENCHMARK(50000000,2*3);
复制代码
总结
以上就是这篇文章的全部内容了,渴望本文的内容对各人的学习或者工作具有一定的参考学习代价,如果有疑问各人可以留言交换,谢谢各人对草根技能分享的支持。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作