• 【Mysql】索引


    一.索引的价值

    索引:提高数据库的性能,索引是物美价廉的东西了,不用加内存,不用改程序,不用调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-其它字段占用的大小]
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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, '欧阳锋');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    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));
    
    • 1
    • 第二种方式:在创建表的最后,指定某列或某几列为主键索引
     create table user2(id int, name varchar(30), primary key(id));
    
    • 1
    • 第三种方式:创建表以后再添加主键
    create table user3(id int, name varchar(30));
    alter table user3 add primary key(id); #把id设为主键
    
    • 1
    • 2

    主键索引的特点:

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

    唯一索引的创建

    创建唯一键以后,MySQL会自动创建唯一索引,

    • 第一种方式:在表定义时,在某列后直接指定unique唯一属性,
    create table user4(id int primary key, name varchar(30) unique);
    
    • 1
    • 第二种方式: 创建表时,在表的后面指定某列或某几列为unique
    create table user5(id int primary key, name varchar(30), unique(name));
    
    • 1
    • 第三种方式 :创建表以后再添加唯一键
    create table user6(id int primary key, name varchar(30)); 
    alter table user6 add unique(name);
    
    • 1
    • 2

    唯一索引的特点:

    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));
    
    • 1

    • 第二种方式: 创建完表以后指定某列为普通索引
    create table user9(id int primary key, name varchar(20), email varchar(30)); 
    alter table user9 add index(name);  #指定某列为普通索引
    
    • 1
    • 2

    • 第三种方式: 手动创建一个索引名为 idx_name 的索引
    create table user10(id int primary key, name varchar(20), email varchar(30));  
    create index idx_name on user10(name);
    
    • 1
    • 2

    此时查询索引可以发现:

    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    |       |
    +-------+----------+------+-----+---------+-------+
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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;
    
    • 1

    第三种方法方法:drop index 索引名 on 表名;

    drop index idx_name  on user8;
    
    • 1

    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的信息过多,就会产生多次回表操作

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


  • 相关阅读:
    【leetcode刷题之路】面试经典150题(6)——图+图的广度优先搜索+字典树+回溯
    python进阶
    ESP32FreeRTOS开发笔记:1.双核并行
    Linux【网络】数据链路层
    Docker镜像构建之docker commit
    【LeetCode刷题(数据结构与算法)】:二叉树的中序遍历
    FFmpeg 的简介与安装
    Hbase的shell命令(详细)
    神经网络的基本特性有,简述什么是神经网络
    神马搜索引擎
  • 原文地址:https://blog.csdn.net/chuxinchangcun/article/details/127942290