MYSQL 5分钟掌握架构设计(一)

304 阅读7分钟

架构设计

image.png

客户端

MySQL客户端和服务器之间的通讯协议是“半双工”的,这意味着,在任何一个时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器发送数据,这两个动作不能同时发生。所以,我们无法也无须将一个消息切成小块独立来发送。

这种协议让MySQL通讯简单快速,但是也从很多地方限制了MySQL。一个明显的限制是,这意味着没法进行流量控制。一旦一端开始发送消息,另一端要接收完整整个消息才能响应它。

参数 max_allowed_packet 限制服务端接受的数据包的大小

相反的,一般服务器响应给用户的数据通常很多,由多个数据包组成。当服务器开始响应客户端请求时,客户端必须完整的接收整个返回结果。换一种方式解释这种行为:当客户端从服务器取数据时,看起来是一个拉数据的过程,但实际上是MySQL在向客户端推送数据的过程

多数连接MySQL的库函数都可以获得全部结果集并缓存到内存里,还可以逐行获取需要的数据。默认一般是获得全部结果集并缓存到内存中。MySQL通常需要等所有的数据都已经发送给客户端才能释放这条查询所占用的资源,所以接收全部结果并缓存通常可以减少服务器的压力,让查询能够早点接收、早点释放相应的资源。

查询状态

select * from information_schema.processlist;
SHOW FULL PROCESSLIST

状态说明
Sleep线程正在等待客户端发送新的请求
Query线程正在执行查询或者正在将结果发送客户端
Locked在MySQL服务器层,该线程正在等待表锁
Analyzing and statistics线程正在收集存储引擎的统计信息,并生成查询的执行计划
Copying to tmp table [on disk]线程正在执行查询,并且将结果集复制到一个临时表,可能是GROUP BY,UNION
Sorting result线程正在对结果集进行排序
Sending data线程可能在多个状态之间传送数据,或者在生成结果集,或者在向客户端返回数据

服务端

查询缓存

在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存中的数据。

如果当前的查询命中了查询缓存,在返回查询结果之前MySQL会检查一次用户权限。如果权限没有问题,MySQL会跳过所有其它阶段,直接从缓存中拿到结果并返回给客户端。

解析器

MySQL通过关键字将SQL语句进行解析,并生成一棵对应的“解析树”。MySQL解析器将使用MySQL语法规则验证和解析查询。例如,它将验证是否使用错误的关键字,或者使用关键字的顺序是否正确等,再或者它还会验证引号是否能前后正确匹配。

预处理器则根据一些MySQL规则进一步检查解析树是否合法,例如,这里将检查数据表和数据列是否存在,还会解析名字和别名,看看他们是否歧义。

下一步预处理器会验证权限,这通常很快。

优化器

现在语法树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询语句可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。

MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。

下面是一些MySQL能够处理的优化类型:

  • 重新定义关联表的顺序

    数据表的关联并不总是按照在查询中指定的顺序进行,决定关联的顺序是优化器很重要的一部分功能。

  • 将外连接转化成内连接

    并不是所有的 OUTER JOIN语句都必须以外连接的方式执行。例如WHERE条件、库表结构都可能会让外连接等价于一个内连接。MySQL能够识别这点并重写查询,让其可以调整关联顺序。

    外连接分为两种,左外连接(left join)和右外连接(right join)。
    外连接有主表和从表之分:1.左连接,坐表为主表 2.右连接,右表为主表。
    外连接是将主表的记录去匹配从表的记录,匹配成功保留,匹配失败也保留,只是从表字段置空(外连接会保留一条记录)。

    内连接是将一张表的每一条记录去另外一张表根据条件匹配,匹配成功保留,匹配失败都不保留。内连接可以没有on条件,得到的结果就是交叉连接(笛卡尔积),无意义。

  • 使用等价变换规则

    它可以合并和减少一些比较,还可以移除一些恒成立和一些恒不成立的判断。例如,(=5 and a>5)将被改写成a>5。类似的,如果有(a<b AND b=c) AND a=5则会改写成b>5 AND b=c AND a=5

  • 优化COUNT()、MIN()和MAX()

    例如,要找到某一列的最小值,只需要查询对应的 B-Tree 索引最左端的记录,MySQL可以直接获取索引的第一行记录。在优化器生成执行计划的时候可以利用这一点,在 B-Tree 索引中,优化器会将这个表达式作为一个常数对待。

  • 预估并转化成常数表达式

    当MySQL检测到一个表达式可以转化成常数的时候,就会一直把该表达式作为常数进行优化处理。 如: image.png MySQL分两步来执行这个查询,也就是上面的执行计划的两行输出。第一步先从 film 表中找到需要的行。因为在 film_id 字段上有主键索引,所以MySQL优化器知道这只会返回一行数据,优化器在生成执行计划的时候,就已经通过索引信息知道将返回多少行数据。因为优化器已经明确知道有多少个值(WHERE条件中的值)需要做索引查询,所以这里的表访问类型是 const

    在执行计划的第二步,MySQL将第一步中返回的 film_id 列作为一个已知取值的列来处理。因为优化器清楚在第一步执行完成后,该值就会是明确的了。注意到正如第一步中一样,使用 film_actor 字段对表的访问类型也是 const

  • 覆盖索引扫描

    当索引中的列包含所有查询中需要使用的列的时候,MySQL就可以使用索引返回需要的数据,而无需查询对应的数据行。

  • 子查询优化

    MySQL在某些情况下可以将子查询转换一种效率更高的形式,从而减少多个查询多次对数据进行访问。

  • 提前终止查询

    在发现已经满足查询需求的时候,MySQL总是能够立刻终止查询。 image.png 从这个例子看到查询在优化阶段就已经终止。

  • 等值传播

    如果两个列的值通过等式关联,那么MySQL能够把其中一个列的WHERE条件传递到另一列上。 image.png 因为这里使用了 film_id 字段进行等值关联,MySQL知道这里的WHERE子句不仅适用于 film 表,而且对于 film_actor 表同样适用。

  • 列表 IN() 的比较

    MySQL将IN()列表中的数据先进行排序,然后通过二分查找的方式来确定列表中的值是否满足条件,这是一个 O(log n) 复杂度的操作。