标签: 标签: 「咱们都是小青蛙」公众号文章mysql
SQL
的全称是Structured Query Language
,翻译成中国话就是结构化查询语言
。这是一种声明式的语法,何为声明式?能够联想一下咱们生活中的老板,老板在布置任务的时候会告诉你:小王啊,今天把这些砖从A地搬到B地啊,而后就没而后了。老板并不关心你是用手抬,仍是用车拉,老板只关心结果:你把砖搬过去就行了。咱们之于数据库而言,就是一个老板,SQL
语句就是咱们给数据库下达的任务,至于具体数据库怎么执行咱们并不关心,咱们只关心最后数据库给咱们返回的结果。程序员
对于设计数据库的人而言,语句怎么执行就得好好考虑了,老板不操心,事儿总还得干。设计MySQL
的大叔人为的把MySQL
分为server
层和存储引擎
层,可是什么操做是在server
层作的,什么操做是在存储引擎
层作的你们可能有些迷糊。本文将以一个实例来展现它们两者各自负责的事情。sql
为了故事的顺利发展,咱们先建立一个表:数据库
CREATE TABLE hero (
id INT,
name VARCHAR(100),
country varchar(100),
PRIMARY KEY (id),
KEY idx_name (name)
) Engine=InnoDB CHARSET=utf8;
复制代码
咱们为hero
表的id
列建立了聚簇索引,为name
列建立了一个二级索引。这个hero
表主要是为了存储三国时的一些英雄,咱们向表中插入一些记录:bash
INSERT INTO hero VALUES
(1, 'l刘备', '蜀'),
(3, 'z诸葛亮', '蜀'),
(8, 'c曹操', '魏'),
(15, 'x荀彧', '魏'),
(20, 's孙权', '吴');
复制代码
如今表中的数据就是这样的:学习
mysql> SELECT * FROM hero;
+----+------------+---------+
| id | name | country |
+----+------------+---------+
| 1 | l刘备 | 蜀 |
| 3 | z诸葛亮 | 蜀 |
| 8 | c曹操 | 魏 |
| 15 | x荀彧 | 魏 |
| 20 | s孙权 | 吴 |
+----+------------+---------+
5 rows in set (0.00 sec)
复制代码
准备工做就作完了。优化
一条语句在执行以前须要生成所谓的执行计划,也就是该语句将采用什么方式来执行(使用什么索引,采用什么链接顺序等等),咱们能够经过Explain
语句来查看这个执行计划,比方说对于下边语句来讲:ui
mysql> EXPLAIN SELECT * FROM hero WHERE name < 's孙权' AND country = '蜀';
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
| 1 | SIMPLE | hero | NULL | range | idx_name | idx_name | 303 | NULL | 2 | 20.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+------------------------------------+
1 row in set, 1 warning (0.03 sec)
复制代码
输出结果的key
列值为idx_name
,type
列的值为range
,代表会针对idx_name
二级索引进行一个范围查询。不少同窗在这里有一个疑惑:究竟是一次性把全部符合条件的二级索引都取出来以后再统一进行回表操做,仍是每从二级索引中取出一条符合条件的记录就进行回表一次?其实server层和存储引擎层的交互是以记录为单位的,上边这个语句的完整执行过程就是这样的:spa
server层第一次开始执行查询,把条件name < 's孙权'
交给存储引擎,让存储引擎定位符合条件的第一条记录。翻译
存储引擎在二级索引idx_name
中定位name < 's孙权'
的第一条记录,很显然符合该条件的二级索引记录的name
列的值为'c曹操'
。而后须要注意,咱们看到EXPLAIN
语句的输出结果的Extra
列有一个Using index condition
的提示,这代表会将有关idx_name
二级索引的查询条件放在存储引擎层判断一下,这个特性就是所谓的索引条件下推
(Index Condition Pushdown,简称ICP
)。很显然这里的ICP
条件就是name < 's孙权'
。有的同窗可能会问这不就是脱了裤子放屁么,name
值为'c曹操'
的这条记录就是经过name < 's孙权'
这个条件定位的,为啥还要再判断一次?这就是设计MySQL 的大叔的粗暴设计,十分简单,没有为啥~
小贴士: 对于使用二级索引进行等值查询的状况有些许不一样,比方说上边的条件换成`name = 's孙权'`,对于等值查询的这种状况,设计MySQL的大叔在InnoDB存储引擎层有特殊的处理方案,是不做为ICP条件进行处理的。
而后拿着该二级索引记录中的主键值去回表,把完整的用户记录都取到以后返回给server层
(也就是说获得一条二级索引记录后当即去回表,而不是把全部的二级索引记录都拿到后统一去回表)。
咱们的执行计划输出的Extra
列有一个Using Where
的提示,意味着server层在接收到存储引擎层返回的记录以后,接着就要判断其他的WHERE条件是否成立(就是再判断一下country = '蜀'
是否成立)。若是成立的话,就直接发送给客户端。
小贴士: 什么?发现一条记录符合条件就发送给了客户端?那为何个人客户端不是一条一条的显示查询结果,而是一会儿所有展现呢?这是客户端软件的鬼,人家规定在接收彻底部的记录以后再展现而已。
若是不成立的话,就跳过该条记录。
接着server层向存储引擎层要求继续读刚才那条记录的下一条记录。
由于每条记录的头信息中都有next_record
的这个属性,因此能够快速定位到下一条记录的位置,而后继续判断ICP
条件,存储引擎把下一条记录取出后就将其返回给server层。
而后重复第3步的过程,直到存储引擎层遇到了不符合name < 's孙权'
的记录,而后向server层返回了读取完毕的信息,这是server层将结束查询。
这个过程用语言描述仍是有点儿啰嗦,咱们写一个超级简化版的伪代码来瞅瞅(注意,是超级简化版):
first_read = true; //是不是第一次读取
while (true) {
if (first_read) {
first_read = false;
err = index_read(...); //调用存储引擎接口,定位到第一条符合条件的记录;
} else {
err = index_next(...); //调用存储引擎接口,读取下一条记录
}
if (err = 存储引擎的查询完毕信息) {
break; //结束查询
}
if (是否符合WHERE条件) {
send_data(); //将该记录发送给客户端;
} else {
//跳过本记录
}
}
复制代码
上述的伪代码虽然很粗糙,但也基本代表了意思哈~ 以后有机会咱们再唠叨唠叨使用临时表的状况已经使用filesort
的状况是怎么执行的。
写文章挺累的,有时候你以为阅读挺流畅的,那实际上是背后无数次修改的结果。若是你以为不错请帮忙转发一下,万分感谢~ 这里是个人公众号「咱们都是小青蛙」,里边有更多技术干货,时不时扯一下犊子,欢迎关注:
另外,做者还写了一本MySQL小册:《MySQL是怎样运行的:从根儿上理解MySQL》的连接 。小册的内容主要是从小白的角度出发,用比较通俗的语言讲解关于MySQL进阶的一些核心概念,好比记录、索引、页面、表空间、查询优化、事务和锁等,总共的字数大约是三四十万字,配有上百幅原创插图。主要是想下降普通程序员学习MySQL进阶的难度,让学习曲线更平滑一点~ 有在MySQL进阶方面有疑惑的同窗能够看一下: