
MySQL学习:
https://blog.csdn.net/2301_80220607/category_12971838.html?spm=1001.2014.3001.5482
前言:
在前面我们基本上已经把MySQL的基础知识都进行了学习,但是我们之前处理的数据都是十分少的,但是如果当我们的数据量很大的时候,比如一张表中有一百万个数据,我们要对其中一个进行查询的时候,效率就会很慢了,这个时候我们就可以借助索引来帮我们完成提高效率的工作,索引简单点来说就是将数据以特定的数据结构组织起来,从而方便查询和管理
索引是数据库中一种特殊的数据结构,它类似于书籍的目录,能够帮助数据库系统快速定位到表中的特定数据,而不必扫描整个表。索引本质上是通过额外的数据结构(如B+树、哈希表等)对表中的一个或多个列的值进行排序和组织,从而加速数据检索操作。
索引可以提高数据库的性能,而且索引不用加内存,不用改程序,不用调sql,只需要执行正确的创建索引语句,就可以很方便的帮助我们在大量的数据中进行查找工作,它的本质就是通过特定的数据结果对数据库中的数据进行管理,主要是通过B+树
没有什么东西是百利而无一弊的,索引虽然可以提高我们的查询速度,但是插入、更新、和删除的速度降低为代价的,因为本来这些操作就涉及大量的IO操作,索引的创建会增加IO操作次数,IO操作会大大影响这些操作的效率。同时索引的创建还会消耗额外的内存空间。
但是对于拥有海量数据的数据库,索引的创建仍是很有必要的。
关于索引的使用其实还是比较简单的,关键是我们需要明白索引的实现原理是什么,这里我们就讲一下索引的底层实现
在计算机硬盘的实现中,硬盘是由多个扇面组成的,每个扇面上又被划成不同的扇区,每个扇区的大小都是512字节,即我们存储在磁盘中的内容都是以512字节作为存储单元的。
那这是不是意味着我们所有的I/O操作都是以512字节为单位的呢?
答案其实是否定的。不同的服务进行I/O操作的单位其实是不同的,比如我们的MySQL的操作单位是16KB,那为什么不是512字节而是16KB呢?这其实与MySQL服务的所在层级有关

如图,MySQL服务实际上是作为一个服务进程在应用层跑动的,所以MySQL并不是直接与磁盘或内存进行交互的,它是通过操作系统(OS)提供的接口与磁盘进行数据的传送的,虽然磁盘的操作单位为512字节,但是MySQL服务综合考虑速度、容量等各方面因素,它所选择的操作单位是16KB
实现方法就是:在操作系统层次和MySQL服务应用层上实际上都有一个文件缓冲区的存在,MySQL服务写入的内容在将buffer pool写满之后传给操作系统,操作系统再将这部分内容传给磁盘;同理,磁盘的操作也是这样的,磁盘将MySQL服务所需数据传给操作系统,操作系统再将数据传给MySQL服务
这个基本的存储单元就叫做MySQL的页
还遗漏了一个重要的知识点是在MySQL的底层实现中,不同的存储引擎的实现是不同的,但是不同的地方主要体现在页的管理上,上面的内容基本上实现还都是一样的
建立测试表:
create table if not exists user (
id int primary key, --一定要添加主键哦,只有这样才会默认生成主键索引
age int not null,
name varchar(16) not null
);
插入多条测试记录:
--插入多条记录,注意,我们并没有按照主键的大小顺序插入哦
mysql> insert into user (id, age, name) values(3, 18, '杨过');
Query OK, 1 row affected (0.01 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.00 sec)
mysql> insert into user (id, age, name) values(1, 56, '欧阳锋');
Query OK, 1 row affected (0.00 sec)查看插入结果:
select * from user;
观察这个插入结果,我们可以发现,我们并没有按照id顺序来插入数据,但是最后却发现插入后的数据是按照id来排序的,id是主键,其实原因是建表时被设置为主键的列会默认建立索引,索引通过这样排序的方式就可以帮助我们更快的查找到我们想要的数据
但是仅仅知道这些还是不够的,我们需要知道索引工作的原理,下面我们就来看一下
上面我们讲过MySQL与磁盘的交互单位为page(16KB),但是为什么呢?为什么不用多少,加载多少呢?
这其实是为了提高效率,比如上面我们插入的数据,我们现在如果要查找id=1的记录,按照用多少取多少的方式,我们就需要直接把它从磁盘加载到MySQL服务端,进行依次I/O操作,如果又想查id=2的记录,就有需要再进行这样一次的I/O
需要注意的是这样的I/O操作在计算机运行中,会消耗大量的空间,所以为了提高效率我们必须想办法减少I/O操作的次数,所以我们就可以一次直接I/O更多的数据(page),比如把五条记录全部取了,这样不管要哪条记录,我们的服务端都可以直接在自己的缓冲区中找就可以了,这样就节省的大量的时间
当然并不是每次要取的数据都能在同一个page页,但是根据局部性原理,还是能够保证我们在大部分情况下都是效率更高的
上面讲了page的概念后,实际上我们就应该认识到page作为MySQL的存储单元一定会伴随着许多的设计的。
MySQL中有很多表,这些表中存放着大量的数据,我们可以理解成这些表中的数据是存放在一个或多个page中的,由于大量page表的存在,所以我们需要对page进行组织管理

如上,就是page的基本构成,page中的数据是以链表的形式存放的,同时page自身也是通过链表的形式进行组织的,它里面有两个指针分别指向前一个page和后一个page

现在有一本书,我们找到其中某些内容的时候,一定是先看目录,找到这些内容对应的页数,然后再根据页数再去找这部分内容,这样可以帮助我们节省很多时间。
页目录会占用几页,这是一种空间换时间的做法,但是仍然是十分值得去做的
为了方便我们的page进行高效查找,我们可以对page页也进行添加目录操作,即可以添加在page内对page中内容进行管理,也可以添加在page外对page进行管理
添加在page内

那么当前,在一个Page内部,我们引入了目录。比如,我们要查找id=4记录,之前必须线性遍历4次,才能拿到结果。现在直接通过目录2[3],直接进行定位新的起始位置,提高了效率。现在我们可以再次正式回答上面的问题了,为何通过键值 MySQL 会自动排序?
添加在page外
MySQL 中每一页的大小只有 16KB ,单个Page大小固定,所以随着数据量不断增大, 16KB 不可能存下所有的数据,那么必定会有多个页来存储数据。
我们前面讲过page页中会有两个指针的,这两个指针就是帮助我们建立这样的双链表的结构的

在单表数据不断被插入的情况下, MySQL 会在容量不足的时候,自动开辟新的Page来保存新的数据,然后通过指针的方式,将所有的Page组织起来。 需要注意,上面的图,是理想结构,大家也知道,目前要保证整体有序,那么新插入的数据,不一定会在新Page上面,这里仅仅做演示。 这样,我们就可以通过多个Page遍历,Page内部通过目录来快速定位数据。可是,貌似这样也有效率问题,在Page之间,也是需要 MySQL 遍历的,遍历意味着依旧需要进行大量的IO,将下一个Page加载到内存,进行线性检测。这样就显得我们之前的Page内部的目录,有点杯水车薪了。
那么解决方法是什么呢?解决方法,其实就是我们之前的思路,给page也带一个目录

存在一个目录页来管理页目录,目录页中的数据存放的就是指向的那一页中最小的数据。有数据,就可通过比较,找到该访问那个Page,进而通过指针,找到下一个Page。
其实目录页的本质也是页,普通页中存的数据是用户数据,而目录页中存的数据是普通页的地址。 可是,我们每次检索数据的时候,该从哪里开始呢?虽然顶层的目录页少了,但是还要遍历啊?不用担心,可以在加目录页

我们观察就可以发现这就是数据结构中的B+树啊!!至此,我们就给我们的user表建立了主键索引。现在随便找一个id=?的数据,我们会发现查询速度会快很多。
以上就是索引的底层实现,但是需要注意的是我们讲的这种B+树的底层实现方式,适用的主要是MyISAM存储引擎,不同的存储引擎的底层实现可能是不同的,比如我们还常用的另一种存储引擎InnoDB就是常用B树来作为底层数据结构,用B树实现的存储引擎它的用户数据和索引数据不会分离,被称为聚簇索引;而用B+树实现的索引类型一般为非聚簇索引
-- 在创建表的时候,直接在字段名后指定 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); --创建完表以后指定某列为普通索引create table user10(id int primary key, name varchar(20), email varchar(30));
-- 创建一个索引名为 idx_name 的索引
create index idx_name on user10(name);普通索引的创建:
当对文章字段或有大量文字的字段进行检索时,会使用到全文索引。MySQL提供全文索引机制,但是有要求,要求表的存储引擎必须是MyISAM,而且默认的全文索引支持英文,不支持中文。如果对中文进行全文检索,可以使用sphinx的中文版(coreseek)。
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (title,body)
)engine=MyISAM;
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 ...');如果使用如下查询方式,虽然查询出数据,但是没有使用到全文索引
select * from articles where body like '%database%';
可以用explain工具看一下,是否使用到索引
explain select * from articles where body like '%database%'\G
SELECT * FROM articles
-> WHERE MATCH (title,body) AGAINST ('database');
通过explain来分析这个sql语句
explain SELECT * FROM articles WHERE MATCH (title,body) AGAINST ('database')\G
show keys from user\G
以上就是关于索引的基本知识和原理,关于索引的用法还有很多,比如复合索引、索引覆盖、索引最左匹配原则等,有关索引的更多知识感兴趣的可以自行深入了解学习
感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!!