MYSQL的索引及底层原理详解

MYSQL中的索引及底层原理详解

索引介绍

1.有关概念:
定义:创建在表上,对数据库表中的一列或者多列的值进行排列得到的结果(相当于表中数据的向导)。
工作方式:一个表创建的索引以文件的形式存储下来,要用该表的数据就先把索引从磁盘上加载到内存(磁盘IO),从内存中先读索引,再根据索引找对应的数据。

作用:提高查询效率(优势)
劣势:

  • 索引也要存储,过多的索引会占用空间
  • 索引并非越多越好,过多的索引会导致cpu使用率降低(过多的索引会导致cpu处理索引的时间过多,处理数据的时间相对少)
  • 由于数据改动会影响索引的改动,过多的索引会引起磁盘IO频繁而造成cpu负载过重

2.索引的分类

  • 普通索引:没有任何限制条件,可以给任意类型的字段添加普通索引
  • 唯一性索引:使用unique修饰的字段,值是不能重复的,主键索引就隶属于唯一性索引
  • 主键索引:使用primary key修饰的字段MYSQL会自动创建为其创建索引,InnoDB存储引擎中不设置主键也会自动找一个字段创建主键索引,一个表只能有一个主键索引
  • 单列索引:在一个字段上创建的索引
  • 多列索引:在表的多个字段上创建的索引
  • 全文索引:使用fulltext参数设置全文索引,只支持char、varchar、text类型的字段上,常用于数据量比较大的 字符串类型中,可以提高查询速度,只有myisam存储引擎支持
  • 空间索引:空间型数据的索引,使用spatial修饰

索引创建和删除的SQL语句:

创建:
1.创建表的时候创建索引

CREATE TABLE table_name(
属性 数据类型,
[unique|fulltext|spatial|primary] [index|key] [索引名] (属性(属性长度) [asc|desc])
);
注意:primary后面必须是key而不能是index
索引名一般以 idx_属性名 这样的形式命名
指定属性长度指建立前缀索引(例如:name属性值’abcde‘,长度3,即以'abc'为索引)
 [asc|desc]:指定数据按索引升序还是降序排列
 例:创建一个student表,表中有id、name、sex三个属性,id为索引,索引名为idx_id
create table student(
id int,
name varchar(20),
sex varchar(10),
index idx_id (id)
);

创建后用show create table 表名; 来看创建表的详细语句:
-----------------------------------------------+
student | CREATE TABLE student (
id int(11) DEFAULT NULL,
name varchar(20) DEFAULT NULL,
sex varchar(10) DEFAULT NULL,
KEY idx_sex (sex)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
±--------±---------------------------------
第四行可以看出id是一个普通索引,索引名为idx_id

2.在已经创建的表上添加索引

2.1 :create [unique|fulltext|spatial|primary] [index|key] [索引名] on 表名 (属性);
例:在student表中创建一个名为idx_name的以name为唯一性索引的索引
create unique index idx_name on student (name);
2.2:alter table 表名 add [unique|fulltext|spatial|primary] [index|key] [索引名] (属性);
例:在student表中创建一个名为idx_id的以id为主键索引的索引
alter table student add primary key idx_id (id);

-----------------------------------------------+
| student | CREATE TABLE student (
id int(11) DEFAULT NULL,
name varchar(20) DEFAULT NULL,
sex varchar(10) NOT NULL,
PRIMARY KEY (id), UNIQUE KEYidx_name(name), KEYidx_sex(sex)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
±--------±---------------------------------

删除

drop index 索引名 on 表名;
注意:一个表的主键索引不能这样删除,这样删除会报错
需要用alter table 表名 drop primary key;来删除
例:删除上面创建的idx_name,idx_id,idx_sex
drop index idx_name on student;
drop index ide_sex on student;
alter table student drop primary key;

-------------------------------------------------------------------+
| student | CREATE TABLE student (
id int(11) NOT NULL,
name varchar(20) DEFAULT NULL,
sex varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
±--------±------------------------------------------

分析有无索引SQL语句的执行过程:

首先student表中设置id为主键索引,并将其创建唯一性索引idx_id,为name创建唯一性索引idx_name
-----------------------------------------------------+
| student | CREATE TABLE student (
id int(11) NOT NULL,
name varchar(20) DEFAULT NULL,
sex varchar(10) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY idx_id (id),
UNIQUE KEY idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
±--------±-----------------------------

再插入一些记录
insert into student values(1,‘zhaolei’,‘nan’),(2,‘qiandian’,‘nan’),(3,‘sunfen’,‘nan’),(4,‘wulan’,‘nv’);
mysql> select * from student;
±—±---------±-----+
| id | name | sex |
±—±---------±-----+
| 1 | zhaolei | nan |
| 2 | qiandian | nan |
| 3 | sunfen | nan |
| 4 | wulan | nv |
±—±---------±-----+

再根据explain +SQL来分析SQL的执行计划:

1.为了看出效果,我们先把idx_name 删除掉:drop index idx_name on student;
再根据explain select * from student where name=‘zhaolei’\G 来查看select * from student where name=‘zhaolei’\G的执行计划

mysql> explain select * from student where name=‘zhaolei’\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: student
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4
filtered: 25.00
Extra: Using where
±--------±---------------------------------
possible_keys和key表示本次查询没有用到索引,rows表示查询了4条数据,意味着进行了全表扫描,当数据量很大时,全表扫描效率很低。

2.给name字段添加索引:create index idx_name on student (name);
再根据explain select * from student where name=‘zhaolei’\G 来查看select * from student where name=‘zhaolei’\G的执行计划
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: student
partitions: NULL
type: ref
possible_keys: idx_name
key: idx_name
key_len: 63
ref: const
rows: 1
filtered: 100.00
Extra: NULL
±--------±---------------------------------
可以看到,此次查询命中了idx_name的索引,而且此次查询’zhaolei’只查询了一条数据,高效很多

索引的底层原理

MySQL支持两种索引,B-树索引哈希索引
MySQL中innoDB存储引擎是基于B-树的存储结构(实际上是B+树)

先来看看这几种树

  • B-树:
    在这里插入图片描述
    B-树是一种多路搜索树,是一种m阶树(一个B-树的节点有多少个子节点,就可以把这个树称为几阶树,二叉树也可以叫做二阶树),图为三阶树。
    B-树每个节点可存储多个数据(二叉树一个节点存储一个数据),存储同样的数据量B-树的高度(层数)是很低的
    B-树无论是叶子节点还是非叶子节点都存数据和数据地址
    B-树对数据查询的耗时情况不均匀,有可能在根节点就找到数据,有可能在子节点或叶子节点才找到数据(每个数据在树中只出现一次,要么在叶子节点,要么在非叶子节点(子节点和根节点))
    B-树一个节点的大小一般和磁盘一个块(block块操作)的大小一致,这样在节点的读取或存储过程中,磁盘IO的次数最少(如果一个节点大小设为两个块,一个节点的存储就需要进行两次block块操作,如果一个节点小于一个块,又会造成一次块操作处理的数据少实际块利用率低的问题)

B+树:
在这里插入图片描述
B+树是B-树的变体,非叶子节点只存关键字,叶子节点存放关键字和数据(数据/数据地址),而B-树无论是叶子节点还是非叶子节点都存数据和数据地址
B+树有一条有序链表串联整个叶子节点

B * 树:
在这里插入图片描述
B*树是B+树的变体,区别在于它把非叶子节点的数据也用链表串起来

关于这几个树不再详细介绍,我是参考B树、B-树、B+树、B*树之间的关系理解的,个人感觉这位老兄总结的不错

MYSQL为什么采用B+树而不用B-树?

  1. B-树的每个节点(叶子节点和非叶子节点)存储的是都关键字和对应的数据地址
    B+树的非叶子节点存储关键字,不存储数据地址。对于相同大小的非叶子节点,B+树存储的关键字多,对于相同数量的数据的关键字,B+树使用更少的节点存,这样树的高度(层数)就更少,使用磁盘IO的次数也少,查询效率高
    B+树的叶子节点存关键字和数据地址
  2. B-树每一个节点都存储了关键字和数据地址,离根节点近的数据查询快,离根节点远的数据查询慢,耗时不均匀
    而B+树所有的数据都在叶子节点上,因此在B+树上搜索关键字,耗时相对是均匀的,没有快慢之分
  3. 区间查找B+树是很快的,因为B+树的叶子节点被连接成一个有序的链表结构,因此做整表查询和区间查询更快(先从根节点向下遍历树,找到一个数据后,直接遍历链表得到其他数据,不用遍历整个树)

哈希索引
底层实现是哈希表,哈希表不能保证数据有序,不适合进行区间查找(因为哈希表的每个数据根据计算的散列码来存储,数据与数据之间无规律可循,无法保证有序,区间查找也相当于查询整个表)

MYISAM和INNODB存储引擎的索引结构

MYISAM:
主键索引:
主键索引
辅助(非主键)索引:
在这里插入图片描述
都是基于B+树的存储结构
索引和数据本身分开(非聚集索引
主键索引和辅助索引的叶子节点都只存关键字和数据地址,区别在于主键索引的关键字是唯一不能重复的

INNODB:
主键索引:
在这里插入图片描述
辅助(非主键)索引:
在这里插入图片描述
都是基于B+树的存储结构
索引和数据不分开(聚簇型索引
主键索引:叶子节点中存关键字和相应的数据
辅助索引:叶子节点中存关键字和主键

对比两大存储引擎索引的查询过程
在这里插入图片描述
左边为INNODB,右边为MYISAM
可以看出:

  1. INNODB引擎中根据主键查询时,直接找到要查找的关键字所在的叶子节点就可以直接从叶子节点种拿到所有数据;根据辅助索引查询时,先找到要查找的关键字所在的节点,节点中只能找到辅助索引的数据和主键的数据,要查找其他数据得根据该节点中的主键去主键索引中查找
    MYISAM引擎中无论是根据主键索引查找还是根据辅助索引查找,都会找到要查找的关键字的叶子节点,然后根据叶子节点中的数据地址去查找所需数据

  2. INNODB引擎中,每个表存储在硬盘包括两个文件:除了表结构文件,还需要一个 .ibd文件来存储数据和索引
    MYISAM引擎中,每个表存储在硬盘包括三个文件:除了表结构文件,还需要一个 .myi文件来存储索引,一个 .myd文件来存储数据

系统(Linux或Windows)中查看数据库中存储的有关表的文件

  1. 命令行方式:
Linux中在/usr/local目录下找MySQL安装的目录
Windows中在某个盘里找到你安装MySQL的地方
然后 cd 上面找到的目录
cd data
cd 库名
ls(linux)  dir(Windows)
可以看到 表名.frm(表结构文件) 表名.ibd(索引+数据文件)--->该表的存储引擎是INNODB
可以看到 表名.frm(表结构文件) 表名.myi(索引文件) 表名.myd(数据文件)--->该表的存储引擎是MYISAM

我的结果:只看了以INNODB引擎存储的表
在这里插入图片描述
2. 鼠标点击到MySQL存储的目录,再点击data,再点击数据库名
在这里插入图片描述
索引的设计原则:

可以看出,使用索引能提高查询效率,但是给表创建过多的索引,效率反而会降低,因此在设计表索引的时候,
需要遵循以下的设计原则:
1、给区分度高的字段创建索引 eg:学号、身份证号
2、给经常需要排序,分组和多表联合操作的字段创建索引
3、经常作为查询条件的字段创建索引
4、索引的数据不宜过多
5、使用数据量少的索引(如前缀索引,主要针对字符串索引,字符串类型的数据尽量创建前缀索引)
6、对于多列索引,优先指定最左边的列集
7、删除不再使用或者很少使用的索引
发布了19 篇原创文章 · 获赞 9 · 访问量 2217

猜你喜欢

转载自blog.csdn.net/weixin_44480874/article/details/99658104