【MySQL】索引特性

目录

MySQL索引特性

索引的概念

认识磁盘

磁盘的结构

磁盘的随机访问(Random Access)与连续访问(Sequential Access)

MySQL与磁盘交互的基本单位

索引的理解

观察主键索引现象

推导主键索引结构的构建

索引结构可以采用哪些数据结构

聚簇索引 VS 非聚簇索引

索引操作

创建主键索引

创建唯一索引

创建普通索引

创建全文索引

查询索引

删除索引

索引创建原则


MySQL索引特性

索引的概念

索引的概念

  • 数据库中存储的数据都是以记录为单位的,如果查询数据时直接一条条遍历表中的数据记录,那么查询的时间复杂度就是 O(N)。
  • 索引的价值在于提供海量数据的检索速度,只要执行了正确的创建索引的操作,查询速度就可能提高上千倍。当一张表创建索引后,在数据库底层就会为表中的数据记录构建特定的数据结构,后续的插叙表中数据时就能通过查询该数据结构快速定位到目标数据。
  • 索引虽然能够提高数据的查询的速度,但在一定程度上也会降低数据增删改查的效率,因为这时对表中的数据进行增删改操作时,除了需要进行对应的增删改操作之外,可能还需要对底层建立的数据结构进行调整维护。

常见的索引分为:

  • 主键索引(primary key)。
  • 唯一索引(unique)。
  • 唯一索引(unique)。
  • 全文索引(fulltext)。

索引的价值

使用如下SQL创建一个海量数据表:

drop database if exists `index_demon`;
create database if not exists `index_demon` default character set utf8;
use `index_demon`;-- 构建一个8000000条记录的数据
-- 构建的海量表数据需要有差异性,所以使用存储过程来创建-- 产生随机字符串
delimiter $$
create function rand_string(n INT)
returns varchar(255)
begin
declare chars_str varchar(100) default
'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i = i + 1;
end while;
return return_str;
end $$
delimiter ;-- 产生随机数字
delimiter $$
create function rand_num( )
returns int(5)
begin
declare i int default 0;
set i = floor(10+rand()*500);
return i;
end $$
delimiter ;-- 创建存储过程,向雇员表添加海量数据
delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
set autocommit = 0;
repeat
set i = i + 1;
insert into EMP values ((start+i)
,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
until i = max_num
end repeat;
commit;
end $$
delimiter ;-- 雇员表
CREATE TABLE `EMP` (`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇员编号',`ename` varchar(10) DEFAULT NULL COMMENT '雇员姓名',`job` varchar(9) DEFAULT NULL COMMENT '雇员职位',`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇员领导编号',`hiredate` datetime DEFAULT NULL COMMENT '雇佣时间',`sal` decimal(7,2) DEFAULT NULL COMMENT '工资月薪',`comm` decimal(7,2) DEFAULT NULL COMMENT '奖金',`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部门编号'
);-- 执行存储过程,添加8000000条记录
call insert_emp(100001, 8000000);

上述SQL中创建了一个名为index_demon的数据库,在该数据库中创建了一个名为EMP的员工表,并向表当中插入了八百万条记录。

将上述SQL保存到文件中,然后在MySQL中使用source命令依次执行文件中的SQL即可。如下:

SQL执行完毕后查看数据库就能看到一个名为index_demon的数据库。如下:

进入该数据库,在数据库中可以看到一个名为EMP的员工表。如下:

由于EMP表中有八百万条记录,因此在查看EMP表中的数据时可以带上limit子句。如下:

通过desc命令可以看到,目前EMP员工表中没有建立任何索引。如下:

查询EMP表中指定工号的员工信息,可以看到每次查询过程都需要花费4.5秒左右。如下:

当我们给员工表中的工号建立索引后,数据库底层就会为员工表中的数据记录构建特定的数据结构,需要注意的是,由于当前员工表中的数据量较大,因此建立索引时也需要花费较长时间。如下:

这时再查询EMP表中指定工号的员工信息,可以看到几乎检测不到查询时耗费的时间。如下:

根本原因就是,给员工工号创建索引后再根据员工工号来查询数据,这时就能够直接通过底层建立的数据结构来快速定位到目标数据,从而提高数据的检索速度,这就是索引的价值。

认识磁盘

  • MySQL给用户提供存储服务,存储的数据在磁盘这个外设当中。
  • 磁盘是计算机中的一个机械设备,相比于计算机的其他电子元件,磁盘的效率是比较低的。
  • 而如何提高效率是MySQL的一个重要话题,因此我们有必要了解一下磁盘的相关内容。
磁盘的结构

磁盘的整体结构

磁盘的整体结构如下:

部分说明:

  • 永磁铁: 机械硬盘的存储方式与磁带比较类似,磁体具有记忆的功能,永磁铁是为了保证磁性的稳定。
  • 音圈马达: 硬盘读取数据的关键部位,主要作用是将存储在磁盘上的信息转换为电信号向外传输。
  • 主轴: 保证电机稳定的转动,磁盘转动才能读出数据。
  • 空气滤波片: 过滤空气硬盘透气孔中进入的空气,保证硬盘内部清洁,同时还可以防止硬盘内部的零件氧化,确保硬盘安全使用。
  • 磁盘: 硬盘一般都是铝合金制作的,主要是用来存储文件的。
  • 磁头: 用来读取盘片上的信息。
  • 串行接口: 用来连接电脑与硬盘的接口,起到传输的作用。

磁盘中的一个盘片

一个磁盘由多个盘片叠加而成,盘片的表面涂有磁性物质,这些磁性物质就是用来记录二进制数据的,因为盘片的正反两面都可涂上磁性物质,因此一个盘片有两个盘面。

磁盘中的一个盘片如下:

部分说明:

  • 磁道: 磁盘表面被分为许多同心圆,每个同心圆称为一个磁道,每个磁道都有一个编号,最外面的是0磁道。
  • 扇区: 每个磁道被划分成若干个扇区,每个扇区的存储容量为512字节,每个扇区都有一个编号。

说明一下:

  • 由于每个扇区的存储容量相同,因此最内侧磁道上的扇区数据密度最大,而最外侧磁道上的扇区数据密度最小。
  • 近三十年来,扇区大小一直是512字节,但最近几年正在迁移到更大、更高效的4096字节扇区,通常称为4K扇区。
  • 数据库文件就是保存在磁盘中的一个个扇区中的,因此找到一个文件本质就是,在磁盘上找到保存该文件的所有扇区。

扇区的定位方式

  • 一个磁盘由多个盘片叠加而成,每个盘片有两个盘面,所有盘面中半径相同的同心磁道构成一个柱面。
  • 每个盘面都有一个对应的磁头,每个磁头都有一个编号,所有的磁头都是连在同一个磁臂上的。

示意图如下:

定位扇区时采用CHS寻址方式:

  • 磁头(Heads): 每个盘面都有一个对应的磁头,因此确定了磁头也就确定了数据在哪一个盘面。
  • 柱面(Cylinder): 所有盘面中半径相同的同心磁道构成柱面,因此在确定了数据在哪一个盘面的基础上,再确定柱面也就确定了数据在该盘面上的哪一个磁道。
  • 扇区(Sector): 每个磁道被划分成若干个扇区,因此在确定了数据在哪一个磁道的基础上,再确定扇区也就确定了数据在该磁道上的哪个扇区。

简单来说,CHS寻址方式就是先通过H确定数据所在的盘面,再通过C确定数据所在的磁道,最后通过S定位到目标扇区。

说明一下:

  • CHS寻址方式是磁盘定位扇区的方式,但实际CHS寻址方式对磁盘以外的设备来说没什么作用,因此系统软件在定位磁盘上的数据时采用的是LBA(Logical Block Address,逻辑区块地址)。
  • LBA是描述计算机存储设备上数据所在区块的通用机制,LBA和CHS之间可以通过计算公式进行相互转换,LBA存在的意义就是对底层逻辑器件进行虚拟化,让系统软件可以不用关心底层硬件具体的寻址方式,而实际底层硬件采用的还是CHS寻址方式。

操作系统与磁盘交互的基本单位

操作系统与磁盘进行IO交互的基本单位是4KB,而不是扇区的大小512字节,原因如下:

  • 物理内存实际是被划分成了一个个 4KB 大小的页框,磁盘上的数据也会被划分成为一个个 4KB大小的页帧,因此操作系统与磁盘以 4KB为单位进行IO交互,就能提高数据加载和保存的效率。
  • 操作系统与磁盘进行IO交互时,如果直接以扇区的大小为IO的基本单位,那么这时系统的IO代码和硬件就是强相关的,将来当硬件的扇区大小发生变化事就需要对应修改操作系统的IO代码。
  • 此外,以扇区的大小作为IO的基本单位太小了,这就意味着读取相同数据内容时,需要进行更多次的磁盘访问,而磁盘的效率是比较低的,这样IO效率就降低了。

因此操作系统与磁盘以4KB作为IO交互的基本单位,一方面是为了提高IO效率,另一方面是为了实现硬件和系统的解耦。

磁盘的随机访问(Random Access)与连续访问(Sequential Access)

随机访问与连续访问

  • 随机访问: 本次IO所给出的扇区地址与上次IO给出的扇区地址不连续,磁头在两次IO操作之间需要做比较大的移动动作才能找到目标扇区进行IO。
  • 连续访问: 本次IO所给出的扇区地址与上次IO给出的扇区地址是连续的,磁头很快就能找到目标扇区进行IO。

需要注意的是,尽管两次IO是在同一时刻发出的,但如果它们请求的扇区地址相差很大,那也只能称为随机访问,因为连续访问中的连续指的是访问的扇区地址的连续,而不是访问时间的连续,由于连续访问不需要过多的定位,因此效率比较高。

MySQL与磁盘交互的基本单位

MySQL与磁盘交互的基本单位

MySQL作为一款应用软件,可以想象成是一种特殊的文件系统,它有着更高频的IO场景,因此为了提高基本的IO效率,MySQL与磁盘交互的基本单位是16KB,这个基本数据单元在MySQL这里也叫做Page。

通过show命令查看系统中的全局变量,可以看到InnoDB存储引擎交互的基本单位是16KB。如下:

说明一下: 本篇博客中没有做特殊说明的地方,都是以InnoDB存储引擎为例进行讲解的。

Buffer Pool

  • 在MySQL中进行的各种CRUD操作时,都需要先通过计算找到对应的操作位置,只要涉及计算就需要CPU参与,而冯诺依曼体系结构决定了CPU只能和内存打交道,因此为了便于CPU参与,就需要先将数据加载到内存当中。
  • 所以在特定的时间内,MySQL中的数据一定是同时存在于磁盘和内存中的,当操作完内存数据后,再以特定的刷新策略将内存中的数据刷新到磁盘当中,这时MySQL和磁盘进行数据交互的基本单位就是Page。
  • 为了更好的支持上述操作,MySQL服务器在启动的时候会预先申请一块内存空间来进行各种缓存,这块内存空间叫做Buffer Pool,后续磁盘中加载的数据就会保存在Buffer Pool中,刷新数据时也就是将Buffer Pool中的数据刷新到磁盘。
  • 由于内核中是有内核缓冲区的,因此MySQL从磁盘读取数据时,需要先将数据从磁盘读取到内核缓冲区,再将数据从内核缓冲区读取到Buffer Pool,MySQL将数据刷新到磁盘时,同样需要先将数据从Buffer Pool刷新到内核缓冲区,再将数据从内核缓冲区刷新到磁盘。

因此所谓的操作系统和磁盘交互的基本单位是4KB,就是指内核缓冲区与磁盘之间是以4KB为单位进行交互的,而MySQL的Buffer Pool和磁盘实际并不是直接的交互的,因此所谓的MySQL与磁盘交互的基本单位是16KB,指的是MySQL和Buffer Pool与内核缓冲区之间是以16KB为单位进行交互的。只不过在说的时候更关注的是MySQL和磁盘之间的关系,所以直接说的是MySQL与磁盘交互的基本单位是16KB,相当于忽略了中间的内核缓冲区。

示意图如下:

索引的理解

观察主键索引现象

观察主键索引现象

创建一个用户表,表当中包含用户的id、年龄和姓名,并将用户的id设置成主键。如下:

 

创建表完毕后向表中插入一些数据,并且插入数据时没有按照主键的大小顺序插入。如下:

但最终当我们查看表中的数据时,却发现显示出来的数据是按照主键进行有序排列的。如下:

 根本原因就是,因为我们创建表时设置了主键,即便向表中插入数据时是乱序插入的,MySQL底层也会自动按照主键对插入的数据进行排序。

为什么MySQL与磁盘交互的基本单位是Page?

MySQL与磁盘进行交互时为什么不是按需交互,而是以Page为基本单位进行交互的?

  • 当我们查询表中的某一条记录时,如果MySQL只从磁盘中将这条记录加载到内存当中,那么当我们继续查询表中的其他记录时,MySQL就一定需要再次与磁盘进行IO交互。
  • 而如果当我们查询表中的某一条记录时,MySQL直接将这条记录所在的整个Page都加载到内存当中,那么当我们继续查询表中的其他记录时,MySQL很可能就不再需要与磁盘进行IO交互了,因为这条记录很可能也在被加载进来的Page当中,这时直接在内存中进行查询即可,大大减少了IO的次数。
  • 当然,我们不能保证用户下一次要访问的数据一定就在本次加载进来的Page当中,但是根据统计学原理,当一个数据正在被访问时,那么下一次有很大可能会访问其周围的数据(局部性原理),因此我们有较大概率保证用户下一次要访问的数据和本次访问的数据在同一个Page当中,如果局部性原理没有起作用,那就再把对应的Page加载到内存当中即可。

也就是说,MySQL与磁盘进行交互时以Page为基本单位,可以减少与磁盘IO交互的次数,进而提高IO的效率。

推导主键索引结构的构建

单个Page

  • MySQL中要管理很多数据文件,在运行期间一定有大量的Page需要被换入换出,因此MySQL一定需要将内存中大量的Page管理起来。
  • MySQL将内存中的每一个Page都用一个结构体描述起来,然后再将各个结构体以双链表的形式组织起来,因此一个Page结构体内部既包含数据字段,也包含属性字段。
  • 此外,为了方便后续数据的插入和删除,每个Page结构体内部存储的数据记录会以单链表的形式组织起来,并且各个记录之间会按照主键进行排序。

假设上述测试表中的记录都在同一个Page当中,那么该Page的结构大致如下:

说明一下:

  • 每个Page结构体内部的数据会按照主键进行排序,目的是为了优化数据查询的效率,因为单链表在查找的时候是顺序查找的,有序就意味着在查找过程中有机会提前结束查询过程。
  • 这也就是前面所说的,只要设置了主键,即便向表中插入的数据时乱序的,MySQL底层也会自动按照主键对插入的数据进行排序,因此查询得到的数据是按照主键进行有序排序的。

单个Page内创建页内目录

  • Page结构体内部存储的数据记录是以单链表的形式组织起来的,当页内部的数据增多时,本质在页内部进行的还是线性遍历,效率低下。
  • 这是可以在Page结构体内部引入页内目录,将Page结构体内部存储的数据记录按照主键划分为若干区域,页内目录中存储着这若干区域的最小键值。
  • 在Page结构体内部引入页内目录后,在页内部查询数据时就可以先通过页内目录找到目标数据所在区域的起始记录,然后再从该记录开始向后遍历找到目标记录。

比如在之前的Page内部引入页内目录后的结构大致如下:

说明一下:

  • 在每个Page结构体内部引入页内目录,目的是为了加速在单个Page内部数据查询的效率。由于这个页内目录也是保存在Page内部的,而单个Page的大小是固定的,因此添加页内目录后Page内部能够保存的数据记录变少了,所以在Page内部引入页内目录本质是一种空间换时间的做法,就像给书添加目录需要花费更多的纸张一样。
  • 每个Page结构体内部的数据会按照主键进行排序,其实就是为了引入页内目录,因为只有数据按照主键排序后引入页内目录才有意义,就像书中每一页都是按照页码进行排序的一样,如果一本书的页码是乱序的,那么它的目录根本就没有意义。

多个Page

  • 随着数据量不断增大,单个Page中无法存下所有数据,这时就需要用多个Page来存储数据。
  • 这时在查询数据时就需要,先遍历Page双链表确定目标数据在哪一个Page,然后再在该Page内部找到目标数据。

多个Page的示意图如下:

Page之上创建页目录

  • 虽然在单个Page内部能够通过页内目录来快速定位数据,但在遍历Page双链表寻找目标Page时本质还是线性遍历。
  • 这时可以给各个Page结构体也建立页目录,页目录中的每个目录项都指向一个Page,而这个目录项放的就是指向的Page中存放的最小数据的键值。
  • 在给各个Page结构体建立页目录后,在查询数据时就可以先遍历页目录找到数据所在的Page,然后再给Page内部找到目标数据。

给各个Page建立页目录后的示意图如下:

说明一下:

  • 这里的页目录与之前的页内目录的区别在于,页目录管理的是一个个的Page,而页内目录管理的是一条条的记录。此外,页内目录与其管理的多条记录是保存在同一个Page中的,而页目录是重新申请的一个Page结构体来保存的。
  • 随着数据量不断增大,Page变得越来越多,这时一个页目录无法管理所有的Page,这时就需要更多个的页目录。这些页目录也是一个个的Page结构体,只不过这些Page结构体中存放的不是数据记录,而是各个Page的目录信息。但是在MySQL看来,无论Page当中存储的是什么数据,都应该被管理起来,因此这些Page页目录也需要用双链表连接起来。
     

页目录之上再创建页目录

  • 就算给各个Page结构体也建立了页目录,但随着数据量不断增大,页目录的数量也会越来越多,这时在遍历页目录寻找目标Page时本质还是线性遍历。
  • 类似的,我们可以不断在页目录之上再创建目录,最终一定能够得到一个入口页目录,这时在查询数据时就可以从入口页目录开始不断查询页目录,最终找到目标数据所在的Page,然后在该Page内部找到目标数据。

最终构建出来的结构如下:

说明一下:

  • 最终构建出来的实际就是一棵B+树,这棵B+树就是InnoDB的索引结构,其中每一层Page的作用就是加速它的下一层的查找效率。
  • 如果我们创建表时设置了主键,那么MySQL在底层就会自动将这张表中的的数据以B+树的形式组织起来,保存在Buffer Pool当中,当我们查询数据时就可以通过查询这棵B+树来提高查询效率。
  • MySQL中可能同时有大量的表正在被处理,因此Buffer Pool中可能会存在多个索引结构,也就是同时存在多个B+树结构,当我们查询表时访问的就是这张表对应的B+树结构。

B+树中的Page结点是否需要全量加入到Buffer Pool中?

  • 当对MySQL中的某张表进行增删改查操作时,不需要将其对应B+树种的所有结点全量加入到Buffer Pool 中,甚至在刚开始只需要将对应B+树的根节点加入到Buffer Pool中。
  • 当后续访问表中的数据时,再将该数据对应路径上的结点加入到Buffer Pool中即可,对于其他不需要的结点根本不用加入到Buffer Pool中,这一点和操作系统中的页表是很像的。
  • 此外,在刷新数据时也不需要将B+树种的所有结点都进行刷新,在Page结构体中有一个标志位用来标志当前Page是否被修改过,如果被修改过则说明这是一个脏数据,再刷新数据时只有脏数据才需要被刷新到磁盘上。
  • 由于B+树种的结点都是16KB大小的Page,因此无论刷新数据到磁盘,从磁盘加载数据到Buffer Pool,都是以Page为单位进行的,这也就是所谓的MySQL与磁盘交互的基本单位是Page。

如果把这棵B+树逆时针旋转90度,就会发现这其实就是操作系统中的页表结构,本质操作系统中的页表也是B+树结构。如下:

 以32位平台为例,页表将一个虚拟地址转换成物理地址的过程如下:

  1. 选择虚拟地址的前10个比特位在页目录中进行查找,扎到对应的页表;
  2. 再继续选择虚拟地址后续的10个比特位在对应的页表当中进行查找,找到物理内存中对应页框的起始地址;
  3. 最后选择虚拟地址中剩下的12个比特位作为偏移量,从对应页框的起始地址向后便宜,最终得到的就是转换后的物理地址。

说明一下:

  • 12个比特位有 2^12 种取值,而 2^12字节对应就是4KB,所以物理内存中一个页框的大小就是4KB,这也就是为什么操作系统与磁盘交互的基本单位是4KB的原因。
  • 此外,页表中的各个B+树结点也不需要全量加入到内存中,而只需要加入访问到的结点即可,所以页表占用的内存大小实际是可控的,这也就是为什么二级页表可行的原因。
索引结构可以采用哪些数据结构

索引结构可以采用哪些数据结构

索引结构可以采用哪些数据结构

  • 链表:查找时是线性遍历,效率太低。
  • 普通二叉搜索树:可能退化成线性结构,这时查找还是线性遍历。
  •  AVL树和红黑树:虽然保证了二叉树是绝对或近似平衡的,不会退化成线性结构,但AVL树和红黑树都是二叉树结构,这就意味着树的层高会比较高,而查询数据时都是从根结点开始向下进行查找的,这也就意味着在查询过程中需要遍历更多结点,如果这些结点还没有被加载到Buffer Pool中,这时就需要进行更多次的IO操作,所以最终没有选择其作为索引结构。
  • 哈希表:官方的索引实现方式中MySQL是支持HASH的,只不过InnoDB和MyISAM存储引擎并不支持。哈希表的优点就是它的时间复杂度是 O ( 1 ) O(1)O(1) 的但哈希表也有一个缺点就是不利于进行数据的范围查找。

下面是几个常见的存储引擎,与其所支持的索引类型: 

存储引擎支持的索引类型
InnoDBBTREE
MyISAMBTREE
MEMORY/HEAPHASH、BTREE
NDBHASH、BTREE
聚簇索引 VS 非聚簇索引

MyISAM存储引擎 - 主键索引结构

  • 之前推导的主键索引结构是InnoDB存储引擎的主键索引结构,而MyISAM存储引擎同样采用B+树作为索引的基本数据结构。
  • 与InnoDB存储引擎的B+树不同的是,MyISAM存储引擎的B+树的叶子结点存放的不是数据记录,而是数据记录对应的地址。

比如下图为MyISAM存储引擎的主键索引结构,其中Col1为主键。如下:

MyISAM存储引擎 - 普通索引结构

  • MyISAM存储引擎的普通索引采用的也是B+树结构,与主键索引唯一不同的地方就是普通索引的B+树中的键值可以重复。
  • 因此一张表可能会同时存在多个B+树结构,但由于MyISAM存储引擎的B+树叶子结点中,存储的是对应的数据记录的地址,因此有效数据只会存储一份。

比如下图为MyISAM存储引擎的普通索引结构,其中Col2为索引列。如下:

InnoDB存储引擎 - 普通索引结构

  • InnoDB存储引擎的普通索引采用的也是B+树结构,但普通索引的B+树中的键值可以重复,并且B+树的叶子结点中存储的不是数据记录,而是对应数据记录的主键值。
  • 当根据普通索引查询数据时,会先查找普通索引对应的B+树找到目标记录的主键值,然后再查找主键索引对应的B+树找到目标记录,这个过程就叫做回表查询。

比如下图为InnoDB存储引擎的普通索引结构,其中Col3为索引列。如下:

说明一下:

  • InnoDB存储引擎的普通索引的B+树叶子结点中没有保存整条数据记录,是为了节省空间,因为同一张表可能会创建多个普通索引,每个普通索引的B+树种都会保存一份数据,这样会造成数据冗余,所以通过回表查询主键索引对应的B+来获取整个数据记录,该做法本质是一种以时间换取空间的做法。
  • 当根据普通索引查询数据时,其实也不一定需要进行回表查询,因为有可能我们要查询的就是这条记录所对应的主键值,因此查询完普通索引对应B+树后即可完成查询。
  • 采用InnoDB存储引擎建立的每张表都会有一个主键,就算用户没有设置,InnoDB也会自动帮你创建一个不可见的主键,因为完整数据记录只会存储在主键索引对应的B+树中的,因此采用InnoDB存储引擎建立的表必须有主键。

聚簇索引 VS 非聚簇索引

  • 聚簇索引: 像InnoDB存储引擎这种,将数据记录与索引结构放在一起的索引方案,叫做聚簇索引。
  • 非聚簇索引: 像MyISAM存储引擎这种,将数据记录与索引结构分离的索引方案,叫做非聚簇索引。

当采用InnoDB存储引擎创建表时,在数据库对应的目录下会新增两个文件。如下:

当采用MyISAM存储引擎创建表时,在数据库对应的目录下会新增三个文件。如下:

说明一下:

  • 采用InnoDB和MyISAM存储引擎创建表时都会生成xxx.frm文件,该文件中存储的是表结构相关的信息。
  • 采用InnoDB存储引擎创建表时会生成一个xxx.ibd文件,该文件中存储的是索引和数据相关的信息,这就是所谓的聚簇索引,索引和数据是存储在同一个文件中的。
  • 采用MyISAM存储引擎创建表时会生成一个xxx.MYD文件和一个xxx.MYI文件,其中xxx.MYD文件中存储的是数据相关的信息,而xxx.MYI文件中存储的是索引相关的信息,这就是所谓的非聚簇索引,索引和数据是分开存储的。

索引操作

创建主键索引

方式一

创建表时,直接在对应的字段名后指定primary key。如下:

方式二

在创建表的最后,指定某列或某几列为主键索引。如下:

方式三

创建表后,使用alter命令给指定字段添加主键索引。如下:

主键索引的特点

主键索引的特点如下:

  • 一个表中,最多只能有一个主键索引,一个主键可以由多个列同时承担。
  • 主键索引的查询效率高。
  • 创建主键索引的列,其列值不能为NULL,且不能重复。
  • 主键索引的列一般是数字类型。
创建唯一索引

方式一

在创建表时,直接在对应的字段名后指定unique。如下:

方式二

在创建表的最后,指定某列或某几列为唯一索引。如下:

方式三

创建表后,使用alter命令给指定字段添加唯一索引。如下:

唯一索引的特点

唯一索引的特点如下:

  • 一个表中,可以有多个唯一索引,一个唯一键可以由多个列同时承担。
  • 唯一索引的查询效率高。
  • 创建唯一索引的列,其列值可以为NULL,但是不能重复。
  • 如果给唯一索引设置NOT NULL属性,则等价于主键索引。
创建普通索引

方式一

在创建表的最后,指定某列或某几列为普通索引。如下:

方式二

创建表后,使用alter命令给指定字段添加普通索引。如下:

方式三

创建表后,使用create命令给指定字段创建普通索引,并指定索引名。如下:

普通索引的特点

普通索引的特点如下:

  • 一个表中,可以有多个普通索引,一个普通索引可以由多个列同时承担。
  • 创建普通索引的列,其列值可以为NULL,也可以重复。
创建全文索引

全文索引案例

全文索引比较常见的案例就是对文章中的词进行搜索,比如下面创建一个文章表,表当中包含文章的id、文章名称、文章内容,并在创建表的最后通过fulltext给title和body列创建全文索引。如下:

下面向表当中插入一些测试数据。如下:

如果要查询哪些文章中包含database关键字,我们可以通过模糊匹配进行查找。如下:

但实际这种查找方式并没有用到全文索引,在SQL语句前面加上explain,可以看到key对应的值为NULL,表示这条SQL在执行过程中没有用到任何索引。如下:

如果要通过全文索引来查询,需要使用match against进行搜索。如下:

在这条SQL语句前面加上explain,可以看到key对应的值为title,表示这条SQL在执行过程中用到了索引名为title的索引。如下:

说明一下:

  • MyISAM存储引擎是支持全文索引的,而InnoDB存储引擎是在5.6以后才开始支持全文索引的。
  • 同时使用title和body建立全文索引时,相当于建立了一个复合索引,默认会选择fulltext中的第一个列名作为这个复合索引的索引名,所以这里explain中key对应的索引名为title。
  • 由于是title和body共同建立的全文索引,所以如果文章当中没有出现关键字,但文章名称中出现了关键字则也会被筛选出来(当前示例没有体现出来)。
查询索引

方式一

使用show keys from 表名SQL查询,比如查询articles表中的索引信息。如下:

说明一下:

  • Table: 表示创建索引的表的名称。
  • Non_unique: 表示该索引是否是唯一索引,如果是则为0,如果不是则为1。
  • Key_name: 表示索引的名称。
  • Seq_in_index: 表示该列在索引中的位置,如果索引是单列的,则该列的值为1,如果索引是复合索引,则该列的值为每列在索引定义中的顺序。
  • Column_name: 表示定义索引的列字段。
  • Collation: 表示列以何种顺序存储在索引中,“A”表示升序,NULL表示无分类。
  • Cardinality: 索引中唯一值数目的估计值。基数根据被存储为整数的统计数据计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。
  • Sub_part: 表示列中被编入索引的字符的数量,若列只是部分被编入索引,则该列的值为被编入索引的字符的数目,若整列被编入索引,则该列的值为NULL。
  • Packed: 指示关键字如何被压缩。若没有被压缩,则值为NULL。
  • Null: 用于显示索引列中是否包含NULL,若包含则为YES,若不包含则为NO。
  • Index_type: 显示索引使用的类型和方法(BTREE、FULLTEXT、HASH、RTREE)。
  • Comment: 显示评注。

方式二

使用show index from 表名SQL查询,比如查询articles表中的索引信息。如下:

方式三

使用desc 表名SQL查询(信息比较简略),比如查询articles表中的索引信息。如下:

删除索引

创建测试表

创建一个用户表用于测试索引的删除,表中包含用户的id、姓名和邮箱,并将这三列分别设置为主键索引、唯一索引和普通索引。如下:

删除主键索引

使用alter table 表名 drop primary keySQL即可删除主键索引。如下:

删除非主键索引

使用alter table 表名 drop index 索引名SQL即可删除指定的非主键索引。如下:

此外,也可以使用drop index 索引名 on 表名SQL也可以删除指定的非主键索引。如下:

说明一下:

  • 一个表只有一个主键索引,所以在删除主键索引的时候不用指明索引名,而一个表中可能有多个非主键索引,所以在删除非主键索引时需要指明索引名。
索引创建原则

索引创建原则

索引创建的原则如下:

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

时刻要记住,创建索引的目的就是为了提高查询的效率。 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IPSG技术和IP组播

1&#xff0c;IPSG技术概述 实验&#xff1a; DHCP snooping IPSG 拓扑&#xff1a; 需求&#xff1a; 1&#xff0c;实现PC1 和PC2 动态获取IP地址 2, 在SW2 配置DHCP snooping 实现DHCP 服务器的安全 3, 在 连接PC 1 和 PC2 的 接口上 做IPSG &#xff0c;防止终端…

贪心算法+练习

正值国庆之际&#xff0c;祝愿祖国繁荣昌盛&#xff0c;祝愿朋友一生平安&#xff01;终身学习&#xff0c;奋斗不息&#xff01; 目录 1.贪心算法简介 2.贪心算法的特点 3.如何学习贪心算法 题目练习&#xff08;持续更新&#xff09; 1.柠檬水找零&#xff08;easy&…

​苹果应用高版本出现:“无法安装此app,因为无法验证其完整性”是怎么回事?竟然是错误的?

最近经常有同学私聊我问苹果应用签名后用落地页下载出现高版本是什么意思&#xff1f;我一脸懵&#xff01;还有这个操作&#xff1f;高版本是个啥玩意&#xff01;所以我就上了一下科技去搜索引擎搜索了下&#xff0c;哈哈哈&#xff0c;然后了解下来发现是这样的首先我们确定…

Kubernetes安装部署 1

本文主要描述kubernetes的安装部署&#xff0c;kubernetes的安装部署主要包括三个关键组件&#xff0c;其中&#xff0c;包括kubeadm、kubelet、kubectl&#xff0c;这三个组件的功能描述如下所示&#xff1a; Kubeadm 用于启动与管理kubernetes集群 Kubelet 运行在所有集群的…

Mac版快速切换工具:One Switch中文 for mac

One Switch是一款功能强大、体验极简的Mac菜单栏工具&#xff0c;适合需要频繁切换系统设置和启动应用程序的用户使用。通过它&#xff0c;用户可以更方便地完成日常操作&#xff0c;提高工作效率。 快速访问工具&#xff1a;One Switch提供了一个便捷的菜单栏图标&#xff0c;…

nodejs+vue晨拾酒馆管理系统elementui

晨拾酒馆管理系统&#xff0c;主要的模块包括管理员&#xff1b;系统首页、个人中心、用户管理、图书分类管理、图书信息管理、图书借阅管理、图书归还管理、图书入库管理、热门图书管理、论坛管理、系统管理&#xff0c;用户&#xff1b;系统首页、个人中心、图书借阅管理、图…

数据科学最佳实践:Kedro 的工程化解决方案 | 开源日报 No.47

leonardomso/33-js-concepts Stars: 58.4k License: MIT 这个项目是一个帮助开发者掌握 JavaScript 概念的资源库。该项目基于 Stephen Curtis 撰写的一篇文章&#xff0c;包含了对 33 个重要 JavaScript 概念全面深入地讲解&#xff0c;并被 GitHub 评为 2018 年最佳开源项目…

【二】spring boot-设计思想

spring boot-设计思想 简介&#xff1a;现在越来越多的人开始分析spring boot源码&#xff0c;拿到项目之后就有点无从下手了&#xff0c;这里介绍一下springboot源码的项目结构 一、项目结构 从上图可以看到&#xff0c;源码分为两个模块&#xff1a; spring-boot-project&a…

linux虚拟机查看防火墙状态

linux虚拟机查看防火墙状态 在Linux虚拟机中&#xff0c;你可以通过以下几种方法查看防火墙状态&#xff1a; 查看iptables防火墙状态 对于使用iptables防火墙的Linux系统&#xff0c;可以使用以下命令查看防火墙状态&#xff1a; sudo iptables -L -v -n查看firewalld防火墙…

c++---模板篇

1、模板 概念&#xff1a;模板就是建立通用的模具&#xff0c;大大提高复用性 特点&#xff1a; 模板不可以直接使用&#xff0c;它只是一个框架模板的通用并不是万能的 1.1、函数模板 C另一种编程思想称为泛型编程&#xff0c;主要利用的技术就是模板C提供两种模板机制&a…

3D孪生场景SDK:Viwer 孪生世界

NSDT 编辑器 提供三维场景构建、场景效果设计、场景服务发布全流程工具等&#xff0c;其场景编辑器支持资产管理、灯光设置、骨骼动画等功能&#xff1b;致力于协助资源不足的中小企业及个人快速开发数字孪生场景&#xff0c;帮助企业提高生产力、实现降本增效。 NSDT编辑器简…

MySQL之主从复制

概述&#xff1a; 将主库的数据 变更同步到从库&#xff0c;从而保证主库和从库数据一致。 它的作用是 数据备份&#xff0c;失败迁移&#xff0c;读写分离&#xff0c;降低单库读写压力 原理&#xff1a; 主服务器上面的任何修改都会保存在二进制日志&#xff08; Bin-log日志…

【Overload游戏引擎分析】画场景网格的Shader

Overload引擎地址&#xff1a; GitHub - adriengivry/Overload: 3D Game engine with editor 一、栅格绘制基本原理 Overload Editor启动之后&#xff0c;场景视图中有栅格线&#xff0c;这个在很多软件中都有。刚开始我猜测它应该是通过绘制线实现的。阅读代码发现&#xff0…

【Pytorch笔记】6.Transforms

pytorch官方文档 - transforms transforms需要使用计算机视觉工具包&#xff1a;torchvision。 torchvision.transforms&#xff1a;常用的图像预处理方法&#xff1b; torchvision.datasets&#xff1a;常用数据集的dataset实现&#xff0c;如MNIST、CIFAR-10、ImageNet等&am…

[每日算法 - 阿里机试] leetcode19. 删除链表的倒数第 N 个结点

入口 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/remove-nth-node-from-end…

java项目log4j2单独为某个类配置日志文件

在项目中&#xff0c;一般都是把日志记录到一个日志文件中。 对应的log4j2.xml内容如下图所示&#xff1a;只有一个RollingFile节点&#xff0c;整个系统只会生成一个log日志文件。 生成的日志文件如下图&#xff1a; 当系统不断扩大&#xff0c;业务越来越复杂&#xff0c;所…

假期题目整合

1. 下载解压题目查看即可 典型的猪圈密码只需要照着输入字符解开即可得到答案 2. 冷门类型的密码题型&#xff0c;需要特意去找相应的解题思路&#xff0c;直接百度搜索天干地支解密即可 3. 一眼能出思路他已经给了篱笆墙的提示提示你是栅栏密码对应解密即可 4. 最简单的社会主…

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的…

(二)Web服务器之Linux多进程

一、基础概念 Linux操作系统一般由以下四个主要部分组成&#xff1a;内核、shell、文件系统和应用程序 。 内核&#xff08;Kernel&#xff09;&#xff1a;是操作系统的核心部分&#xff0c;负责管理系统的硬件资源、进程管理、内存管理、文件系统等。它直接与硬件交互&…

【计算机组成 课程笔记】7.2 DRAM和SRAM

课程链接&#xff1a; 计算机组成_北京大学_中国大学MOOC(慕课) 7 - 2 - 702-DRAM和SRAM&#xff08;13-22--&#xff09;_哔哩哔哩_bilibili 从【计算机组成 课程笔记】7.1 存储层次结构概况_Elaine_Bao的博客-CSDN博客中&#xff0c;我们了解到&#xff1a;SRAM比较快&#x…