[MySQL] 索引
索引
- 1.为什么有索引?
- 2.MySQL的存储(MySQL与磁盘交互的基本单位)
- 3.小总结
- 4.索引的进一步理解
- 4.1测试案例
- 4.2 理解单个page
- 4.3 理解多个page
- 页目录
- 单页情况
- 多页情况
- 4.4 B+树 VS B树
- 4.5 聚簇索引 VS 非聚簇索引
- 1.非聚簇索引
- 2.聚簇索引
- 5.索引操作
- 5.1创建主键索引(其实就是定义主键)
- 5.2 唯一键索引的创建
- 5.3 普通索引的创建
- 5.4 全文索引的创建
- 5.5 查询索引
- 5.6 删除索引
- 5.7 索引的创建规则
此片需要用到的文档在我的gitee中的工具分支中
tpoog的gitee
1.为什么有索引?
索引是为了提高数据库的性能。 索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行 正确的create index,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度。
看定义跟没看一样,所以我们创建一个海量数据的数据库来看看所谓的检测速度。
案例:我们通过查询的时间来观察现象
1.查询员工编号为998877的员工
--原始方法
select * from EMP where empno=998877;
可以看到我们只进行了查找就花了将近6s的时间,这在我们实际生产的时候是不可容忍的。
解决方法:创建索引
alter table EMP add index(empno);
我们第二次的寻找甚至没有用到0.1s
2.MySQL的存储(MySQL与磁盘交互的基本单位)
我们之前学习操作系统的时候知道了系统读取磁盘是以块作为单位的(4KB)
但是mysql作为一款应用软件,他有着更高的IO场景,所以为了提高IO的效率,mysql进行IO的基本单位是16KB的。
我们也可以通过系统函数查看这个基本单位
> show global status like ‘innodb_page_size’;
也就是说,磁盘这个硬件设备的基本单位是512 字节,而即,M ySQL 和磁盘进行数据交互的基本单位是MySQL
InnoDB引擎使用16KB 进行IO交互。16KB 。这个基本数据单元,在MySQL 这里叫做page(注意和系统的page区分)
我们画一张图来理解这段话
OS和磁盘之间的IO是4KB的,但是为了满足mysql的IO,在OS中创建一个缓冲区,存放从磁盘读取的数据,让后应用层和OS的数据交换基本单位就是16KB,所以看似是磁盘和应用层这一步,实际上是分成了两步完成的。
3.小总结
- MySQL 中的数据文件,是以page为单位保存在磁盘当中的
- MySQL 的CURD 操作,都需要通过计算,找到对应的插入位置,或者找到对应要修改或者查询的数据。
- 而只要涉及计算,就需要CPU参与,而为了便于CPU参与,一定要能够先将数据移动到内存当中
- 为了更好的进行上面的操作,MySQL 服务器在内存中运行的时候,在服务器内部,就申请了被称uffer Pool的的大内存空间,来进行各种缓存。其实就是很大的内存空间,来和磁盘数据进行IO交互。
- 为何更高的效率,一定要尽可能的减少系统和磁盘IO的次数
4.索引的进一步理解
4.1测试案例
建立测试表
mysql> create table my_user(
-> id int primary key,
-> age int not null,
-> name varchar(20) not null
-> );
乱序插入数据
mysql> insert into my_user (id, age, name) values(3, 18, '杨过');
Query OK, 1 row affected (0.00 sec)
mysql> insert into my_user (id, age, name) values(4, 16, '小龙女');
Query OK, 1 row affected (0.00 sec)
mysql> insert into my_user (id, age, name) values(2, 26, '黄蓉');
Query OK, 1 row affected (0.00 sec)
mysql> insert into my_user (id, age, name) values(5, 36, '郭靖');
Query OK, 1 row affected (0.00 sec)
mysql> insert into my_user (id, age, name) values(1, 56, '欧阳锋');
Query OK, 1 row affected (0.00 sec)
现象:我们乱序插入的数据,在查看的时候居然是有序的?
那么为什么要排序?排序的好处是什么?
带着这个问题我们理解page
中断一下:为什么IO交互要是page??用多少,加载多少不香吗?
4.2 理解单个page
MySQL 中要管理很多数据表文件,而要管理好这些文件,就需要成一个个独立文件是有一个或者多个Page构成的。先描述,在组织,我们目前可以简单理解成一个个独立文件是有一个或者多个Page构成的。
page的大小就是16kb的,使用prev和next让他成为一个双向链表 因为有主键的问题,prev 和next
构成双向链表MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的。
这时候就可以回答上面的问题为什么要排序?
插入数据时排序就是为了优化查询的效率,我们看到上面存储数据的结构就是一个链表而链表的特点是什么?增删快,查找慢,所以优化查找效率是必须的。
正式因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,而且,如果运气好,是可以提前结束查找过程的。
4.3 理解多个page
上面的一个page,功能就是在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。但是本质上的查找还是逐条比较的线性查找。如果有1千万条数据,一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的。那么,查找特定一条记录,也一定是线性查找。这效率也太低了。
页目录
我们在看语文课本的时候,假如今天上课上到了《滕王阁序》那么我们如何快速的找到这一页呢?两种方法
- 一页一页的翻找
- 通过目录查看他在那一页,然后快速定位
很明显第二种方式更快,但是目录也需要花纸张,所以目录就是一种 “以空间换取时间的方式”而我们的MySQL也是应用了这种思想。
单页情况
针对上面的单页Page,我们能否也引入目录呢?
之前我们查找4号的时候,需要遍历四次,但是引入目录之后,4号更靠近目录2,然后即可快速定位。
我们再次回答上面为什么要排序的问题?
就是为了方便引入目录结构
多页情况
既然单页都可以引入目录,那自然我们的多页也可以引入目录结构。
实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址。
完整的结构:
注意看这里的目录中是没有存储数据的,所以这一个结构就是所谓的B+树。
至此,我们已经给我们的表user构建完了主键索引。随便找一个id=?我们发现,现在查找的Page数一定减少了,也就意味着IO次数减少了,那么效率也就提高了。
思考:为什么别的数据结构不可以呢?
4.4 B+树 VS B树
区别:
- B树节点,既有数据也有page指针,而B+树只有叶子节点才有数据,页目录只用键值+page指针
- B+树的叶子节点全部相连,但是B树没有
为什么选择B+树?
- 节点不存储data,这样一个节点就可以存储更多的key。可以使得树更矮,所以IO操作次数更少
- 叶子节点相连,更便于进行范围查找
4.5 聚簇索引 VS 非聚簇索引
上述我们说的索引都是在存储引擎是InnoDB的背景下。
MyISAM 存储引擎-主键索引 MyISAM
最大的特点是,将索引Page和数据Page分离,也就是叶子节点没有数据,只有对应数据的地址。
相较于InnoDB 索引,InnoDB是将索引和数据放在一起的。
我们来看看他们在文件中的存放情况
1.非聚簇索引
mysql> create table mtest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=MyISAM;
Query OK, 0 rows affected (0.01 sec)
红色标注的对应的分别是
- 表结构数据
- -该表对应的数据,当前没有数据,所以是0
- 该表对应的主键索引数据
其中,MyISAM 这种用户数据与索引数据分离的索引方案,叫做非聚簇索引
2.聚簇索引
mysql> create table itest(
-> id int primary key,
-> name varchar(11) not null
-> )engine=InnoDB;
Query OK, 0 rows affected (0.01 sec)
红色标注的对应的分别是
- 表的结构
- 该表对应的主键索引和用户数据,虽然现在一行数据没有,但是该表并不为0,因为有主键索引数据
其中,InnoDB 这种用户数据与索引数据在一起索引方案,叫做聚簇索引
下图就是基于MyISAM 的Col2 建立的索引,和主键索引没有差别
InnoDB 除了主键索引,用户也会建立辅助(普通)索引
可以看到,InnoDB 的非主键索引中叶子节点并没有数据,而只有对应记录的key值。
回表查询:
通过辅助(普通)索引,找到目标记录,需要两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。这种过程,就叫做回表查询
5.索引操作
5.1创建主键索引(其实就是定义主键)
5.2 唯一键索引的创建
5.3 普通索引的创建
5.4 全文索引的创建
全文索引键的创建目的是:找到一个列内部的某些字段(列内部的数据非常的长)
mysql> CREATE TABLE articles (
-> id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
-> title VARCHAR(200),
-> body TEXT,
-> FULLTEXT (title,body)
-> )engine=MyISAM;
Query OK, 0 rows affected (0.00 sec)
mysql> 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 ...');
Query OK, 6 rows affected (0.00 sec)
Records: 6 Duplicates: 0 Warnings: 0
查询有没有database数据
不用索引的方式:
select * from articles where body like '%database%';
我们可以查一查是否用到了索引
explain select * from articles where body like '%database%'\G
select * from articles where match(title, body) against('database');
5.5 查询索引
5.6 删除索引
5.7 索引的创建规则
- 经常被查找的数据建议索引
- 唯一性太差的字段不适合索引(比如性别)
- 更新很频繁的字段也不适合,每次改动树也跟着改变
- 不会出现在where字句的字段不适合索引