小白debug

一起在知识的海洋里呛水

0%


今天这篇文章,其实也是我曾经面试中遇到过的真题。

分库分表大家可能听得多了,但读扩散问题大家了解吗?

这里涉及到几个问题。

分库分表是什么?

读扩散问题是什么?

分库分表为什么会引发读扩散问题?

怎么解决读扩散问题?

能不能不要在评论区叫我刁毛?


不好意思,失态了。


阅读全文 »


那天,我还在外面吃成都六姐的冒菜。

牛肉丸裹上麻酱后,狠狠嘬一口,都要入嘴了。

产品经理突然发来消息。

“线上有些用户不能注册了”

心想着”关我 x 事,又不是我做的模块”,放下手机。

不对,那老哥上礼拜刚离职了,想到这里,夹住毛肚的手微微颤抖

对面继续发:**”还有些用户不能改名”**

“如果用上表情符号的话,问题必现”

阅读全文 »

mysql 的索引为什么使用 B+树而不使用跳表?


在我们的印象中,mysql 数据表里无非就是存储一行行的数据。跟个 excel 似的。

直接遍历这一行行数据,性能就是 O(n),比较慢。为了加速查询,使用了B+树来做索引,将查询性能优化到了**O(lg(n))**。

但问题就来了,查询数据性能在 lg(n) 级别的数据结构有很多,比如 redis 的 zset 里用到的跳表,也是**lg(n)**,并且实现还贼简单。

那为什么 mysql 的索引,不使用跳表呢?

我们今天就来聊聊这个话题。


B+树的结构

之前的一篇文章里,已经提到过B+树的结构了。文章不长,如果没看过,建议先看下。

当然,不看也行。

在这里,为了混点字数,我简单总结下 B+树的结构。

B+树查询过程
如上图,一般 B+树是由多个页组成的多层级结构,每个页16Kb,对于主键索引来说,最末级的叶子结点放行数据,非叶子结点放的则是索引信息(主键 id 和页号),用于加速查询。

比方说我们想要查找行数据 5。会先从顶层页的 record 们入手。record 里包含了主键 id 和页号(页地址)。关注黄色的箭头,向左最小 id 是 1,向右最小 id 是 7。那 id=5 的数据如果存在,那必定在左边箭头。于是顺着的 record 的页地址就到了6号数据页里,再判断 id=5>4,所以肯定在右边的数据页里,于是加载105号数据页。

105号数据页里,虽然有多行数据,但也不是挨个遍历的,数据页内还有个页目录的信息,它可以通过二分查找的方式加速查询行数据,于是找到 id=5 的数据行,完成查询。

从上面可以看出,B+树利用了空间换时间的方式(构造了一批非叶子结点用于存放索引信息),**将查询时间复杂度从 O(n)优化为 O(lg(n))**。


跳表的结构

看完 B+树,我们再来看下跳表是怎么来的。

同样的,还是为了存储一行行的数据。

我们可以将它们用链表串起来。

单链表

想要查询链表中的其中一个结点,时间复杂度是 O(n),这谁顶得住,于是将部分链表结点提出来,再构建出一个新的链表。

两层跳表

这样当我想要查询一个数据的时候,我先查上层的链表,就很容易知道数据落在哪个范围,然后跳到下一个层级里进行查询。这样就把搜索范围一下子缩小了一大半。

比如查询 id=10 的数据,我们先在上层遍历,依次判断 1,6,12,很快就可以判断出 10 在 6 到 12 之间,然后往下一跳,就可以在遍历 6,7,8,9,10 之后,确定 id=10 的位置。直接将查询范围从原来的 1 到 10,变成现在的 1,6,7,8,9,10,算是砍半了。

两层跳表查找id为10的数据

既然两层链表就直接将查询范围砍半了,那我多加几层,岂不妙哉?

于是跳表就这样变成了多层。

三层跳表

如果还是查询 id=10 的数据,就只需要查询 1,6,9,10 就能找到,比两层的时候更快一些。

三层跳表查询id为10的数据

可以看出,跳表也是通过牺牲空间换取时间的方式提升查询性能。**时间复杂度都是 lg(n)**。


B+树和跳表的区别

从上面可以看到,B+树和跳表的最下面一层,都包含了所有的数据,且都是顺序的,适合用于范围查询。往上的层级都是构建出来用于提升搜索性能的。这两者实在是太像了。但他们两者在新增和删除数据时,还是有些区别的。下面我们以新增数据为例聊一下。


B+树新增数据会怎么样

B+树本质上是一种多叉平衡二叉树。关键在于”平衡“这两个字,对于多叉树结构来说,它的含义是子树们的高度层级尽量一致(一般最多差一个层级),这样在搜索的时候,不管是到哪个子树分支,搜索次数都差不了太多。

当数据库表不断插入新的数据时,为了维持 B+树的平衡,B+树会不断分裂调整数据页。

我们知道 B+树分为叶子结点和非叶子结点

当插入一条数据时,叶子结点和它上层的索引结点(非叶子结点)最大容量都是 16k,它们都有可能会满。

为了简化问题,我们假设一个数据页只能放三条行数据或索引。

加入一条数据,根据数据页会不会满,分为三种情况。

  • 叶子结点和索引结点都没满。这种情况最简单,直接插入到叶子结点中就好了。

叶子和非叶子都未满

  • 叶子结点满了,但索引结点没满。此时需要拆分叶子结点,同时索引结点要增加新的索引信息。

叶子满了但非叶子未满.drawio

  • 叶子结点满了,且索引结点也满了。叶子和索引结点都要拆分,同时往上还要再加一层索引。

叶子和非叶子都满了

从上面可以看到,只有在叶子和索引结点都满了的情况下,B+树才会考虑加入一层新的结点。

而从之前的文章知道,要把三层 B+树塞满,那大概需要 2kw 左右的数据。


跳表新增数据

跳表同样也是很多层,新增一个数据时,最底层的链表需要插入数据。

此时,是否需要在上面的几层中加入数据做索引呢?

这个就纯靠随机函数了。

理论上为了达到二分的效果,每一层的结点数需要是下一层结点数的二分之一。

也就是说现在有一个新的数据插入了,它有50%的概率需要在第二层加入索引,有25%的概率需要在第三层加个索引,以此类推,直到最顶层

举个例子,如果跳表中插入数据 id=6,且随机函数返回第三层(有 25%的概率),那就需要在跳表的最底层到第三层都插入数据。

跳表插入数据

如果这个随机函数设计成上面这样,当数据量样本足够大的时候,数据的分布就符合我们理想中的”二分”。

跟上面 B+树不一样,跳表是否新增层数,纯粹靠随机函数,根本不关心前后上下结点。


好了,基础科普也结束了,我们可以进入正题了。


mysql 的索引为什么使用 B+树而不使用跳表?

B+树是多叉树结构,每个结点都是一个 16k 的数据页,能存放较多索引信息,所以扇出很高三层左右就可以存储2kw左右的数据(知道结论就行,想知道原因可以看之前的文章)。也就是说查询一次数据,如果这些数据页都在磁盘里,那么最多需要查询三次磁盘 IO


跳表是链表结构,一条数据一个结点,如果最底层要存放2kw数据,且每次查询都要能达到二分查找的效果,2kw大概在2的24次方左右,所以,跳表大概高度在24 层左右。最坏情况下,这 24 层数据会分散在不同的数据页里,也即是查一次数据会经历24 次磁盘 IO

因此存放同样量级的数据,B+树的高度比跳表的要少,如果放在 mysql 数据库上来说,就是磁盘 IO 次数更少,因此 B+树查询更快

而针对写操作,B+树需要拆分合并索引数据页,跳表则独立插入,并根据随机函数确定层数,没有旋转和维持平衡的开销,因此跳表的写入性能会比 B+树要好。

其实,mysql 的存储引擎是可以换的,以前是myisam,后来才有的innodb,它们底层索引用的都是B+树。也就是说,你完全可以造一个索引为跳表的存储引擎装到 mysql 里。事实上,facebook造了个rocksDB的存储引擎,里面就用了跳表。直接说结论,它的写入性能确实是比 innodb 要好,但读性能确实比 innodb 要差不少。感兴趣的话,可以在文章最后面的参考资料里看到他们的性能对比数据。


redis 为什么使用跳表而不使用 B+树或二叉树呢?

redis 支持多种数据结构,里面有个有序集合,也叫ZSET。内部实现就是跳表。那为什么要用跳表而不用 B+树等结构呢?

这个几乎每次面试都要被问一下。

虽然已经很熟了,但每次都要装作之前没想过,现场思考一下才知道答案。

真的,很考验演技。

大家知道,redis 是纯纯的内存数据库。

进行读写数据都是操作内存,跟磁盘没啥关系,因此也不存在磁盘 IO了,所以层高就不再是跳表的劣势了。

并且前面也提到 B+树是有一系列合并拆分操作的,换成红黑树或者其他 AVL 树的话也是各种旋转,目的也是为了保持树的平衡

而跳表插入数据时,只需要随机一下,就知道自己要不要往上加索引,根本不用考虑前后结点的感受,也就少了旋转平衡的开销

因此,redis 选了跳表,而不是 B+树。


总结

  • B+树是多叉平衡搜索树,扇出高,只需要 3 层左右就能存放 2kw 左右的数据,同样情况下跳表则需要 24 层左右,假设层高对应磁盘 IO,那么 B+树的读性能会比跳表要好,因此 mysql 选了 B+树做索引。
  • redis 的读写全在内存里进行操作,不涉及磁盘 IO,同时跳表实现简单,相比 B+树、AVL 树、少了旋转树结构的开销,因此 redis 使用跳表来实现 ZSET,而不是树结构。
  • 存储引擎 RocksDB 内部使用了跳表,对比使用 B+树的 innodb,虽然写性能更好,但读性能属实差了些。在读多写少的场景下,B+树依旧 YYDS。

参考资料

《MYSQL 内核:INNODB 存储引擎 卷 1》

《RocksDB 和 Innodb 引擎性能 PK 胜负难料?》

https://cloud.tencent.com/developer/article/1813695

最后


最近在看《龙蛇演义》,剧情很一般,但我硬是一口气看到了最新一集,还很上头。

为啥?

点开它,看到女主角的时候你就理解我了。

这么说吧,一个颜值出众,身材火辣的姐姐,还是个世界顶级的武术高手,穿着旗袍,踩着高跟,做着各种让牛顿棺材板都快要按不住的动作,只为手把手教会你武术基本功。

这时候,剧情还重要吗?

不得不说,当我看到姐姐穿成这样用木棍顶起 400 斤的汞球时。


我可以肯定,导演根本不懂物理。


但是!


导演很懂男人!


这不得不让我陷入沉思,到底什么才是好的内容?

难道现在有个大姐姐穿个黑丝高跟超短裙,教你变量的声明和定义这么基础的东西,你也会去看吗?

我不知道你们会不会。


反正我会。


最近原创更文的阅读量稳步下跌,思前想后,夜里辗转反侧。

我有个不成熟的请求。


离开广东好长时间了,好久没人叫我靓仔了。

大家可以在评论区里,叫我一靓仔吗?

我这么善良质朴的愿望,能被满足吗?

如果实在叫不出口的话,能帮我点下右下角的点赞和在看吗?


别说了,一起在知识的海洋里呛水吧

点击下方名片,关注公众号:【小白 debug】


不满足于在留言区说骚话?

加我,我们建了个划水吹牛皮群,在群里,你可以跟你下次跳槽可能遇到的同事或面试官聊点有意思的话题。就超!开!心!

文章推荐:


故事从好多年前说起。

想必大家也听说过数据库单表建议最大 2kw条数据这个说法。如果超过了,性能就会下降得比较厉害。

巧了。

我也听说过。

但我不接受它的建议,硬是单表装了 1 亿条数据。

这时候,我们组里新来的实习生看到了之后,天真无邪的问我:”单表不是建议最大两千万吗?为什么这个表都放了 1 个亿还不分库分表“?

我能说我是因为懒吗?我当初设计时哪里想到这表竟然能涨这么快。。。

我不能。

说了等于承认自己是开发组里的毒瘤,虽然我确实是,但我不能承认

我如坐针毡,如芒刺背,如鲠在喉。

开始了一波骚操作。

“我这么做是有道理的”

“虽然这个表很大,但你有没有发现它查询其实还是很快”

“这个 2kw 是个建议值,我们要来看下这个 2kw 是怎么来的”


数据库单表行数最大多大?

我们先看下单表行数理论最大值是多少。

建表的 SQL 是这么写的,

1
2
3
4
5
6
7
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
PRIMARY KEY (`id`),
KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=100037 DEFAULT CHARSET=utf8;

其中 id 就是主键。主键本身唯一,也就是说主键的大小可以限制表的上限。

如果主键声明为int大小,也就是 32 位,那么能支持 2^32-1,也就是21 个亿左右。

如果是bigint,那就是 2^64-1,但这个数字太大,一般还没到这个限制之前,磁盘先受不了

搞离谱点。

如果我把主键声明为 tinyint,一个字节,8 位,最大 2^8-1,也就是255

1
2
3
4
5
6
7
CREATE TABLE `user` (
`id` tinyint(2) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
PRIMARY KEY (`id`),
KEY `idx_age` (`age`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;

如果我想插入一个 id=256 的数据,那就会报错

1
2
mysql> INSERT INTO `tmp` (`id`, `name`, `age`) VALUES (256, '', 60);
ERROR 1264 (22003): Out of range value for column 'id' at row 1

也就是说,tinyint主键限制表内最多 255 条数据。

那除了主键,还有哪些因素会影响行数?


索引的结构

索引内部是用的 B+树,这个也是八股文老股了,大家估计也背得很熟了。

为了不让大家有过于强烈的审丑疲劳,今天我尝试从另外一个角度给大家讲讲这玩意。


页的结构

假设我们有这么一张 user 数据表。

user表

其中 id 是唯一主键

这看起来的一行行数据,为了方便,我们后面就叫它们record吧。

这张表看起来就跟个 excel 表格一样。excel 的数据在硬盘上是一个 xx.excel 的文件。

而上面 user 表数据,在硬盘上其实也是类似,放在了 user.ibd文件下。含义是 user 表的 innodb data 文件,专业点,又叫表空间

虽然在数据表里,它们看起来是挨在一起的。但实际上在 user.ibd 里他们被分成很多小份的数据页,每份大小 16k。

类似于下面这样。

ibd文件内部有大量的页

我们把视角聚焦一下,放到页上面。

整个页16k,不大,但 record 这么多,一页肯定放不下,所以会分开放到很多页里。并且这 16k,也不可能全用来放 record 对吧。

因为 record 们被分成好多份,放到好多页里了,为了唯一标识具体是哪一页,那就需要引入页号(其实是一个表空间的地址偏移量)。同时为了把这些数据页给关联起来,于是引入了前后指针,用于指向前后的页。这些都被加到了页头里。

页是需要读写的,16k 说小也不小,写一半电源线被拔了也是有可能发生的,所以为了保证数据页的正确性,还引入了校验码。这个被加到了页尾

那剩下的空间,才是用来放我们的 record 的。而 record 如果行数特别多的话,进入到页内时挨个遍历,效率也不太行,所以为这些数据生成了一个页目录,具体实现细节不重要。只需要知道,它可以通过二分查找的方式将查找效率**从 O(n) 变成 O(lgn)**。

页结构

从页到索引

如果想查一条 record,我们可以把表空间里每一页都捞出来,再把里面的 record 捞出来挨个判断是不是我们要找的。

行数量小的时候,这么操作也没啥问题。

行数量大了,性能就慢了,于是为了加速搜索,我们可以在每个数据页里选出主键 id 最小的 record,而且只需要它们的主键 id 和所在页的页号。组成新的 record,放入到一个新生成的一个数据页中,这个新数据页跟之前的页结构没啥区别,而且大小还是 16k。

但为了跟之前的数据页进行区分。数据页里加入了页层级(page level)的信息,从 0 开始往上算。于是页与页之间就有了上下层级的概念,就像下面这样。

两层B+树结构

突然页跟页之间看起来就像是一棵倒过来的树了。也就是我们常说的B+树索引。

最下面那一层,page level 为 0,也就是所谓的叶子结点,其余都叫非叶子结点

上面展示的是两层的树,如果数据变多了,我们还可以再通过类似的方法,再往上构建一层。就成了三层的树。

B加树结构7.drawio


那现在我们就可以通过这样一棵 B+树加速查询。举个例子。

比方说我们想要查找行数据 5。会先从顶层页的 record 们入手。record 里包含了主键 id 和页号(页地址)。看下图黄色的箭头,向左最小 id 是 1,向右最小 id 是 7。那 id=5 的数据如果存在,那必定在左边箭头。于是顺着的 record 的页地址就到了6号数据页里,再判断 id=5>4,所以肯定在右边的数据页里,于是加载105号数据页。在数据页里找到 id=5 的数据行,完成查询。

B+树查询过程

另外需要注意的是,上面的页的页号并不是连续的,它们在磁盘里也不一定是挨在一起的。

这个过程中查询了三个页,如果这三个页都在磁盘中(没有被提前加载到内存中),那么最多需要经历三次磁盘 IO 查询,它们才能被加载到内存中。


B+树承载的记录数量

从上面的结构里可以看出 B+树的最末级叶子结点里放了 record 数据。而非叶子结点里则放了用来加速查询的索引数据。

也就是说

同样一个 16k 的页,非叶子节点里每一条数据都指向一个新的页,而新的页有两种可能。

  • 如果是末级叶子节点的话,那么里面放的就是一行行 record 数据。
  • 如果是非叶子节点,那么就会循环继续指向新的数据页。

假设

  • 非叶子结点内指向其他内存页的指针数量为x
  • 叶子节点内能容纳的 record 数量为y
  • B+树的层数为z

总行数的计算方法

那这棵 B+树放的行数据总量等于 (x ^ (z-1)) * y


x 怎么算

我们回去看数据页的结构。

页结构

非叶子节点里主要放索引查询相关的数据,放的是主键和指向页号。

主键假设是bigint(8Byte),而页号在源码里叫FIL_PAGE_OFFSET(4Byte),那么非叶子节点里的一条数据是12Byte左右。

整个数据页16k, 页头页尾那部分数据全加起来大概128Byte,加上页目录毛估占1k吧。那剩下的15k除以12Byte,等于1280,也就是可以指向x=1280 页

我们常说的二叉树指的是一个结点可以发散出两个新的结点。m 叉树一个节点能指向 m 个新的结点。这个指向新节点的操作就叫扇出(fanout)

而上面的 B+树,它能指向 1280 个新的节点,恐怖如斯,可以说扇出非常高了。


y 的计算

叶子节点和非叶子节点的数据结构是一样的,所以也假设剩下15kb可以发挥。

叶子节点里放的是真正的行数据。假设一条行数据1kb,所以一页里能放y=15 行


行总数计算

回到 (x ^ (z-1)) * y 这个公式。

已知x=1280y=15

假设 B+树是两层,那z=2。则是(1280 ^ (2-1)) * 15 ≈ 2w

假设 B+树是三层,那z=3。则是(1280 ^ (3-1)) * 15 ≈ 2.5kw

这个 2.5kw,就是我们常说的单表建议最大行数 2kw 的由来。毕竟再加一层,数据就大得有点离谱了。三层数据页对应最多三次磁盘 IO,也比较合理。


行数超一亿就慢了吗?

上面假设单行数据用了 1kb,所以一个数据页能放个 15 行数据。

如果我单行数据用不了这么多,比如只用了250byte。那么单个数据页能放 60 行数据。

那同样是三层 B+树,单表支持的行数就是 (1280 ^ (3-1)) * 60 ≈ 1个亿

你看我一个亿的数据,其实也就三层 B+树,在这个 B+树里要查到某行数据,最多也是三次磁盘 IO。所以并不慢。

这就很好的解释了文章开头,为什么我单表 1 个亿,但查询性能没啥大毛病。


B 树承载的记录数量

既然都聊到这里了,我们就顺着这个话题多聊一些吧。

我们都知道,现在 mysql 的索引都是 B+树,而有一种树,跟 B+树很像,叫B 树,也叫 B-树

它跟 B+树最大的区别在于,B+树只在末级叶子结点处放数据表行数据,而 B 树则会在叶子和非叶子结点上都放。

于是,B 树的结构就类似这样

B树结构

B 树将行数据都存在非叶子节点上,假设每个数据页还是 16kb,掐头去尾每页剩 15kb,并且一条数据表行数据还是占 1kb,就算不考虑各种页指针的情况下,也只能放个 15 条数据。数据页扇出明显变少了。

计算可承载的总行数的公式也变成了一个等比数列

1
15 + 15^2 +15^3 + ... + 15^z

其中z 还是层数的意思。

为了能放2kw左右的数据,需要z>=6。也就是树需要有 6 层,查一次要访问 6 个页。假设这 6 个页并不连续,为了查询其中一条数据,最坏情况需要进行6 次磁盘 IO

而 B+树同样情况下放 2kw 数据左右,查一次最多是3 次磁盘 IO。

磁盘 IO 越多则越慢,这两者在性能上差距略大。

为此,B+树比 B 树更适合成为 mysql 的索引。


总结

  • B+树叶子和非叶子结点的数据页都是 16k,且数据结构一致,区别在于叶子节点放的是真实的行数据,而非叶子结点放的是主键和下一个页的地址。
  • B+树一般有两到三层,由于其高扇出,三层就能支持 2kw 以上的数据,且一次查询最多 1~3 次磁盘 IO,性能也还行。
  • 存储同样量级的数据,B 树比 B+树层级更高,因此磁盘 IO 也更多,所以 B+树更适合成为 mysql 索引。
  • 索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B+树层级更高,影响查询性能。
  • 单表最大值还受主键大小和磁盘大小限制。

参考资料

《MYSQL 内核:INNODB 存储引擎 卷 1》

最后


虽然我在单表里塞了 1 亿条数据,但这个操作的前提是,我很清楚这不会太影响性能。

这波解释,毫无破绽,无懈可击。

到这里,连我自己都被自己说服了。想必实习生也是。

可恶,这该死的毒瘤竟然有些”知识渊博”。


最近原创更文的阅读量稳步下跌,思前想后,夜里辗转反侧。

我有个不成熟的请求。


离开广东好长时间了,好久没人叫我靓仔了。

大家可以在评论区里,叫我一靓仔吗?

我这么善良质朴的愿望,能被满足吗?

如果实在叫不出口的话,能帮我点下右下角的点赞和在看吗?


别说了,一起在知识的海洋里呛水吧

点击下方名片,关注公众号:【小白 debug】


不满足于在留言区说骚话?

加我,我们建了个划水吹牛皮群,在群里,你可以跟你下次跳槽可能遇到的同事或面试官聊点有意思的话题。就超!开!心!

文章推荐:


我熟练应用 ctrl c 和 ctrl v 开发 curd 代码好多年了。

mysql 查询为什么会慢,关于这个问题,在实际开发经常会遇到,而面试中,也是个高频题。

遇到这种问题,我们一般也会想到是因为索引。

那除开索引之外,还有哪些因素会导致数据库查询变慢呢?

有哪些操作,可以提升 mysql 的查询能力呢?

今天这篇文章,我们就来聊聊会导致数据库查询变慢的场景有哪些,并给出原因和解决方案。


数据库查询流程

我们先来看下,一条查询语句下来,会经历哪些流程。

比如我们有一张数据库表

1
2
3
4
5
6
7
8
9
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '名字',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`gender` int(8) NOT NULL DEFAULT '0' COMMENT '性别',
PRIMARY KEY (`id`),
KEY `idx_age` (`age`),
KEY `idx_gender` (`gender`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我们平常写的应用代码(go 或 C++之类的),这时候就叫客户端了。

客户端底层会带着账号密码,尝试向 mysql 建立一条 TCP 长链接。

mysql 的连接管理模块会对这条连接进行管理。

建立连接后,客户端执行一条查询 sql 语句。 比如:

1
select * from user where gender = 1 and age = 100;

客户端会将 sql 语句通过网络连接给 mysql。

mysql 收到 sql 语句后,会在分析器中先判断下 SQL 语句有没有语法错误,比如 select,如果少打一个l,写成slect,则会报错You have an error in your SQL syntax; 。这个报错对于我这样的手残党来说可以说是很熟悉了。

接下来是优化器,在这里会根据一定的规则选择该用什么索引

之后,才是通过执行器去调用存储引擎的接口函数。

Mysql架构

阅读全文 »