文章目录
- 一.索引的价值
- 1.1. mysql与磁盘交互的基本单位
- 建立共识
- 1.2. 为什么IO交互的基本单位为Page
- 理解单个Page
- 理解多个Page
- 提高在单个Page中的查找效率
- 针对多页情况的页目录
- 为什么选择B+树,而不是其他数据结构?
- 没有主键会怎么创建索引
- 为什么推荐使用自增ID作为主键
- B+树在哪里
- 二.聚簇索引和非聚簇索引
- 三.索引操作
- 3.1. 创建索引
- 创建主键索引
- 唯一索引的创建
- 普通索引的创建
- 3.2. 查询索引
- 3.3. 删除索引
- 3.4. 全文索引的创建
- 3.5. explain
- 3.6索引创建原则
- 四.验证索引以及其效率
- 五.索引覆盖,索引最左匹配原则,索引下推
一.索引的价值
索引:提高数据库的性能,索引是物美价廉的东西了,不用加内存,不用改程序,不用调sql,只要执行正确的 create index ,查询速度就可能提高成百上千倍,但是天下没有免费的午餐,查询速度的提高是以插入,更新,删除的速度为代价的
- 因此索引带来的价值,是提高查找的效率, 但是如果有大量的插入、更新、删除则不建议使用索引
常见索引分为: 主键索引(primary key),唯一索引(unique),普通索引(index),全文索引(fulltext)–解决中子文索引问题
案例: 先整一个海量表(800万条数据),
查询员工编号为998866的员工看看没有索引时有什么问题:
可以看到耗时接近5秒,这还是在本机一个人来操作,在实际项目中,如果放在公网中,假如同时有1000个人并发查询,那很可能就死机
解决方法:创建索引,创建索引也是需要花费时间的 此时我们可以发现,创建索引之后再进行查找,效率变高了!
Mysql与存储
MySQL 给用户提供存储服务,而存储的都是数据,数据在磁盘这个外设当中,磁盘是计算机中的一个机械设备,相比于计算机其他电子元件,磁盘效率比较低,在加上IO本身的特征,可以知道,如何提交效率,是 MySQL 的一个重要话题
磁盘随机访问(Random Access)与连续访问(Sequential Access)
随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据
连续访问:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问
因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问,磁盘是通过机械运动进行寻址的,随机访问不需要过多的定位,故效率比较高
所谓的IO效率低指的是哪方面效率低
- IO=等(磁盘要给你找到你要的一个一个的数据磁道)(主要矛盾)+拷贝(数据从磁盘拷贝到内存)(次要矛盾)
- 磁盘传送数据最耗时的操作不是传送数据,而是磁头寻址!(磁头定位,磁盘旋转)
- 减少IO次数,潜台词是减少数据查找的次数!然后才是读取或者写入外设的次数!
1.1. mysql与磁盘交互的基本单位
操作系统读取磁盘,是以块为单位的,基本单位是 4KB ,MySQL 作为一款应用软件,可以想象成一种特殊的文件系统,它有着更高的IO场景,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (innoDB 存储引擎),) 即:mysql和外设交互的基本单位是16KB 磁盘这个硬件设备的基本单位是 512 字节
1)MySQL 和 磁盘进行数据交互的基本单位是 16KB ,这个基本数据单元,在 MySQL 这里叫 做 page ( 注意和系统的page区分 ),只要发生了数据的交互,哪怕只有1bit,也是需要以16KB为基本单位对数据进行交互
2)MySQL 在服务器启动的时候,会预先加载一大块空间自己进行内存管理,这块空间被称为Buffer Pool(MySQL 5.7之中为128KB),mysql对数据做的CRUD操作,根据冯诺依曼体系,mysql不可能直接去访问磁盘, 实际上,数据库中数据做的所有的访问,全部都是再内存中进行的,然后定期的将数据刷新到磁盘当中
3)Page的大小是固定的,就是16KB, 数据量有限,如果不存储数据,就能够存储更多的索引信息,目录Page能够管理更多的Page,否则目录Page管理的页数太少,整颗树的层数就越多,更深,也就意味着从根节点到叶子节点的Page更多,即需要更多的IO
mysql对数据进行操作,实际上是先将磁盘的数据(16KB为单位)加载到mysql的大块内存当中, 对数据操作完成后,先刷新到内核缓冲区当中, 然后再刷新到磁盘当中
- 当我们要进行数据的增删查改,先将数据拿到MySQL自己的缓冲区里面,然后在缓冲区里面做修改, mysql会定期的把数据刷新到内核缓冲区,然后再刷新到磁盘里
- mysql没资格和磁盘直接交互,必须得通过OS
建立共识
- MySQL 中的数据文件,是以page为单位保存在磁盘当中的,
- MySQL 的 CURD 操作,都需要通过计算,找到对应的插入位置,或者找到对应要修改或者查询的数据,而只要涉及计算,就需要CPU参与,而为了便于CPU参与,一定要能够先将数据移动到内存当中,
- 所以在特定时间内,数据一定是磁盘中有,内存中也有,后续操作完内存数据之后,**以特定的刷新策略,刷新到磁盘,**而这时,就涉及到磁盘和内存的数据交互,也就是IO了,而此时IO的基本单位就是Page,
- 为了更好的进行上面的操作, MySQL 服务器在内存中运行的时候,在服务器内部,就申请了被称为 Buffer_Pool 的的大内存空间,来进行各种缓存,其实就是很大的内存空间,来和磁盘数据进行IO交互,(mysql有自己的内存管理空间)
- 为何更高的效率,一定要尽可能的减少系统和磁盘IO的次数
1.2. 为什么IO交互的基本单位为Page
- 例如有5条记录,要查找id=2的记录,第一次加载id=1,第二次加载id=2,一次一条记录,那么就需要2次IO,如果要找id=5,那么就需要5次IO, 但是如果这5条(或者更多)都被保存在一个Page中(16KB,能保存很多记录),那么第一次IO查找id=2的时候,整个Page会被 加载到MySQL的Buffer Pool中,这里完成了一次IO,但是往后如果在查找id=1,3,4,5等,完全不需要进行IO了,而是直接在内存中进行了,所以,就在单Page里面,大大减少了IO的次数,
1)由冯诺依曼体系可知,IO的时间远远大于CPU运算的时间,如果每次要哪个数据就加载哪个数据,这样IO的次数就提高了,程序所需要的时间就会提高,效率也就降低了, 因此mysql提高效率的本质是减少IO
2)根据局部性原理,我们查找的数据有很大的概率会在同一个区间之内,即同一个page之内,因此我们进行IO的时候就加载一个page到内存之中,如果下次数据在这个区间之间,就不需要进行IO,提高了程序的效率
理解单个Page
Mysql在运行的时候,一定有大量的page需要被替换,一个时间点内,MySQL内部一定存在着大量的Page,MySQL要把这个Page也管理起来? 先描述,在组织!!
struct Page
{
struct Page* next;
struct Page* prev;
char buffer[16kb-其它字段占用的大小]
};
MySQL中的数据往往都是封装成Page结构体的形式,保存在内存中,单个大小16KB,
- 多个Page结构体之间使用前后指针构成双向链表,
- 单个Page结构体内部多条记录也是使用链表形式存储,
对Page的管理变成了对链表的管理,当有数据加载进来,先new一个节点,然后把数据拷贝到buffer里,然后把它连接到链表里面, 当这个节点的内容要刷新:把里面的数据刷新到OS里面,然后把这个节点释放掉
例如: 但我们有主键的时候: 我们乱序插入,最后得到的也会是有序的, 如果没有主键,就是我们插入的顺序
create table if not exists user (
id int primary key, --一定要添加主键哦,只有这样才会默认生成主键索引
age int not null,
name varchar(16) not null
);
--插入多条记录,注意,我们并没有按照主键的大小顺序插入哦,因为有主键,所以得到的结果是有序的
mysql> insert into user (id, age, name) values(3, 18, '杨过');
mysql> insert into user (id, age, name) values(4, 16, '小龙女');
mysql> insert into user (id, age, name) values(2, 26, '黄蓉');
mysql> insert into user (id, age, name) values(5, 36, '郭靖');
mysql> insert into user (id, age, name) values(1, 56, '欧阳锋');
- 不同的 Page ,在 MySQL 中,都是 16KB ,使用 prev 和 next 构成双向链表
- 因为有主键的问题, MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的,
问:为什么有主键的时候,数据库在插入数据时要对其进行排序呢,我们按正常顺序插入数据不是也挺好的吗
插入数据时排序的目的,就是优化查询的效率,页内部存放数据的模块,实质上也是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的
正是因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,而且,如果运气好,是可以提前结束查找过程的
理解多个Page
通过上面的分析,我们知道,上面页模式中,只有一个功能,就是**在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能,**但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构,前一条数据指向后一条数据,本质上还是通过数据的逐条比较来取出特定的数据,
如果有1千万条数据,一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的,那么,查找特定一条记录,也一定是线性查找,这效率也太低了
页目录
我们在看某一本书的时候,如果我们要看<某一章节>,找到该章节有两种做法
- 从头逐页的向后翻,直到找到目标内容,
- 通过书提供的目录,发现该章节在234页(假设),那么我们便直接翻到234页,同时,查找目录的方案,可以顺序找,不过因为目录肯定少,所以可以快速提高定位
本质上,书中的目录,是多花了纸张的,但是却提高了效率 所以,目录,是一种“空间换时间的做法”
提高在单个Page中的查找效率
针对单页情况的页目录:
线性有序的数据可以类比一本书,书的页码从前到后一次排布,而书有目录,目录表明了所在章节的页码范围,在查找指定知识点的时候,可以先从目录找到所在章节,到指定章节再线性查找到指定位置,
针对单页Page我们也可以添加“目录”,如果我们想查找指定位置数据,先找目录到大体位置在线性查找即可,这是一种以空间换时间的做法,比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果,现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率
从目录开始查找就能一次性排除更多数据,当然这一切也是建立在数据有序的基础上,
所以我们也能理解为何通过键值 MySQL 会自动排序? 可以很方便引入目录, 页内目录可以快速查询内部数据
针对多页情况的页目录
Page之间也是以链表的形式组织的,只能线性查找的话效率太低,如何提高在多个Page中的查找效率呢?
page页相连
1),MySQL 中每一页的大小只有 16KB ,单个Page大小固定,所以随着数据量不断增大, 16KB 不可能存下所有的数据,那么必定会有多个页来存储数据,在单表数据不断被插入的情况下**,**
2)MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来,
- 需要注意,上面的图,是理想结构,目前要保证整体有序,那么新插入的数据,不一定会在新Page面,这里仅仅做演示
再次新插入的数据不一定会被放在最后一个Page的末尾,也有可能放在中间某个Page的中间位置,如果我们需要靠后的Page,但为了线性遍历,必须要把之前的所有Page导入内存,这样会造成大量IO,就算需要的Page已经在内存中,线性遍历多个Page的方式也是问题,
解决思路就是给多个Page也搭配上“目录”:把多个Page看作多个记录,把Page中的最小记录的键值作为Page的键值,为其标上目录,
- 创建一个Page其中记录用来存储目录项,目录项由键值和指针构成,指向其下Page最小键值的记录,
- 页内目录管理的是记录,这种目录管理的是页,
提升查找page页的效率:
可在Page之间,也是需要 MySQL 遍历的,遍历意味着依旧需要进行大量的IO,将下一个Page加载到内存,进行线性检测,这样就显得我们之前的Page内部的目录,有点杯水车薪了,
解决方案,用之前的思路,给Page也带上目录,
- 使用一个目录项来指向某一页,而这个目录项存放的就是将要指向的页中存放的最小数据的键值
- 和页内目录不同的地方在于,这种目录管理的级别是页,而页内目录管理的级别是行,
- 其中,每个目录项的构成是:键值+指针,图中没有画全,
存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据,有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个Page,
其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址,
- 我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历!不用担心,可以在加目录页
- 这就是传说中的B+树 ,至此,我们已经给我们的表user构建完了主键索引,
- 我们发现,现在查找的Page数一定减少了,也就意味着IO次数减少了,那么效率也就提高了
概述总结:
由于MySQL和磁盘交互的基本单位是Page,这些Page叫做数据页,我们只需要将数据保存在每个数据页中即可,加载时直接加载一整个数据页,而数据页与数据页之间通过指针连成双向链表,这样就能够获取前一个或后一个数据页,而每条数据之间通过主键进行排序,在每个数据页中也有一个目录,这样在单个数据页中的查询速度就会加快
在单表数据不断被插入的情况下, MySQL 会在容量不足的时候,自动开辟新的数据页来保存新的数据,然后通过指针的方式,将所有的数据页组织起来,但是当数据页多起来的时候,如果加载每个数据页去遍历检测的话,时间会非常慢,这时可以给每个数据页建立起对应的目录,这就是索引,而索引也是通过Page保存的,我们称之为目录页,目录页只放各个下级Page的最小键值
小结
- Page分为目录页和数据页,目录页只放各个下级Page的最小键值,
- 查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数
- MySQL是一个用户级别的文件系统,所以底层对于所谓的数据库的读写,本质也是通过读取文件完成的! page是固定大小!
- 用户是不是访问表的时候,一定要把所有的数据都够建成为index全量的加入buffer pool中 ? 不需要全量加入的 , 需要时换入,
- 一个表添加了主键,mysql自动形成主键索引,意思是: mysql默认会把这张表的所有数据整体以B+树的形式在buffer_pool当中保存.
不保存任何用户数据,只保存它所管理的所有的page的最小记录ID为什么?
- page都是16KB,只保存其他page的指针结构,就意味着能一次保存更多的地址信息!
- page的大小是固定的,数据量有限,不存数据,就能够存更多的索引信息,目录page就能管理更多的page,否则,目录page管理的数据页太少,整棵树的层数更多更深就意味着,从root到叶子节点经历的page更多,需要更多次IO过程
为什么选择B+树,而不是其他数据结构?
数据结构 | 劣势 |
---|---|
链表 | 链表是线性结构的, 查找的时候只能线性遍历,效率太低 |
二叉搜索树 | 在某些场景下可能退化成为线性结构 |
AVL和红黑树 | 和B+树相比,B+树的层数更低,每层进行一次IO,树越矮,IO的次数越少,AVL和红黑树相对来说,比B+树更高 |
Hash | 散列随机的算法特征决定了范围查找时效率不行,只能一个一个找 |
B树 | 底层无法线性遍历,范围查找效率不高。树体存储数据导致树过高 |
B树
B+树
B树节点,既有数据,又有Page指针,而B+,只有叶子节点有数据,其他目录页,只有键值和Page指针
- B+树的的非叶子节点只存储索引,叶子节点存储数据(所以B+树能存储更多的索引,并且查询次数也是一样的)
- B+树每个叶子节点都包含了根节点的键值数据,每个叶子节点的关键字从小到大链
为什么选择B+树
B+树非叶子节点不存储数据,这样一个节点就可以存储更多的key,可以使得树更矮,所以IO操作次数更少,B+叶子节点,全部相连,而B没有,叶子节点相连,更便于进行范围查找
- B+树减少了IO次数,效率更高 : 就是减少了磁盘的访问次数,毕竟内存速度要比磁盘快的多
- B+树查询更稳定,因为所有数据放在叶子节点
- B+树范围查询更好,因为叶子节点指向下一个叶子结点
没有主键会怎么创建索引
上面的组织形式是以主键为索引的组织形式,也就是先按照主键进行排序放到数据页中,再用目录页将数据页组织成B+树,如果没有设置主键该以谁为索引呢?
mysql当中的表是B+树组织的;根本原因在于,任何一张表,一个用户如果没有创建主键mysq会给这个表默认形成一个主键, 当我们没有设置主键的时候,InnoDB会优先选取一个唯一键作为索引,如果表中连唯一键也没有的话,就会自动为每一条记录添加一个叫做DB_ROW_ID的列作为默认主键,该列是一个6字节的自增数值,随着插入而自增,但是这个主键我们看不到,
为什么推荐使用自增ID作为主键
换句话说,为什么主键必须是自增的,用非自增的(比如学号,身份证号)会怎么样?
由于数据页中的记录是按照主键从小到大进行串联的,自增ID决定了后来插入的记录一定会排列在上一条记录的后面,只需要简单添加next_record指针就可以了;如果当前数据页写满,那就放心地直接插入新的数据页中就可以了,
而非自增的主键则不同,它的大小顺序是不确定的,后来插入的记录有可能(而且概率相当大)插入到上一条记录之前(甚至是当前数据页之前),这就意味着需要遍历当前数据页的记录(或者先找到相关的数据页),然后找到自己的位置进行插入;如果当前数据页写满了,只能先找到适合自己位置的数据页,然后在数据页中遍历记录找到自己的合适位置进行插入, 因此使用非自增的主键插入记录花费的时间更长,
B+树在哪里
mysql本身就是一个文件系统, 在磁盘上有完整的B+树和数据,在内存中有局部高频被访问的B+树的核心Page
mysql的查找一定会伴生着mysql进行根据B+树进行page的换入和换出
二.聚簇索引和非聚簇索引
InnoDB 和 MyISAM两种存储引擎索引的区别:
概念 | 存储引擎 | 解释 |
---|---|---|
聚簇索引 | InnoDB | 把索引和数放在一块 |
非聚簇索引 | MyISAM | 把索引和数据分离, 叶节点不存储数据,只存指针 |
MyISAM 最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址,这种用户数据与索引数据分离的索引方案,叫做非聚簇索引,
InnoDB 的数据和索引是放在一起的,这种用户数据与索引数据在一起索引方案,叫做聚簇索引
它们之间有如下差别:
- MyISAM的主键索引(非聚簇索引)的叶子节点存储对应数据的地址;InnoDB 的主键索引(聚簇索引)的叶子节点则是存储用户的整条数据,
- MyISAM的普通索引其叶子节点也只是存储对应数据的地址,和主键索引没区别;InnoDB 的普通索引其叶子节点存储索引值和对应的主键,想找到对应的数据还需要进行回表操作,
- 聚簇索引主键的插入速度要比非聚簇索引主键的插入速度慢很多,相比之下,聚簇索引适合排序,非聚簇索引不适合用在排序的场合,因为聚簇索引本身已经是按照物理顺序放置的,排序很快,非聚簇索引则没有按序存放,需要额外消耗资源来排序,
三.索引操作
3.1. 创建索引
创建主键索引
创建主键以后,MySQL会自动创建主键索引,
- 第一种方式: 在创建表的时候,直接在字段名后指定
primary key
create table user1(id int primary key, name varchar(30));
- 第二种方式:在创建表的最后,指定某列或某几列为主键索引
create table user2(id int, name varchar(30), primary key(id));
- 第三种方式:创建表以后再添加主键
create table user3(id int, name varchar(30));
alter table user3 add primary key(id); #把id设为主键
主键索引的特点:
- 一个表中,最多有一个主键索引,当然可以是复合主键 (多个列共同当一个主键)
- 主键索引的效率高(主键不可重复)
- 创建主键索引的列,它的值不能为null,且不能重复
- 主键索引的列基本上是int
唯一索引的创建
创建唯一键以后,MySQL会自动创建唯一索引,
- 第一种方式:在表定义时,在某列后直接指定unique唯一属性,
create table user4(id int primary key, name varchar(30) unique);
- 第二种方式: 创建表时,在表的后面指定某列或某几列为unique
create table user5(id int primary key, name varchar(30), unique(name));
- 第三种方式 :创建表以后再添加唯一键
create table user6(id int primary key, name varchar(30));
alter table user6 add unique(name);
唯一索引的特点:
- 一个表中,可以有多个唯一索引
- 查询效率高,
- 如果在某一列建立唯一索引,必须保证这列不能有重复数据,
- 如果一个唯一索引上指定not null,等价于主键索引 即:unique not null == primary key
普通索引的创建
- 第一种方式: 在表的定义最后,指定某列为索引
create table user8(id int primary key, name varchar(20), email varchar(30), index(name));
- 第二种方式: 创建完表以后指定某列为普通索引
create table user9(id int primary key, name varchar(20), email varchar(30));
alter table user9 add index(name); #指定某列为普通索引
- 第三种方式: 手动创建一个索引名为 idx_name 的索引
create table user10(id int primary key, name varchar(20), email varchar(30));
create index idx_name on user10(name);
此时查询索引可以发现:
普通索引的特点:
- 一个表中可以有多个普通索引,普通索引在实际开发中用的比较多,
- 如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引,
3.2. 查询索引
- 第一种方法:
show keys from 表名 \G;
- 第二种方法:
show index from 表名\G;
- 第三种方法:
desc 表名;
例子:
mysql> show keys from t4\G;
*************************** 1. row ***************************(第一个主键)
Table: t4 <- 表名
Non_unique: 0 <- 如果索引不能包括重复值则为0,如果可以则为1,也就是平时所说的唯一索引
Key_name: PRIMARY <- 索引的名字
Seq_in_index: 1 <- 索引中的列序列号,从1开始
Column_name: id <- 索引是那个(索引的列名)
Collation: A <- 列以什么方式存储在索引中,大概意思就是字符序
Cardinality: 0 <- 基数的意思,表示索引中唯一值的数目的估计值
Sub_part: NULL <-前置索引的意思,如果列只是被部分地编入索引,则为被编入索引的字符的数目,如果整列被编入索引,则为NULL
Packed: NULL <-指示关键字如何被压缩,如果没有被压缩,则为NULL
Null: <-如果列含有NULL,则含有YES
Index_type: BTREE <- 以B+树的形式构建索引
Comment:
Index_comment: <- 注释的意思
mysql> desc t6;
+-------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id | int(11) | YES | MUL | NULL | | (MUL)表示索引
| name | char(10) | YES | MUL | NULL | |
| grade | int(11) | YES | MUL | NULL | |
+-------+----------+------+-----+---------+-------+
3.3. 删除索引
- 删除主键索引:
alter table 表名 drop primary key;
- 其他索引的删除:
alter table 表名 drop index 索引名;
- 索引名就是show keys from 表名中的Key_name 字段
比如:上述手动创建一个索引名为 idx_name 的普通索引
alter table user10 drop index idx_name;
第三种方法方法:drop index 索引名 on 表名;
drop index idx_name on user8;
3.4. 全文索引的创建
对文章字段或有大量文字的字段进行检索时,会使用到全文索引
MySQL提供全文索引机制,要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文,如果对中文进行全文检索,可以使用sphinx的中文版
例子:创建表并插入数据
查询有没有database数据
- 如果使用如下查询方式,虽然查询出数据,但是没有使用到全文索引
3.5. explain
explain 命令获取 select 语句的执行计划, 通过 explain 我们可以知道以下信息:表的读取顺序,数据读取操作的类型,哪些索引可以使用,哪些索引实际使用了,表之间的引用,每张表有多少行被优化器查询等信息
- 可以用explain工具看一下,是否使用到索引
如何使用全文索引
- match() : 指定被搜索的列
- against() : 指定要使用的搜索表达式
此时再次使用explain查看
注意: 要使用全文本搜索的时候要注意,你的表引擎用的是不是MyISAM
3.6索引创建原则
- 比较频繁作为查询条件的字段应该创建索引;
- 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
- 更新非常频繁的字段不适合作创建索引;
- 不会出现在where子句中的字段不该创建索引,
四.验证索引以及其效率
没有索引时,搜索数据:
将empno字段设置为主键,然后再次搜索数据:
如果不使用主键搜索,使用ename: 由于ename字段并不是索引,所以查找的会很慢
将ename设置为普通索引,此时按照ename的查找速度会大大加快:
此时将主键删除,然后再用ename进行查找:
我们可以发现:即使主键已经删除了,普通索引还是在的,并且主键删除的时间很长 主要是因为主键删除以后,DB_ROW_ID就会成为主键,那Page就会重新排列,并且普通索引叶子节点的主键值也会变为DB_ROW_ID,
- 当我们没有设置主键的时候,InnoDB会优先选取一个唯一键作为索引,如果表中连唯一键也没有的话,就会自动为每一条记录添加一个叫做DB_ROW_ID的列作为默认主键,该列是一个6字节的自增数值,随着插入而自增,但是这个主键我们看不到,
五.索引覆盖,索引最左匹配原则,索引下推
索引覆盖
如果一个表中两个字段,比如id和name,两者为复合索引
- 如果今天我们指向查找一个id对应的name,那么它在索引的过程中就能找到id和name,不需要到叶子节点,相当于覆盖了后面的获取主键然后回表操作
索引最左匹配原则
- 还是id和name为复合索引,它们在索引过程中是以id排序,id相同才以name排序,此时如果以name查找,就不能用这个复合索引,因为并不是以name排序的,这时候应该以name创建一个普通索引,
索引下推
- 还是id和name为复合索引,我们要查询所有id为10,name为
’%李’
的人的信息,把%加在name字段前面的时候,是无法利用索引的顺序性来进行快速比较的,也就是说这条查询语句中只有id字段可以使用索引进行快速比较和过滤,所以会筛选出所有id为10的主键,然后进行回表操作,如果id为10的信息过多,就会产生多次回表操作
索引下推就是过滤的动作由下层的存储引擎层通过使用索引来完成,而减少不必要的回表操作