自学内容网 自学内容网

MySQL之索引

目录

一:索引概论

二:从硬件上理解--磁盘

三:从软件上理解

四:建立共识

五: 索引的理解

六: 索引操作


一:索引概论

(1)在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构,是帮助MySQL高效获取数据的数据结构,它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。
索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序。数据库使用索引以找到特定值,然后顺指针找到包含该值的行。这样可以使对应于表的SQL语句执行得更快,可快速访问数据库表中的特定信息

(2)索引:提高数据库的性能,索引是物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行正确的
create index ,查询速度就可能提高成百上千倍。但是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的IO。所以它的价值,在于提高一个海量数据的检索速度。
(3)常见索引分为:
主键索引(primary key)
唯一索引(unique)
普通索引(index)
全文索引(fulltext)--解决中子文索引问题

当没有索引时,搜索海量数据将会变得很慢,但在加入索引后,就会变得很快

二:从硬件上理解--磁盘

1.MySQL 给用户提供存储服务,而存储的都是数据,数据在磁盘这个外设当中。磁盘是计算机中的一个机械设备,相比于计算机其他电子元件,磁盘效率是比较低的,在加上IO本身的特征

2.磁盘

中间部分有主轴马达做高速旋转,盘片也是在做高速旋转,磁头左右摆动进行寻址,找到磁盘当中特定盘面上的特定柱面,在柱面上找到磁道和对应扇区 

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

 3.定位扇区

(1)柱面(磁道): 多盘磁盘,每盘都是双面,大小完全相等。那么同半径的磁道,整体上便构成了一个柱面

(2)每个盘面都有一个磁头,那么磁头和盘面的对应关系便是1对1的

(3)所以,我们只需要知道,磁头(Heads)、柱面(Cylinder)(等价于磁道)、扇区(Sector)对应的编号。即可在磁盘上定位所要访问的扇区。这种磁盘数据定位方式叫做 CHS 。不过实际系统软件使用的并不是 CHS (但是硬件是),而是 LBA ,一种线性地址,可以想象成虚拟地址与物理地址。系统将 LBA 地址最后会转化成为 CHS ,交给磁盘去进行数据读取。 

4.在系统软件上,就直接按照扇区(512字节,部分4096字节),进行IO交互吗?不是

如果操作系统直接使用硬件提供的数据大小进行交互,那么系统的IO代码,就和硬件强相关,换言之,如果硬件发生变化,系统必须跟着变化
从目前来看,单次IO 512字节,还是太小了。IO单位小,意味着读取同样的数据内容,需要进行多次磁盘访问,会带来效率的降低

文件系统读取基本单位,就不是扇区,而是数据块.故系统读取磁盘是以块为单位的,基本单位是4KB

5. 磁盘随机访问(Random Access)与连续访问(Sequential Access)

随机访问:本次IO所给出的扇区地址和上次IO给出扇区地址不连续,这样的话磁头在两次IO操作之间需要作比较大的移动动作才能重新开始读/写数据。
连续访问:如果当次IO给出的扇区地址与上次IO结束的扇区地址是连续的,那磁头就能很快的开始这次IO操作,这样的多个IO操作称为连续访问。
因此尽管相邻的两次IO操作在同一时刻发出,但如果它们的请求的扇区地址相差很大的话也只能称为随机访问,而非连续访问。
磁盘是通过机械运动进行寻址的,随机访问不需要过多的定位,故效率比较高

三:从软件上理解

1. MySQL 作为一款应用软件,可以想象成一种特殊的文件系统。它有着更高的IO场景,所以,为了提高基本的IO效率, MySQL 进行IO的基本单位是 16KB。也就是说,磁盘这个硬件设备的基本单位是 512 字节,而 MySQL InnoDB引擎 使用 16KB 进行IO交互。即, MySQL 和磁盘进行数据交互的基本单位是 16KB 。这个基本数据单元,在 MySQL 这里叫做page

2.16KB是站在mysql角度向操作系统提出的,在os内部是存在对应的文件缓冲区的,对mysql操作就是对文件进行操作,在mysql中获取到文件fd,文件描述符,这时操作系统在做磁盘级访问时,就会把16KB的访问方式转换成文件系统级的4*4KB的方式

3.mysql要有自己的bufferpool,mysql在进行IO的时候,会将读到的数据写到bufferpool当中,再将bufferpool中的数据刷新到操作系统的缓冲区当中,再刷新到磁盘中

四:建立共识

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

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

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

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

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

(6)为了更高的效率,一定要尽可能的减少系统和磁盘IO的次数

五: 索引的理解

1.引出问题

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

mysql> show create table user \G;
*************************** 1. row ***************************
       Table: user
Create Table: CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `age` int(11) NOT NULL,
  `name` varchar(16) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8  --默认就是InnoDB存储引擎
1 row in set (0.00 sec)


mysql> insert into user (id, age, name) values(3, 18, '杨过');
Query OK, 1 row affected (0.00 sec)

mysql> insert into user (id, age, name) values(4, 16, '小龙女');
Query OK, 1 row affected (0.00 sec)

mysql> insert into user (id, age, name) values(2, 26, '黄蓉');
Query OK, 1 row affected (0.01 sec)

mysql> insert into user (id, age, name) values(5, 36, '郭靖');
Query OK, 1 row affected (0.01 sec)

mysql> insert into user (id, age, name) values(1, 56, '欧阳锋');
Query OK, 1 row affected (0.00 sec)

mysql> select* from user;
+----+-----+-----------+
| id | age | name      |
+----+-----+-----------+
|  1 |  56 | 欧阳锋    |
|  2 |  26 | 黄蓉      |
|  3 |  18 | 杨过      |
|  4 |  16 | 小龙女    |
|  5 |  36 | 郭靖      |
+----+-----+-----------+

会发现当无序插入数据时,mysqld会自己自动对数据进行排序,这是为啥会做排序呢,往下看 

2.IO交互时使用page的原因

为何MySQL和磁盘进行IO交互的时候,要采用Page的方案进行交互呢?用多少,加载多少不香吗?
如上面的5条记录,如果MySQL要查找id=2的记录,第一次加载id=1,第二次加载id=2,一次一条记录,那么就需要2次IO。如果要找id=5,那么就需要5次IO。


但如果这5条(或者更多)都被保存在一个Page中(16KB,能保存很多记录),那么第一次IO查找id=2的时候,整个Page会被加载到MySQL的Buffer Pool中,这里完成了一次IO。但是往后如果在查找id=1,3,4,5等,完全不需要进行IO了,而是直接在内存中进行了。所以,就在单Page里面,大大减少了IO的次数。


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

3.理解mysql中的单个page

在mysql内部数据是巨大的,而一个page只有16KB,这就说明一定需要并会存在大量的page,也就决定了,mysql必须将多个同时存在的page管理起来。要管理所有的mysql内的page,需要先描述,再组织 。所以不要简单的将page认为是一个内存块,在page内部也是有各种字段信息的,在buffer pool内部将所有的page用链表的形式给管理起来

不同的 Page ,在 MySQL 中,都是 16KB ,使用 prev 和 next 构成双向链表
因为有主键的问题, MySQL 会默认按照主键给我们的数据进行排序,从上面的Page内数据记录可以看出,数据是有序且彼此关联的 

做排序的意义:插入数据时排序的目的,就是优化查询的效率。
页内部存放数据的模块,实质上也是一个链表的结构,链表的特点也就是增删快,查询修改慢,所以优化查询的效率是必须的。
正是因为有序,在查找的时候,从头到后都是有效查找,没有任何一个查找是浪费的,而且,如果运气好,是可以提前结束查找过程的

4.理解多个page

通过上面的分析,我们知道,上面页模式中,只有一个功能,就是在查询某条数据的时候直接将一整页的数据加载到内存中,以减少硬盘IO次数,从而提高性能。但是,我们也可以看到,现在的页模式内部,实际上是采用了链表的结构,前一条数据指向后一条数据,本质上还是通过数据的逐条比较来取出特定的数据。
如果有1千万条数据,一定需要多个Page来保存1千万条数据,多个Page彼此使用双链表链接起来,而且每个Page内部的数据也是基于链表的。那么,查找特定一条记录,也一定是线性查找。这效率也太低了。

5.页目录

类似于打开一本书时,开头几页的目录。目录,是一种“空间换时间的做法” 

6.对单页page引入页目录

那么当前,在一个Page内部,我们引入了目录。比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率。
为何通过键值 MySQL 会自动排序?是可以很方便引入目录 

7.多页page引入页目录

MySQL 中每一页的大小只有 16KB ,单个Page大小固定,所以随着数据量不断增大, 16KB 不可能存下所有的数据,那么必定会有多个页来存储数据

在单表数据不断被插入的情况下, MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来

这样,我们就可以通过多个Page遍历,Page内部通过目录来快速定位数据。可是,貌似这样也有效率问题,在Page之间,也是需要 MySQL 遍历的,遍历意味着依旧需要进行大量的IO,将下一个Page加载到内存,进行线性检测。这样就显得我们之前的Page内部的目录,有点杯水车薪了

那么如何解决呢?解决方案,其实就是我们之前的思路,给Page也带上目录

存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据。有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个Page

其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址。
可是,我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?不用担心,可以在加目录页

总结:   Page分为目录页和数据页。目录页只放各个下级Page的最小键值。
查找的时候,自定向下找,只需要加载部分目录页到内存,即可完成算法的整个查找过程,大大减少了IO次数 

8.InnoDB 在建立索引结构来管理数据的时候,其他数据结构为何不行

9.聚簇索引 VS 非聚簇索引

这种用户数据与索引数据分离的索引方案,叫做非聚簇索引

六: 索引操作

 1.创建主键索引

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

主键索引的特点:
一个表中,最多有一个主键索引,当然可以使复合主键
主键索引的效率高(主键不可重复)
创建主键索引的列,它的值不能为null,且不能重复
主键索引的列基本上是int 

2.唯一索引的创建

第一种方式
-- 在表定义时,在某列后直接指定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,等价于主键索引 

3.普通索引的创建

第一种方式
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);

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

4.查询索引

show index from 表名 \G;

 show keys from 表名 \G;

5.删除索引

(1)删除主键索引: alter table 表名 drop primary key;

(2)其他索引的删除:alter table 表名 drop index 索引名;索引名就是show keys from 表名中的
Key_name 字段

(3)drop index 索引名 on 表名

6.索引创建原则
(1)比较频繁作为查询条件的字段应该创建索引
(2)唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
(3)更新非常频繁的字段不适合作创建索引

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


原文地址:https://blog.csdn.net/Miwll/article/details/142376497

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!