【Mysql】索引

news2024/9/23 7:33:22

文章目录

  • 一.索引的价值
    • 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的员工看看没有索引时有什么问题:

image-20221022175319188

可以看到耗时接近5秒,这还是在本机一个人来操作,在实际项目中,如果放在公网中,假如同时有1000个人并发查询,那很可能就死机

解决方法:创建索引,创建索引也是需要花费时间的 此时我们可以发现,创建索引之后再进行查找,效率变高了!

image-20221022175519589

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 字节

image-20221022171348942


1)MySQL 和 磁盘进行数据交互的基本单位是 16KB ,这个基本数据单元,在 MySQL 这里叫 做 page ( 注意和系统的page区分 ),只要发生了数据的交互,哪怕只有1bit,也是需要以16KB为基本单位对数据进行交互

2)MySQL 在服务器启动的时候,会预先加载一大块空间自己进行内存管理,这块空间被称为Buffer Pool(MySQL 5.7之中为128KB),mysql对数据做的CRUD操作,根据冯诺依曼体系,mysql不可能直接去访问磁盘, 实际上,数据库中数据做的所有的访问,全部都是再内存中进行的,然后定期的将数据刷新到磁盘当中

image-20221022171601769


3)Page的大小是固定的,就是16KB, 数据量有限,如果不存储数据,就能够存储更多的索引信息,目录Page能够管理更多的Page,否则目录Page管理的页数太少,整颗树的层数就越多,更深,也就意味着从根节点到叶子节点的Page更多,即需要更多的IO

image-20221022172557298

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

  1. 例如有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, '欧阳锋');

image-20221022194022317

  • 不同的 Page ,在 MySQL 中,都是 16KB ,使用 prev 和 next 构成双向链表
  • 因为有主键的问题, MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的,

问:为什么有主键的时候,数据库在插入数据时要对其进行排序呢,我们按正常顺序插入数据不是也挺好的吗

插入数据时排序的目的,就是优化查询的效率,页内部存放数据的模块,实质上也是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的

正是因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,而且,如果运气好,是可以提前结束查找过程的


理解多个Page

通过上面的分析,我们知道,上面页模式中,只有一个功能,就是**在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能,**但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构,前一条数据指向后一条数据,本质上还是通过数据的逐条比较来取出特定的数据,

如果有1千万条数据,一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的,那么,查找特定一条记录,也一定是线性查找,这效率也太低了

image-20221022195545946

页目录

我们在看某一本书的时候,如果我们要看<某一章节>,找到该章节有两种做法

  • 从头逐页的向后翻,直到找到目标内容,
  • 通过书提供的目录,发现该章节在234页(假设),那么我们便直接翻到234页,同时,查找目录的方案,可以顺序找,不过因为目录肯定少,所以可以快速提高定位

本质上,书中的目录,是多花了纸张的,但是却提高了效率 所以,目录,是一种“空间换时间的做法”


提高在单个Page中的查找效率

针对单页情况的页目录:

线性有序的数据可以类比一本书,书的页码从前到后一次排布,而书有目录,目录表明了所在章节的页码范围,在查找指定知识点的时候,可以先从目录找到所在章节,到指定章节再线性查找到指定位置,

针对单页Page我们也可以添加“目录”,如果我们想查找指定位置数据,先找目录到大体位置在线性查找即可,这是一种以空间换时间的做法,比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果,现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率

image-20221022195826598

从目录开始查找就能一次性排除更多数据,当然这一切也是建立在数据有序的基础上,

所以我们也能理解为何通过键值 MySQL 会自动排序? 可以很方便引入目录, 页内目录可以快速查询内部数据

针对多页情况的页目录

Page之间也是以链表的形式组织的,只能线性查找的话效率太低,如何提高在多个Page中的查找效率呢?

page页相连

image-20221023100441828

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,

image-20221023194342861


其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址,

  • 我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历!不用担心,可以在加目录页
  • 这就是传说中的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树

image-20221023195533017

B+树

image-20221023195540343

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分离,也就是叶子节点没有数据,只有对应数据的地址,这种用户数据与索引数据分离的索引方案,叫做非聚簇索引,

image-20221023195802356

InnoDB 的数据和索引是放在一起的,这种用户数据与索引数据在一起索引方案,叫做聚簇索引

image-20221023195836117


它们之间有如下差别:

  1. MyISAM的主键索引(非聚簇索引)的叶子节点存储对应数据的地址;InnoDB 的主键索引(聚簇索引)的叶子节点则是存储用户的整条数据,
  2. MyISAM的普通索引其叶子节点也只是存储对应数据的地址,和主键索引没区别;InnoDB 的普通索引其叶子节点存储索引值和对应的主键,想找到对应的数据还需要进行回表操作,
  3. 聚簇索引主键的插入速度要比非聚簇索引主键的插入速度慢很多,相比之下,聚簇索引适合排序,非聚簇索引不适合用在排序的场合,因为聚簇索引本身已经是按照物理顺序放置的,排序很快,非聚簇索引则没有按序存放,需要额外消耗资源来排序,

image-20221023200030745


三.索引操作

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设为主键

主键索引的特点:

  1. 一个表中,最多有一个主键索引,当然可以是复合主键 (多个列共同当一个主键)
  2. 主键索引的效率高(主键不可重复)
  3. 创建主键索引的列,它的值不能为null,且不能重复
  4. 主键索引的列基本上是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);

唯一索引的特点:

  1. 一个表中,可以有多个唯一索引
  2. 查询效率高,
  3. 如果在某一列建立唯一索引,必须保证这列不能有重复数据,
  4. 如果一个唯一索引上指定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);

此时查询索引可以发现:

image-20221023204159282

普通索引的特点:

  1. 一个表中可以有多个普通索引,普通索引在实际开发中用的比较多,
  2. 如果某列需要创建索引,但是该列有重复的值,那么我们就应该使用普通索引,

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的中文版

例子:创建表并插入数据

image-20221023212515040

查询有没有database数据

  • 如果使用如下查询方式,虽然查询出数据,但是没有使用到全文索引

image-20221023212502567

3.5. explain

explain 命令获取 select 语句的执行计划, 通过 explain 我们可以知道以下信息:表的读取顺序,数据读取操作的类型,哪些索引可以使用,哪些索引实际使用了,表之间的引用,每张表有多少行被优化器查询等信息

  • 可以用explain工具看一下,是否使用到索引

image-20221023212554540

如何使用全文索引

image-20221023212609288

  • match() : 指定被搜索的列
  • against() : 指定要使用的搜索表达式

此时再次使用explain查看

image-20221023212648363


注意: 要使用全文本搜索的时候要注意,你的表引擎用的是不是MyISAM


3.6索引创建原则

  • 比较频繁作为查询条件的字段应该创建索引;
  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件;
  • 更新非常频繁的字段不适合作创建索引;
  • 不会出现在where子句中的字段不该创建索引,

四.验证索引以及其效率

没有索引时,搜索数据:

image-20221023205141374

将empno字段设置为主键,然后再次搜索数据:

image-20221023205157444

如果不使用主键搜索,使用ename: 由于ename字段并不是索引,所以查找的会很慢

image-20221023205230094

将ename设置为普通索引,此时按照ename的查找速度会大大加快:

image-20221023205249347

此时将主键删除,然后再用ename进行查找:

image-20221023205343689

我们可以发现:即使主键已经删除了,普通索引还是在的,并且主键删除的时间很长 主要是因为主键删除以后,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的信息过多,就会产生多次回表操作

索引下推就是过滤的动作由下层的存储引擎层通过使用索引来完成,而减少不必要的回表操作


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

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

相关文章

Spring(Bean 作用域和生命周期)

目录 1. 案例1: Bean作用域的问题 2. 作用域 3. 设置 Bean 的作用域 4. Spring 的执行流程 5. Bean 的生命周期 1. 案例1: Bean作用域的问题 现在有一个公共的 Bean,通过给 A 用户 和 B 用户使用, 然后在使用的过程中 A 偷偷的修改了公共 Bean 的数据, 导致 B 在使用时发…

html静态网站基于动漫网站网页设计与实现共计4个页面

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

基于遗传算法的微电网调度(风、光、蓄电池、微型燃气轮机)(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清…

Docker介绍

目录 docker定义 docker解决了什么问题 docker技术边界 docker给我们带来了哪些改变 docker和虚拟机的区别 docker基本架构 基本架构图 RootFs Linux Namespace 进程命名空间 查看元祖进程命名空间 查看当前用户进程命名空间 容器进程命名空间 容器进程命名空间的…

[激光原理与应用-33]:典型激光器 -5- 不同激光器的全面、综合比较

目录 第1章 五类激光器的性能及应用对比 第2章 各类激光器的区别特点及应用三张表看懂-超米激光 2.1 固体激光器 2.2 气体激光器 2.3 化学激光器 2.4 染料激光器 2.5 半导体激光器 2.6 光纤激光器 2.7 自由电子激光器 第3章 10多种激光器的全面梳理 3.1 激光器的分类…

二叉树-31-37对称二叉树

31. 对称的二叉树 递归&#xff1a; 把原问题化成更小规模的问题&#xff0c;并且具有相同的问题性质&#xff0c;重复调用本身函数 二叉树的递归&#xff0c;是将某个节点的左子树、右子树看成一颗完整的树&#xff0c;那么对于子树的访问或者操作就是对于原树的访问或者操作…

HTML如何制作音乐网站(如何搭建个人音乐网页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Java毕业设计 JSP+MySQL幼儿园信息管理系统

幼儿园网站就是幼儿园的“商标”,在网站上可以看出一所幼儿园的特色和个性,在这个信息时代,建立属于自己的幼儿园网站是最直接的宣传手段。而就目前的幼儿园信息管理系统来说,有很多功能都是华而不实的,不能很好的与幼儿园日常工作相结合,容易导致日常工作出现异常。本系统的题…

【HDU No. 4417】 超级马里奥 Super Mario

【HDU No. 4417】 超级马里奥 Super Mario 杭电OJ 题目地址 【题意】 可怜的公主陷入困境&#xff0c;马里奥需要拯救他的情人。把通往城堡的道路视为一条线&#xff08;长度为n &#xff09;&#xff0c;在每个整数点i 上都有一块高度为hi 的砖&#xff0c;马里奥可以跳的最…

【博客552】git auto-merge原理以及auto-merge的不同模式

git auto-merge原理 1、merge 常见误区 1、git merge 是用时间先后决定merge结果的&#xff0c;后面会覆盖前面的? 答 &#xff1a;git 是分布式的文件版本控制系统&#xff0c;在分布式环境中时间是不可靠的&#xff0c;git是靠三路合并算法进行合并的。 2、git merge 只…

电脑装了w10没有w7流畅怎么办?

如果我们对自己的电脑进行了系统的重装&#xff0c;在电脑装了win10系统之后发现没有win7流畅的话&#xff0c;很多小伙伴不知道是什么情况应该怎么解决。 那么据微点阅读小编所知可能是我们电脑硬件设施的不兼容所导致的。我们可以在官网上查看win10系统的配置要求是否符合自…

一文带你深入理解【Java基础】· 泛型

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Arduino开发实例-DIY风速测量及显示

DIY风速测量及显示 1、应用介绍 本次实例将使用一款具有 NPN 脉冲输出的数字风速计传感器。 NPN脉冲输出风速计效果好,性价比高。另外它仅在 5V 电源下工作。 在本次实例中,将此风速计传感器与 Arduino 板和 0.96 英寸 OLED 显示屏连接。 OLED显示屏将以米/秒为单位显示风速…

[附源码]计算机毕业设计基于SpringBoot的酒店预订系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

​AAAI 2023 | 基于历史对比学习的时序知识图谱推理

©PaperWeekly 原创 作者 | 徐奕单位 | 上海交通大学Acemap研究方向 | 数据挖掘论文标题&#xff1a;Temporal Knowledge Graph Reasoning with Historical Contrastive Learning论文链接&#xff1a;https://arxiv.org/abs/2211.10904代码链接&#xff1a;https://github…

Elasticsearch好用查询插件分享

以前我常用的ES查询工具是Head&#xff0c;作为插件形式在浏览器中运行&#xff0c;挺方便的&#xff0c;后来发现head不太好用&#xff0c;比如在数据浏览的时候&#xff0c;不小心就点击了两个索引&#xff0c;背景色设置的还不够明显&#xff0c;比较容易看错数据的。于是想…

git中rebase和merge的区别

介绍 Merge和Rebase是合并两个分支的操作。都是checkout到某个分支上&#xff0c;然后将别的分支合并&#xff08;变基&#xff09;到本分支上。 注意&#xff1a;本分支&#xff08;head指向的分支&#xff0c;或者经过checkout后的分支&#xff09;会变化&#xff0c;而别的…

在Linux上部署Servlet程序

目录 一、部署环境 1、安装JDK 2、安装Tomcat 3、安装MySQL 二、部署程序 1、构造云服务器上的数据库 2、打包程序 3、部署程序 一、部署环境 为了部署我们自己的web程序&#xff0c;首先需要在Linux上安装程序所依赖的环境~ 1、安装JDK 直接使用yum安装openjdk&…

小程序云开发笔记二

小程序云开发笔记二一、读取数据库播放列表将数据显示到界面二、上拉加载三、上拉刷新四、云函数路由优化tcb-router案例&#xff1a;点击两个按钮调用同一个云函数将music中写成koa风格的云函数五、事件冒泡组件参数properties和data一、读取数据库播放列表将数据显示到界面 …

java同步方法

观看此文 首先 你要了解 java的同步锁 如果不了解 可以观看我的文章 java 同步锁synchronized 解决线程共享数据重复操作问题 那么 从下图 我们可以看到 逻辑代码上被绑了一个同步锁 但这个其实大可以写成一个函数 看起来会美观便捷很多 同步方法的格式如下 修饰符 synchro…