逻辑架构
如果能在头脑中构建出一幅MySQL各组件之间如何协同工作的架构图,就会有助于深入理解MySQL服务器。下图展示了MySQL的逻辑架构图。
大体来说,MySQL可以分为Server层和存储引擎层两部分。
Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(例如:日期、时间、数学和加密函数),所有跨存储引擎的功能都在这一层实现:等。
:一个数据库里可以存在不同存储引擎建立的表。
而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最长用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始作为MySQL的默认存储引擎,之前版本的默认存储引擎为MyISAM。
从图中不难看出,不同的存储引擎共用一个Server层,也就是从连接器到执行器的部分。下面我们依次来介绍下各个组件。
连接器
连接器负责跟客户端建立连接、获取权限、维持和管理连接。
在客户端程序发起连接时,需要携带主机信息、用户名、密码等信息,连接器会对客户端提供的信息进行验证。如果验证失败,服务端就会拒绝连接;验证成功,连接器会到权限表里面查出你拥有的权限,并里以供后续的查询缓存、分析器、执行器使用。
每个客户端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在这个单独线程中执行。当客户端与服务器断开连接时,服务器并不会立即把该线程销毁,而是会把线程缓存起来,以备后用,因此不需要为每一个新建的连接创建或者销毁线程。但如果缓存的线程太多,就会影响性能,服务端通过参数max_connections
来限制最大允许的连接数量从而限制线程数,该参数默认值为151。
mysql> select @@max_connections;
+-------------------+
| @@max_connections |
+-------------------+
| 151 |
+-------------------+
1 row in set (0.00 sec)
连接完成后,如果没有后续动作,这个连接就处于空闲状态,你可以通过show processlist
命令看到所有的连接。其中,Comman
列会显示连接的当前状态,Sleep
表示该连接正处于空闲状态。
mysql> show processlist;
+----+------+------------------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+------------------+------+---------+------+----------+------------------+
| 4 | root | 172.17.0.1:55024 | NULL | Query | 0 | starting | show processlist |
| 5 | root | 172.17.0.1:55026 | NULL | Sleep | 4 | | NULL |
+----+------+------------------+------+---------+------+----------+------------------+
2 rows in set (0.00 sec)
如果客户端长时间没有动静,连接器就会自动断开连接。MySQL提供了参数wait_timeout
来控制最大空闲时间,默认值为8小时(28800秒)。
mysql> select @@wait_timeout;
+----------------+
| @@wait_timeout |
+----------------+
| 28800 |
+----------------+
1 row in set (0.00 sec)
数据库里面,连接有长、短连接之分。长连接是指客户端连接成功后,一直使用同一个连接来发送请求;短连接是指每次完成一个或几个很少的请求后就断开连接,下次请求再重新建立一个连接。
由于建立连接的过程比较复杂,也比较耗时,所以一般的应用建立连接时都是用的长连接(连接池)。但长连接也有自己的问题,由于MySQL执行过程中临时使用的内存是管理在连接对象里的,这些资源会在连接断开的时候释放。如果连接一直不断开,可能会导致内存占用过大,产生OOM,被系统强行杀掉。
要解决这个问题,考虑以下两种方案:
- 定期断开连接。使用一段时间或完成一定的请求数量,或执行过一个占用内存的大查询之后,断开重连。
- 如果是MySQL 5.7或更新版本,可以通过执行
mysql_reset_connection
来重新初始化连接。这个过程不需要重连和重新做权限验证,但是会把连接恢复到刚刚创建完时的状态。
查询缓存
连接成功后,在检查查询缓存之前,MySQL只做一件事情,就是通过一个大小写不敏感的检查看看SQL语句是不是以SEL开头。
检查未通过,不会去查询缓存查找,直接走后续分析流程。
检查通过,会先到查询缓存看看,之前是不是执行过这条语句。如果查找到了,则直接把缓存结果返回给客户端,不用再去底层的表查找了,效率比较高。如果没找到,执行正常的查询流程,并把查询结果保存到查询缓存中。查询语句及其结果是以key-value对的形式,缓存在内存中的,key是查询语句,value是查询结果。
:如果命中查询缓存,会在查询缓存返回结果时,做权限验证。
查询缓存可以在不同的客户端之间,也就是说,假如客户端A刚刚发送了一个查询请求,而客户端B之后也发送了同样的查询请求,那么客户端B的这次查询就可以直接使用查询缓存中的数据了。
虽然查询缓存有时候会提高性能,但整体说弊大于利,主要基于以下几点分析:
1、查询语句及其结果是以key-value对的形式保存的,如果两个查询请求有任何字符上的不同(例如:空格,注释等),都会导致缓存不命中;
2、如果查询中包含某些系统函数、用户自定义变量和函数、系统表,则这个请求就不会被缓存。以NOW()或CURRENT_DATE()函数为例,每次调用产生的结果都不一致,所以不能被缓存;
3、查询缓存失效非常频繁。只要该表的结构或数据被修改,比如对该表使用了INSERT、UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE或DROP DATABASE语句,则与该表有关的所有查询缓存都将变为无效并从查询缓存中删除。
:查询缓存适用于不经常修改的表,比如配置表。
从MySQL 5.7.20开始,不推荐使用查询缓存,在MySQL 8.0中直接将其删除了。
分析器
如果查询缓存没有命中,接下来就要进入正式的执行阶段了。客户端发送过来的请求本质上就是一段文本,服务端需要对文本进行解析。解析过程涉及“词法分析”、“语法分析”两个阶段。
分析器先做“词法分析”,从文本中将要查询的表,字段、各种查询条件都提取出来。
然后做“语法分析”,判断请求的SQL语句是否满足MySQL语法。
:分析器会在优化器之前调动precheck验证权限。
优化器
经过了分析器,MySQL就知道要做什么了。但是,因为我们写的SQL语句执行起来效率可能不高,MySQL的优化器会对我们的语句做一些优化,如索引的选取、多表关联(join)时各表的连接顺序,外连接转换为内连接、表达式简化、子查询转为连接等一堆东西。优化的的结果就是生成一个执行计划。这个执行计划表明了应该使用哪些索引,表之间的连接顺序是啥等等,我们可以使用EXPLAIN来查看语句的执行计划。
执行器
经过优化器优化后,就要按照生成的执行计划开始执行了。开始执行的时候,要先判断一下有没有执行的权限。
:SQL执行过程中可能会有触发器这种在运行时才能确定的过程,分析器工作结束后的precheck是不能对这种运行时涉及到的表进行权限校验的,所以需要在执行器阶段再次进行权限检查。
如果没有,就会返回没有权限的错误。
如果有权限,就会按照执行计划调用底层存储引擎提供的接口获取到数据后返回给客户端。
不过需要注意的是,Server层和存储引擎层交互时,一般是以记录为单位的。以SELECT语句为例,Server层根据执行计划先向存储引擎层取一条数据,然后判断是否符合WHERE条件;如果符合,就发送给客户端,否则就跳过该记录,然后继续向存储引擎索要下一条记录;依次类推。
:Server层在判断某条记录符合要求后,其实是先将其发送到一个缓冲区,等到该缓冲区满了,才真正向客户端发送记录。该缓冲区大小是由系统变量
net_buffer_length
控制的,默认为16K。
mysql> select @@net_buffer_length;
+---------------------+
| @@net_buffer_length |
+---------------------+
| 16384 |
+---------------------+
1 row in set (0.00 sec)
常用存储引擎
存储引擎 | 描述 |
---|---|
InnoDB | 支持事务、外键、行级锁 |
MyISAM | 不支持事务、表级锁 |
MEMORY | 数据只保存才内存,不同步到磁盘 |
ARCHIVE | 用于数据存档(记录插入后不能再修改) |
NDB | MySQL集群专用存储引擎 |
总结
1、MySQL基本架构包括Server层和存储引擎层,Server层又包括多个组件--连接器、查询缓存、分析器、优化器、执行器;
2、在连接器、分析器、执行器里都涉及权限验证。
3、涉及三个系统变量max_connections
、wait_timeout
、net_buffer_length
。