Mysql——索引

目录

为什么要用索引

索引是什么

Mysql中的page(InnoDB引擎)

索引如何提高查找效率

索引如何生效

排序支持索引

聚簇索引和非聚簇索引

创建多个索引

复合索引

全文索引的简单介绍

索引覆盖

索引的缺点

索引创建


为什么要用索引

一般来说,我们使用一个普通字段的值去查找一列或者几列数据是要对表中的所有数据进行线性遍历的,在数据很多的情况下这样效率非常低,严重影响用户体验,索引用以提升查找速度。


索引是什么

而要提升查询速度无非从两个方面入手:数据结构/算法(算法其实一定程度上受限于数据结构),而索引主要是从数据结构入手来提高查询效率的,即创建索引就是创建一个数据结构帮助提高查找效率。


Mysql中的page(InnoDB引擎)

Mysql作为一个组织数据服务查询的应用,IO需求要比一般程序更大,因此OS以4KB为单位和磁盘进行交互对于Mysql来说还是有点太少,为了进一步减少IO次数,Mysql与磁盘进行交互的基本单位是16KB

Mysql服务实际上会在启动的时候申请一个默认为128M大小的缓冲区**------buffer pool,用以存放当前读入内存的数据。Mysql会****以16KB为单位把磁盘数据读取到buffer pool,也以16KB为单位把buffer pool中的数据写到内核缓冲区,并在适当的时候刷新内核缓冲区。通过这种方式可以让Mysql实现以16KB为单位与磁盘进行交互。**

**在buffer pool中,哪些16KB块需要赶快刷新,哪些16KB块被修改过,哪些16KB块是已经存在,这些问题告诉我们,不单单要把数据加载到buffer pool,还要对其进行管理,而管理的最好方式就是先描述,在组织。**因此每个16KB的块都会对应一个管理它的结构体

我们在数据库中也是以16KB的page为单位存放数据的


索引如何提高查找效率

我们已经知道,Mysql 数据库(InnoDB引擎)中使用page与磁盘进行交互,所以实际上可以推断出,在其存储文件中,数据也是按照Page存储的。不过这不是讨论的重点,只有数据在内存中时才可以对其进行查询操作,因此我们只需要关注buffer pool中的Page即可,下面展示整个Mysql 数据库(InnoDB引擎)的Page结构:

我们可以从上图中推断出如下几点:

  • page分为两类**,一类存储数据和目录(叶子结点),一类只存储目录(非叶子结点),但是所有种类的page都有前后指针负责把同一层的节点相互连接。**
  • 所有的page逻辑上组成一种树形结构**,这种结构叫做B+树。**
  • 在单个叶子结点内部,由于有目录的存在,可以一次性排除更多元素**,快速查找到目标元素。**
  • 在多个叶子节点之间,由于有第二层非叶子节点的存在,我们可以较快速的查找到目标元素在哪个叶子结点里面**,找到后即可在叶子结点内部进行查询。**
  • 如果数据量过多的话,第二层非叶子结点也会过多,那么其本身的查找也变成了效率比较低的线性遍历,因此可以给第二层非叶子节点再次创建目录,即第一层非叶子节点,帮助快速查询。
  • 图中第二层非叶子结点中的每一个目录项都代表多个叶子结点的集合,这样目录才能发挥作用,但是上图中没有显示出这一点
  • 事实上,本质就是使用目录的思想一次性排除更多的元素,在这个过程中选择了B+树这种数据结构**。**

选择B+树而不是B树作为数据结构有什么好处?

  • 首先,B+树的非叶子节点不存放数据,这样一来非叶子节点可以存放更多目录项,那么整棵树就是"矮胖"类型,显而易见,一颗查找树****又矮又胖的话,查找次数会更少
  • 其次,B+树的叶子节点相连,那么就可以很好的支持范围查询。比如说我们的查询语句是这样的:select * from where id>100,那么如果结果集存放在多个叶子结点中,就可以通过前后指针去到其他page找结果,而不用再次从上至下查询一次
  • 使用树形结构,可以只加载目标元素相关的节点进内存,减少了IO次数(当然这一点B树也做得到)

为什么不用其他数据结构?

  • 链表是线性遍历,查找速度很慢
  • 二叉搜索树可能退化成线性结构导致查找速度变慢
  • AVL树和红黑树虽然可以维持搜索树的平衡,但是这两种树毕竟只是二叉树,而不是像B+一样的多叉树,所以会比较高,意味着的IO次数会比较多,而IO耗时是最多的。
  • hash表存放单个数据,这样肯定不行,因为不支持范围查询,而且不支持排序。可以想到用hash存放page(key为page中最后一个数据的值),这样来说可以支持范围查询,排序,但是增删的时候需要把一个page分裂成两个,重新映射page的位置,比较麻烦,而且这样一来page一但过多线性遍历就会比较慢。

索引如何生效

显而易见,只有使用创建索引时候使用的属性查找数据才会加快查找速度。因此可以给经查用于查找的属性列都建立索引


排序支持索引

我们可以发现,当我们在创建表的时候指定一个主键的时候,之后无论如何插入数据,查询到的数据都是按照主键进行排序的,这涉及到两点:

  • Mysql默认会使用主键建立一个索引**,如果你没有设置主键(那么Mysql会使用隐藏的列充当主键建立索引),总之建表之后一定会有一个索引。**
  • page内部以及page之间的数据项有序是使用目录加快查询速度的必要条件**,因此数据必须根据索引进行排序。**

聚簇索引和非聚簇索引

索引是利用合适的数据结构加快查询的过程,而聚簇索引和非聚簇索引的区别就是------数据本身是否存储在数据结构中。上面所说的InnoDB引擎索引就是一种聚簇索引,而MyISAM引擎则采用的是飞聚簇索引,即叶子结点存放指向数据的指针而非数据本身


创建多个索引

如果表中有多个索引(InnoDB引擎),那么就会创建多个不同的索引数据结构,也就是B+树。但是他们不会都存放数据本身,这样太浪费空间。一般来说只有主键索引或者隐藏列索引会存放数据,普通索引存放的数据内容就是主键或者隐藏列,在使用普通索引查找一个元素的时候,先查找普通索引结构确定该普通索引的值对应的主键是谁,然后再通过该值查找主键索引结构获取数据,这个过程叫做回表查询**(当然如果是非聚簇索引就没那么多讲究,因为他根本不存放数据,多个索引结构可以很自然的共享数据)**

注意:只要存在主键,主键索引就一定会被创建,哪怕你是创建表成功后再添加主键,mysql也会让主键索引替换隐藏列索引,如果本来是非隐藏列索引,会保留旧的索引而不是替换**。**


复合索引

**复合索引就是利用多个属性字段构建同一个索引(即同一颗B+树),而它只是排序规则和非复合索引不同,以index(a,b,c)为例,先按照a排序,相同的话再按照b排序。**说白了,复合索引就是使用新的排序规则构建B+树

复合索引的最左匹配原则:使用复合索引,查数据必须从左往右指明索引字段,不能跳、不能插队,否则复合索引就会失效。


全文索引的简单介绍

全文索引就是 MySQL 专门给「长文本关键词搜索」做的专用索引,用来解决 LIKE '%关键词%' 巨慢的问题(毕竟B+树只支持左前缀模糊匹配----核心转化:模糊查询 → 范围查询对于 LIKE 'abc%',等价于查询:Key >= 'abc' AND Key < 'abd'当对大量文字的字段进行检索时,会使用到全文索引。MySQL提供全文索引机制,但要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用sphinx的中文版(coreseek)。

全文索引的数据结构不是B+树。

创建全文索引:

sql 复制代码
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body) --把 title(标题)和 body(正文)两列合并成一个「大文本」,一起做分词、一起建倒排索引。搜索时,只要标题 或 正文里包含关键词,就算匹配。            
)engine=MyISAM;

使用全文索引:

sql 复制代码
SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database');

索引覆盖

我们上面讲解过,如果存在多个索引的话,使用普通索引查询数据项要进行回表查询,比较麻烦。而索引覆盖就是让普通索引page中的数据不止存放主键,还存放该数据相关的一些其他常用字段值,这样一来,如果用户查询的字段都在这几个字段之内,就不用回表查询了,提高了检索速度。


索引的缺点

索引虽然提高了查找速度,但也让我们除了维护数据,还必须维护索引数据结构,在插入或者删除以及修改某数据的时候,都可能导致B+树的结构发生变化,而且有多少个索引就要更新多少次数据结构,做多少次IO。这样来看,索引让增删改的速度降低了


索引创建

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

-- 在表定义时,在某列后直接指定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);

--在表的定义最后,指定某列为索引
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); 

-- 创建一个索引名为 idx_name 的索引
create table user10(id int primary key, name varchar(20), email varchar(30));
create index idx_name on user10(name);
相关推荐
元宝骑士2 小时前
深度解析 ROW_NUMBER() 窗口函数:从入门到实战避坑指南
后端·mysql
014-code3 小时前
MySQL 常用业务 SQL
数据库·sql·mysql
y = xⁿ3 小时前
【MySQL】数据库的脏读,不可重复读和幻读,覆盖索引是什么,索引类型有哪些
数据库·mysql
羊小蜜.4 小时前
Mysql 07: 正则表达式查询(REGEXP)全解
数据库·mysql·正则表达式
Dxy12393102164 小时前
正则表达式如何匹配提取文章日期
数据库·mysql·正则表达式
元宝骑士4 小时前
MySQL联表查询优化实战:小表驱动大表的联合索引设计
后端·mysql
前进的李工4 小时前
MySQL用户管理与权限控制指南(含底层架构说明)
开发语言·数据库·sql·mysql·架构
一直都在5725 小时前
MySQL索引优化
android·数据库·mysql
脑子加油站5 小时前
MySQL8数据库高级特性
数据库·mysql