【MySQL】索引的认识

目录

  • 一、索引初识
  • 二、学习硬件(磁盘)
  • 三、MySQL存储数据图解
  • 四、page的结构理解
    • 1.page的数据结构
    • 2.多个page之间的数据结构
    • 3.简单介绍一下B+树
    • 4.索引的理解
    • 5.聚簇索引和非聚簇索引
  • 五、索引的操作
    • 创建索引
    • 查看索引
    • 删除索引
    • 适合设置索引的字段

一、索引初识

构建一张具有大量数据的表

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 文件中 在mysql使用source命令导入,就会构建表

desc查看表结构,可以看到表既没有主键(此处先认为主键会影响索引),也没有任何索引。
【MySQL】索引的认识_第1张图片

查询EMP表中指定工号的员工信息,可以看到每次查询过程都需要花费4.5秒左右。如下:
【MySQL】索引的认识_第2张图片

当我们给员工表中的工号建立索引后,数据库底层就会为员工表中的数据记录构建特定的数据结构,需要注意的是,由于当前员工表中的数据量较大,因此建立索引时也需要花费较长时间。如下:
【MySQL】索引的认识_第3张图片
这时再查询EMP表中指定工号的员工信息,可以看到几乎检测不到查询时耗费的时间。如下:
【MySQL】索引的认识_第4张图片
根本原因就是,给员工工号创建索引后再根据员工工号来查询数据,这时就能够直接通过底层建立的数据结构来快速定位到目标数据,从而提高数据的检索速度,这就是索引的价值。
下面让我们学习索引。

二、学习硬件(磁盘)

通过之前的学习,我们知道,mysql创建数据库其实就是创建一个目录,在innodb存储引擎下,创建一张表其实就是创建两个文件,在linux下,一切皆文件,此时,mysql进行数据持久化存储则为对磁盘文件系统操作。

磁盘及文件系统(点击跳转)

三、MySQL存储数据图解

MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB (后面统一使用 InnoDB 存储引擎讲解)

【MySQL】索引的认识_第5张图片

1、MySQL服务器在内存中运行的时候,在服务器内部,就申请了被称为 Buffer Pool 的的大内存空间,来进行各种缓存。其实就是很大的内存空间,来和磁盘数据进行IO交互。Buffer Pool单次向磁盘刷入1M数据和单次向磁盘刷入100M数据,效率肯定是不同的,MySQL底层也有自己的‘缓冲区’刷新策略来保证IO效率。
2、MySQL中的数据文件,是以page为单位保存在磁盘当中的。
3、MySQL的CURD操作,都需要通过计算,找到对应的插入位置,或者找到对应要修改或者查询的数据。
4、而只要涉及计算,就需要CPU参与,为了便于CPU参与,一定要先将数据移动到内存当中。所以在特定时间内,数据一定是磁盘中有,内存中也有。后续操作完内存数据之后,以特定的刷新策略,刷新到磁盘。而这时,就涉及到磁盘和内存的数据交互,也就是IO了。而此时IO的基本单位就是Page。
5、为了更高的效率,一定要尽可能的减少系统和磁盘IO的次数。

四、page的结构理解

磁盘这个硬件设备的基本单位是 512 字节(有些会更大),而 MySQL InnoDB引擎 和磁盘进行数据交互的基本单位是 16KB,这一个个的基本数据单位,在 MySQL 中叫做page(注意和系统的page区分)

MySQL中必然存在大量的page,为了先描述再组织,单个page内部除了用户数据之外,还存在一部分的数据结构,用于MySQL组织管理大量的page。

使用page进行IO交互有什么好处呢?
预加载,根据局部性原理,减少IO次数,提升效率。
IO效率低下的最主要矛盾不是IO单次数据量的大小,而是IO的次数。

1.page的数据结构

这目录就像看书、查字典一样,我们可以通过目录快速定位目标数据的大致页数,提升查找效率。所以MySQL的page里的这些目录虽然说多占用了一丢丢的空间,但大大提高了我们查找数据的速度(空间换时间)
【MySQL】索引的认识_第6张图片
这也解释本段开头我们无序插入数据时,MySQL自动帮我们进行了排序。这是因为只有数据是有序的,MySQL才方便引入页目录,提升后续的查找效率。

2.多个page之间的数据结构

【MySQL】索引的认识_第7张图片
刚刚我们在每个page内部搞了页目录,减少了page内部的检索次数,提升了单page的搜索效率。

上图画出了多个page之间的连接关系,从图中看出页目录在多个page中也是呈现顺序关系的,如果是跨页搜索数据,也只能从前往后顺序遍历每个页的页目录,如果page一多,这种检索方式会大大降低页与页之间数据搜索速度,为了解决该问题,我们同样使用目录的方式对每个页中的目录进行管理:
【MySQL】索引的认识_第8张图片
上图同样使用了page的方式来管理页目录,每个page中不包含有效数据,只包含对应page中的始末页目录,所以一个“一级目录”可以管理上千个page。

那么问题又来了,如果底层的page很多,势必会造成一级目录的数量变多,那么我们对一级目录的遍历不是又变成了线性遍历了吗?再套一层:
【MySQL】索引的认识_第9张图片
一般两三层就够了,那些只用于索引的page,每个可都是16KB,单个page可以索引上千个下一层的page!完全够用,这样套了三层,即便面对海量数据,MySQL的查询效率也不低。(如果不够那就再套一层,存储数量指数级增长)后续查找时,自上而下进行查找,查到哪里就加载哪一部分的page,并不会加载整颗B+树到内存。

我们可以看出存储的数据结构是一颗b+树,要注意的是
1、并不是所有的存储引擎的索引都是采用B+树,还有哈希索引等方式。只能说主流的存储引擎是采用B+树作为索引的数据结构。
2、只有叶子结点采用链表进行级联,这是因为这是B+树的特性;同时,叶子结点进行级联可以满足范围查找(有时候数据读取的时候跨页了,叶子结点有指向next页的指针,就很方便)

3.简单介绍一下B+树

【MySQL】索引的认识_第10张图片

1、非叶节点不保存数据,只用来索引,所有数据都保存在叶子节点。

2、数据只在叶子结点保存,并保存指向前后叶子结点的指针,通过链表指针对叶子结点进行级联,且叶子结点本身依关键字的自小而大顺序连接。

为什么要选择B+树

①只有叶子节点用来存储数据,其余节点用来存储目录,这样可以存储更多的目录。这样的一棵树,一定是比较“矮”的树,
这样在查询数据时,可以更快的找到数据。
②在查询数据时,一般局部性原理会吧要查询数据周围一定范围的数据也一并加载,使用链表方式,方便范围查找。

4.索引的理解

我们指明看主键列,MySQL会根据主键进行排序。如果我们在创建表的时候没有指明主键,MySQL会生成一个隐藏的列作为主键。这也说明了我们指明主键,MySQL会按照主键进行排序,我们不指明主键,MySQL的排序以默认生成的主键为准,所以这时我们的数据插入时的顺序是怎样的,拿出来的顺序就是怎样。

建立一张测试表

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

);

插入数据

insert into user values(3,18,'孙权');
insert into user values(4,18,'刘备');
insert into user values(2,18,'关羽');
insert into user values(5,10,'张飞');
insert into user values(1,100,'周瑜');

【MySQL】索引的认识_第11张图片

5.聚簇索引和非聚簇索引

MyISAM 引擎同样使用B+树作为索引结果,和上面讲的innodb存储引擎不一样的是,MyISAM叶节点的data域存放的是数据记录的地址。下图为 MyISAM表的主索引, Col1 为主键。
【MySQL】索引的认识_第12张图片

聚簇索引:像innodb存储引擎那样把B+树和数据存放在一起称为聚簇索引。

非聚簇索引:像MyISAM存储引擎那样把B+树和数据分离的方式称为非聚簇索引。

MyISAM的辅助(普通)索引

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

对于 MyISAM ,建立辅助(普通)索引和主键索引没有差别,无非就是主键不能重复,而非主键可重复。

MyISAM存储引擎可以在一张表中建立多个索引,下图就是基于 MyISAM 的 Col2 建立的索引,和主键索引没有差别:

【MySQL】索引的认识_第13张图片
innodb的辅助(普通)索引
InnoDB 除了主键索引,用户也会建立辅助(普通)索引,我们以上表中的 Col3 建立对应的辅助索引:
【MySQL】索引的认识_第14张图片
InnoDB的非主键索引中叶子节点并没有数据,而只有对应记录的key值。所以通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询。

为何InnoDB针对这种辅助(普通)索引的场景,不给叶子节点也附上数据呢?数据主键索引有,没必要保存两份,不然太浪费空间了。

五、索引的操作

创建索引

我们在前面学习中可知,在创建表时为表添加主键,其实也是在创建索引。

创建索引

①创建表时主动添加 primary key
【MySQL】索引的认识_第15张图片

②创建表的最后,指定某列或某几列为主键索引(和创建表时添加同理)。
【MySQL】索引的认识_第16张图片

③创建表后,使用alter命令给指定字段添加主键索引
【MySQL】索引的认识_第17张图片
创建索引可以分为很多,例如主键索引,唯一键索引,普通索引等。

主键索引的特点:

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

唯一键索引的特点:

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

普通索引的特点:

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

查看索引

①使用show keys(index) from 表名SQL查询,比如查询articles表中的索引信息。如下:
【MySQL】索引的认识_第18张图片

  • 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: 显示评注。

②使用desc 表名SQL查询(信息比较简略),比如查询articles表中的索引信息。如下:
【MySQL】索引的认识_第19张图片

删除索引

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

删除主键索引

使用alter table 表名 drop primary keySQL即可删除主键索引。如下:
【MySQL】索引的认识_第21张图片

删除非主键索引

使用alter table 表名 drop index 索引名SQL即可删除指定的非主键索引。如下:
【MySQL】索引的认识_第22张图片
此外,也可以使用drop index 索引名 on 表名SQL也可以删除指定的非主键索引。如下
【MySQL】索引的认识_第23张图片

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

适合设置索引的字段

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

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

你可能感兴趣的:(MySQL,mysql,adb)