【Mysql】索引

请添加图片描述

【Mysql】索引

索引的概念

索引概念

  • 普通的查询通常都是通过线性遍历查询匹配的数据,查询的效率极具随机性,时间复杂度通常是O(N)的;而使用索引之后,查询的速度就可能提高成百上千倍
  • 面对检索海量数据的场景时,线性遍历的方式是很致命的,而正也是索引真正的价值所在;将热点数据使用某种组织形式(后面会解释)的形式进行组织,形成对应的数据结构查询算法就叫做索引
  • 但索引查询速度提高是以,插入,更新,删除的速度作为代价的;解释:因为查询的速度快肯定是因为数据结构自身的特点,而有得就有失去,维护对应数据结构的成本也就上升了,即插入和更新,删除操作时还需要维持数据结构的特性,需要做出额外的调整操作(如维护红黑树)

常见索引分为:

  • 主键索引(primary key)

  • 唯一索引(unique)

  • 普通索引(index)

  • 全文索引(fulltext)–解决中子文索引问题。

  • 索引的价值

小示例:先整一个海量数据表,在查询的时候,比较有无索引的差距

  • 数据准备:

生成海量表的代码:

drop database if exists `bit_index`;
create database if not exists `bit_index` default character set utf8;
use `bit_index`;

-- 构建一个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的文件,而后将下面代码拷贝进去,在msyql中 source一下即可

我是将其放在了 /home/lsh/c++/mysql/index_data.sql 下

执行:

注:插入数据时间一般需要挺久的,可以先等他一会

执行途中可能会出现这样的报错,可以参考这篇文章 上述代码执行报错问题

如果是配置了my.cnf的,配置好之后使用systemctl restart mysqld 重启一下mysql服务器即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FRPCnjVo-1686323426541)(null)]

  • 开始实验

查询员工编号为997878的员工

select * from emp where empno = 997878;

可以看到耗时还是很大的

我们创建索引后,再进行查询一次,对比一下查找时间

alter table emp add index(empno);
select * from emp where empno = 997878;

我们发现创建索引的时间也是需要很久的(创建对应的数据结构是需要一定时间的),而查询时间我们会发现很快

小总结:

  • 创建索引之后,我们会发现查询的速度有了质的飞跃;

认识磁盘

  • MySQL 给用户提供存储服务,而存储的都是数据,数据在磁盘这个外设当中。
  • 磁盘是计算机中的一个机械设备,相比于计算机其他电子元件,磁盘效率是比较的,在加上IO本身的特征,可以知道,如何提交效率,是 MySQL 的一个重要话题。
磁盘结构

磁盘整体结构

部分说明:

  • 永磁铁: 机械硬盘的存储方式与磁带比较类似,磁体具有记忆的功能,永磁铁是为了保证磁性的稳定。

  • 音圈马达: 硬盘读取数据的关键部位,主要作用是将存储在磁盘上的信息转换为电信号向外传输。

  • 主轴: 保证电机稳定的转动,磁盘转动才能读出数据。

  • 空气滤波片: 过滤空气硬盘透气孔中进入的空气,保证硬盘内部清洁,同时还可以防止硬盘内部的零件氧化,确保硬盘安全使用。

  • 磁盘: 硬盘一般都是铝合金制作的,主要是用来存储文件的。

  • 磁头: 用来读取盘片上的信息。

  • 串行接口: 用来连接电脑与硬盘的接口,起到传输的作用。

    再来看看磁盘中一个盘片

扇区

数据库文件,本质其实就是保存在磁盘的盘片当中。也就是上面的一个个小格子中,就是我们经常所说的扇区。当然,数据库文件很大,也很多,一定需要占据多个扇区。

说明:

  • 从上图可以看出来,在半径方向上,距离圆心越近,扇区越小,距离圆心越远,扇区越大
  • 所有扇区都是默认512字节,因为保证一个扇区多大,是由比特位密度决定的。也就可以说明原来的磁盘内圆的扇区比特位密度是较密的,而外圆的比特位密度是较疏的;不过最新的磁盘技术,已经慢慢的让扇区大小不同了
  • 而找到一个文件的本质就是:在磁盘中找到所有保存文件的扇区。而我们能够定位任何一个扇区,那么便能找到所有扇区,因为查找方式是一样的。

下面就来谈谈如何定位扇区

定位扇区

磁盘具体图如下:

相关概念

  • 柱面(磁道): 多盘磁盘,每盘都是双面,大小完全相等。那么同半径的磁道,整体上便构成了一个柱面
  • 每个盘面都有一个磁头,那么磁头和盘面的对应关系便是1对1的
  • 所以,我们只需要知道,磁头(Heads)、柱面(Cylinder)(等价于定位了某个盘面的具体磁道)、**扇区(Sector)**对应的编号。即可在磁盘上定位所要访问的扇区。这种磁盘数据定位方式叫做 CHS
  • 不过实际系统软件访问文件时,使用的并不是 CHS (但是硬件是),而是 LBA(将磁盘上的磁道想成一个线性结构,像数组一样,LBA就好像数组中的下标一样,使用下标就可以定位数组中的每一个元素) ,一种线性地址,可以想象成虚拟地址与物理地址。系统将 LBA 地址最后会转化成为 CHS ,交给磁盘去进行数据读取

os与磁盘io交互的基本单位

  • os与磁盘的io交互:就是操作系统对磁盘进行读取和存储数据操作
  • 我们上面说磁盘的一个扇区的大小实际上512kb,但实际上os与磁盘的IO的交互基本单位是4KB,因为512字节 的单次IO大小还是太小了,IO的单位太小,意味着读取同样的数据内容,需要进行多次磁盘访问,会带来效率的降低。(4kb的数据在IO单位为512 字节下,需要进行8次IO操作)
  • Linux的文件系统,就是在磁盘的基本结构下建立的,文件系统读取基本单位,就不是扇区,而是数据块。故,系统读取磁盘,是以块为单位的,基本单位是 4KB 。详情可看这篇文章 Linux下io流对应的文件管理系统部分

补充:理解IO请求本质是什么

  • 我们都知道系统中一定会存在很多的IO的请求,所以os肯定是要对这些IO请求进行管理的,怎么管理呢? 先描述再组织 – os使用对应的结构体描述IO请求,而后使用数据结构对这些结构体进行管理

  • 所以说所谓的IO请求中os眼里实际上就是一个又一个结构体或者说是IO请求描述块

  • 所以说我们向磁盘发送io请求的本质就是,os描述磁盘的结构体里面存储一个组织IO请求描述块的数据结构,对IO请求进行管理


磁盘随机访问与连续访问

由上面我们知道,我们要找到一个扇区所在之处,先得先知道其在那个盘面(通过磁头定位),而后再定位其在哪个磁道,而后再通过扇区编号就可以定位某个扇区;其中定位扇区所在磁道的过程也叫“寻道”

  • 随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。

  • 连续访问:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问。

  • 因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问。

  • 磁盘是通过机械运动进行寻址的,连续访问不需要过多的定位,故效率比较高

    而其中俩者访问的本质区别就是,随机访问下次进行IO依旧需要做“寻道”,而连续访问再次进行IO操作时则不需要“寻道”


mysql与磁盘交互的基本单位

理解mysql软件在什么位置

我们都知道mysql就是一款用来管理文件的应用软件,在操作系统角度来说其实就是对应的代码和数据,而我们知道程序是需要在CPU中执行的,而和CPU直接交互的就是我们的内存(冯诺伊曼体系结构),所以说我们使用mysql时,mysql对应的代码和数据一定是被加载到内存中的,所以说在操作系统视角,mysql就是处于内存中的代码和数据;

而我们知道mysql是专门用来管理文件的应用软件,也可以理解为一种特殊的文件管理系统,而我们的文件实际是存储于磁盘中的,所以我们的mysql是肯定会和磁盘进行IO交互的

mysql和磁盘交互的基本单位

mysql和磁盘之间存在IO交互,就一定存在基本的IO大小,而mysql是具有更高的IO的场景,需要更高的IO效率,所以mysql和磁盘之间的IO交互是更大的,一般是16KB; 这个基础数据单元,在mysql中也叫做page (数据页) ,而我们知道磁盘的基本单位是512字节,这个IO交互是怎么实现的呢?

实际上,数据库文件也是文件,打开数据库在操作系统下就是一个打开的文件,就会有对应的文件描述符,内核级别缓冲区;而我们知道用户层的数据实际上都是先被刷新到对应的文件内核级别缓冲里去,而后再刷新到磁盘中;

所以mysql和磁盘进行IO的框架是这样的mysql - 内核缓冲区- 磁盘 ;而内核缓冲区是属于内核的,而内核和磁盘进行IO交互的单位为4KB,所以说mysql和内核缓冲区进行IO交互是以16KB进行交互的,而内核代替mysql与磁盘进行交互时,是将mysqlIO的16KB数据分成4次IO与磁盘进行交互的;即mysql实际上是以为自己是直接与磁盘以16KB大小交互的,这是因为内核级别缓冲区起到了隔离作用

简单示意图

说明:

  • MySQL 中的数据文件,是以page为单位保存在磁盘当中的。

  • MySQL 的 CURD 操作,都需要通过计算,找到对应的插入位置,或者找到对应要修改或者查询的数据。

  • 而只要涉及计算,就需要CPU参与,而为了便于CPU参与,一定要能够先将数据移动到内存当中。

  • 所以在特定时间内,数据一定是磁盘中有,内存中也有。后续操作完内存数据之后,以特定的刷新策略,刷新到磁盘。而这时,就涉及到磁盘和内存的数据交互,也就是IO了。而此时IO的基本单位就是Page。

  • 为了更好的进行上面的操作, MySQL 服务器在内存中运行的时候,在服务器内部,就申请了被称为 BufferPool 的的大内存空间,来进行各种缓存。其实就是很大的内存空间,来和磁盘数据进行IO交互。

  • 更高的效率,一定要尽可能的减少系统和磁盘IO的次数 – 因为磁盘本身运行速度是很慢的


索引理解

上面说过,创建索引的本质,实际上就是将热点数据以某种组织形式组织起来,形成对应的数据结构和算法;而所谓的组织形式就是我们上面所说的Page,就是说组织热点数据就是对page进行组织

注:下面讲解都是默认以mysql的Innodb搜索引擎进行讲解

观察现象

先建立一张测试表,添加主键实际上就是在建立索引

create table if not exists user (
    id int primary key, --一定要添加主键哦,只有这样才会默认生成主键索引
    age int not null,
    name varchar(16) not null
);

然后再按不同顺序插入数据,我们可以发现我们插入数据时,主键值是无序的

我们此时查看一下表中数据分布

我们会发现里面的数据竟然是按照主键值排序好的,为什么数据是按照主键值的排序好的呢?这实际上有page的内部结构是相关的;


理解单个page

MySQL 中要管理很多数据表文件,而要管理好这些文件,就需要先描述,在组织 ,我们目前可以简单理解成一个个独立文件是有一个或者多个Page构成的。

理解为什么IO交互的基本大小是page

  • 如果mysql与磁盘进行IO交互时,采取的方案是用多少,加载多少,就上面的5条记录进行举例:

    如果我们一开始要查找id = 1的记录,后面依次查找id = 2,3,4,5的记录,那我们的mysql就需要与磁盘进行5次IO – 效率是极低的

  • 而如果我们使用的是page的方案,我们第一次查询id = 1的记录时,就将id =1 所在的page都加载到了内存中,而后的4次查询,直接在page查询即可,即只需要进行一次IO即可;所以说使用单个page页的是可以大大减少IO的次数

  • 你怎么保证,用户一定下次找的数据,就在这个Page里面?我们不能严格保证,但是有很大概率,因为有局部性原理。往往IO效率低下的最主要矛盾不是IO单次数据量的大小,而是IO的次数。

而将一个一个page组织起来的方式就是双链表,使用链表将一个个page连接到一起,而page本质也是用来存储数据的,而page内部组织数据的方式实际上是使用单链表(为什么不使用数组呢?因为数据一般都是结构体类型的);如下图所示

单个page示意图

而为什么会通过主键值对数据进行排序呢,实际上我们创建索引后,因为数据是按照主键值拍好序的有序链表,而我们查询数据只需拿着主键值从头向尾查找即可,没有一个查找是浪费的;但这个查找效率依旧不是很乐观


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

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


页目录

而我们联想一下,我们平时看书时,寻找一本书里面的某一个句话,实际上是这样的流程,通过大目录定位具体章,而后再通过小目录定位具体节,而后开始翻书查找;而索引中就是使用这种方式优化对page的组织

page内部的目录

即在page内部按照键值划分出不同组,以该组的第一个键值作为目录,目录项存储的是俩个值,一个是键值和该键值对应的记录的地址;

例如:我们现在要查找键值为4的记录

我们先去目录项中进行遍历,先遍历1,而4>1,所以4肯定在下一个目录项指向处,而最后遍历到某个大于4的目录项s1,即键值为4的记录就一定处于[3,s1]之间,所以就直接通过目录项3所指向的地址,开始遍历查找键值为4的记录;而原来的方式是需要进行4次遍历,明显目录项遍历的效率会高不少;并且我们这里举的例子都是小数据范围,而处于大数据范围时,这种遍历方式会更加的高效

示意图

但page页内目录项这种方案依旧是有以下缺点的

  • 插入数据时,只需要维护page之间的有序性的,即数据插入的位置不一定是在第一个page内的,最坏情况是插入到最后一个page内;即插入数据时,也是需要遍历page里面的目录项,寻找合适的插入位置进行插入,其中还需要线性遍历不同page;这种插入方式效率是非常低

  • 其次就是我们上面这种方案只是在page页内的效率会很高,而如果我们需要查询的数据是分布在链表中间的page,这就意味着我们需要遍历完前面的所有page里面的目录项最后才能找到对应的记录;而mysql中的page是需要将对应的page加载到内存中,而后对该page进行目录项的遍历;

    而将page加载到内存中,这意味着可能某次查询操作就会包含大量的IO的操作

而解决方案就是继续套娃

page和page之间的目录项

  • 使用一个目录项来指向某一页,而这个目录项存放的就是将要指向的页中存放的最小数据的键值。

  • 和页内目录不同的地方在于,这种目录管理的级别是,而页内目录管理的级别是

  • 其中,每个目录项的构成是:键值+指针

简单示意图

而一个目录页就能管理大概管理2KB个page(将目录项按照8字节大小计算),这种管理方案效率是非常高的

  • 插入数据时,只需现在目录页中遍历找到大致的page,而后在再对应page之中遍历目录项即可;对比前面的方案,只需要遍历一个page里面的目录项即可效率是高出很多的
  • 遍历page时附带的大量IO操作也可以很好解决,遍历page的操作变成了遍历目录page中的目录项,找到大致范围的page时,只需将该page加载到内存进行遍历里面的目录项即可;即如果我们要查询的是page双链表中间的page,也可通过这种方式,快速筛出数据所在的page是那个,只需要将这个page加载到内存即可

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

可是,我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?不用担心,可以在加目录页

示意图

而上面的数据结构实际上就是我们的B+树,这也就是索引的底层原理;

注:

上面的目录页使用链表连接只是为了方便,实际情况是没有的,只有底层的Page页才使用了双链表进行组织

小总结:

  • page分为目录页和数据页,目录页只放各个下级page的最小键值。
  • 我们进行查询操作时,只需提前将顶层的目录页加载到内存即可,后面需要那个二级目录页和具体的page,再将这俩个page加载到内存即可,大大的减少了IO次数;
  • 但缺点是,我们维护该数据结构的成本就更大了,例如插入,修改,删除操作;可能会存在插入某个数据时,需要当前page的某个数据为其“腾位置” ,这可能会牵制到几个目录页的目录项的调整

其他数据结构建立索引结构的可行性

链表

使用链表的最优情况依旧是线性遍历,所以不可行

二叉搜索树

二叉搜索存在当插入的数据是有序时,会退化成链表,也即线性结构,所以不可行

AVL ,红黑树

虽然说AVL树和红黑树是近似平衡,查询数据的效率也是log(N) 级别的,但维护的层数相比于B+树来说会多很多,而层数越多,就意味着系统和磁盘会有更多的IO交互;虽然AVL和红黑树是可行的,但并不是最优解

HASH

官方的索引实现方式中, MySQL 是支持HASH的,不过 InnoDB 和 MyISAM 并不支持.Hash跟进其算法特征,决定了虽然有时候也很快(O(1)),不过,在面对范围查找就明显不行(因为哈希的数据是散乱排布的,对于范围查找也只能细分为一个数据一个数据的查找,而B+树底层数据是使用链表连接起来的,进行范围查找时,只需查找左端点即可,后面直接遍历链表即可),另外还有其他差别,有兴趣可以查一下。

B 树 vs B + 树

感兴趣的小伙伴可以进入这个网站模拟一下B+树和B树[数据结构模拟网站](Data Structure Visualization (usfca.edu))

使用B树和B+树构建索引结构的示意图

  • B树

  • B+树

区别之处

  • B树中的目录页节点,既有数据又page指针,而B+树,只有叶子节点存储数据,其他目录页中的非叶子节点,只有键值和Page指针
  • B+树的叶子节点(存储数据)都是使用链表连接起来的,而B树并没有

B树不行的原因

  • B树中目录页不仅存储数据而且还存储了page指针,这意味着B树中一个目录页能管理的Page相比于B+树来是要少的,这意味着B树的层数可能更高,这也就意味着更多的IO交互
  • 而且B数面对范围查找也是较为乏力的,和HASH类似;

聚簇索引 vs 非聚簇索引

MyISAM 存储引擎-主键索引

MyISAM 引擎同样使用B+树作为索引结构,但叶节点的data域存放的是数据记录的地址。

下图为 MyISAM 表的主索引,Col1 为主键。

其中MyISAM最大的特点就是,将索引page和数据page分离,也就是叶子节点没有数据,只有对应数据的地址,相较于InnoDB索引,InnoDB是将索引和数据放在一起的,所以MyISAM数据page的设计是比InnoDB更优的;

  • 创建一个存储引擎为InnoDB的表时,实际上创建俩个文件
    • .frm(表结构数据)
    • .idb(主键索引和用户数据)
  • 创建一个存储引擎为MyISAM的表时,实际上创建3个文件
    • .frm(表结构数据)
    • .myd (表数据)
    • .myi(主键索引数据)

而其中MyISAM这种用户数据和索引数据分离的方案就是非聚簇索引

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

当然, MySQL 除了默认会建立主键索引外,我们用户也有可能建立按照其他列信息建立的索引,一般这种索引可以叫做辅助(普通)索引

而聚簇索引和非聚簇索引最大区别就在于普通索引的存储数据方案上

  • 非聚簇索引: 对于MyISAM来说,创建普通索引和主键索引并没有什么区别,因为MyISAM中索引保存的是数据的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rCjKlFhw-1686323426525)(null)]

  • 聚簇索引:对于InnoDB来说,普通索引结构中的存储的数据实际上是主索引结构中的主键值

所以通过辅助索引,找到目标记录,需要俩次遍历:首先检测辅助索引获得主键值,然后用主键到主索引中检索获得记录,这样的过程,就叫做回表查询

而至于为什么InnoDB针对这种普通索引的场景,不采取给叶子节点也附上数据,就是因为这样太浪费空间了


索引操作

其实创建主键就是在创建我们的主键索引;创建唯一键的本质就是创建特殊的普通索引;俩者都具有唯一性,符合索引的结构特征

创建主键索引
  • 方式一:

    -- 在创建表的时候,直接在字段名后指定 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);
    

主键索引的特点:

  • 一个表中,最多只有一个主键索引
  • 主键索引的效率高
  • 创建主键索引的列,它的值不为空,且不能重复
  • 主键索引的列基本都是int类型
创建唯一索引
  • 方式一:

    -- 在表定义时,在某列后直接指定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属性,等价于主键索引
创建普通索引
  • 方式一:

    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); --创建完表以后指定某列为普通索引
    
  • 方式三:

    create table user10(id int primary key, name varchar(20), email varchar(30));
    -- 创建一个索引名为 idx_name 的索引
    create index idx_name on user10(name);
    

普通索引的特点

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

注: 创建唯一键索引和普通索引都是需要搭配主键索引的(InnoDB特性)


全文索引

当对文章字段或有大量文字的字段进行检索时,会使用到全文索引。MySQL提供全文索引机制,但是有要求,要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用sphinx的中文版(coreseek)

例:

先创建出一个张带有全文索引的表

CREATE TABLE articles (
    id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    title VARCHAR(200),
    body TEXT,
    FULLTEXT (title,body) -- 全文索引
)engine=MyISAM;

插入一批数据

INSERT INTO articles (title,body) VALUES
    ('MySQL Tutorial','DBMS stands for DataBase ...'),
    ('How To Use MySQL Well','After you went through a ...'),
    ('Optimizing MySQL','In this tutorial we will show ...'),
    ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    ('MySQL vs. YourSQL','In the following database comparison ...'),
    ('MySQL Security','When configured properly, MySQL ...');

查询database数据

select * from articles where body like '%database%' ;

虽然可以查询到对应的数据,但这种方式并没有使用全文索引,而是线性遍历所有数据,寻找匹配的

使用explain分析sql语句

语法:explain sql语句

正确使用全文索引的方式

SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database');

再使用explain分析该语句,我们发现这条查询使用到的索引是title

explain详解可参考这篇博客 explain详解

查询索引
  • 方法一:

    show keys from 表名;
    
  • 方法二:

    show index from 表名;
    
  • 方法三:

    desc 表名; -- 信息比较简略
    
删除索引操作
  • 方法一:-删除主键索引

    alter table 表名 drop primary key;
    
  • 方法二:其他索引的删除

    alter table 表名 drop index 索引名; --索引名就是show keys from 表名中的Key_name 字段
    
  • 方法三:

    drop index 索引名 on 表名;
    

注: 我们会发现创建索引时都是需要括号的,而我们删除索引是不需要带括号的;

索引创建原则
  • 比较频繁作为查询条件的字段应该创建索引

  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件

  • 更新非常频繁的字段不适合作创建索引

  • 不会出现在where子句中的字段不该创建索引

其他
  • 复合索引 --就是我们之前所说的复合主键

  • 索引最左匹配原则

  • 索引覆盖

具体可观看这篇博客 复合索引探究

s from 表名;


* 方法二:

```sql
show index from 表名;
  • 方法三:

    desc 表名; -- 信息比较简略
    
删除索引操作
  • 方法一:-删除主键索引

    alter table 表名 drop primary key;
    
  • 方法二:其他索引的删除

    alter table 表名 drop index 索引名; --索引名就是show keys from 表名中的Key_name 字段
    
  • 方法三:

    drop index 索引名 on 表名;
    

注: 我们会发现创建索引时都是需要括号的,而我们删除索引是不需要带括号的;

索引创建原则
  • 比较频繁作为查询条件的字段应该创建索引

  • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件

  • 更新非常频繁的字段不适合作创建索引

  • 不会出现在where子句中的字段不该创建索引

其他
  • 复合索引 --就是我们之前所说的复合主键

  • 索引最左匹配原则

  • 索引覆盖

具体可观看这篇博客 复合索引探究

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值