mysql是最广泛使用的开源数据库之一,作为后端开发人员,或多或少都会和mysql打交道,本篇文章会从sql查询语句的执行过程,来介绍mysql的服务器架构,
查询的过程大致分为从客户端到服务器,在服务器上解析,生成执行计划,执行,并返回结果给客户端。如下图1.1所示。
图1.1 sql查询执行路径
1.1 连接器
连接器对客户端的连接请求进行用户名和密码的验证,并会管理连接池。客户端和服务器为“半双工”通信协议,客户端和服务器无法同时发送消息,当服务器向客户端发送查询结果时,必须全部返回,客户端不能主动停止,mysql需要等所有的数据都发送给了客户端才会释放本次查询所占用的资源,因此大多数库函数都会接收结果并缓存在客户端,以使查询早点结束,减轻服务端压力。
对于连接到mysql的客户端,可以用show full processlist查看连接状态,该状态表示了mysql当前在做什么,一个查询完整的生命周期,状态会变很多次。
在show full processlist中,字段command表示线程正在执行的命令类型,The type of command the thread is executing,一般就是休眠(sleep),查询(query),连接 (connect),含义如下:
- sleep,线程正在等待客户端发送新的请求。
- query,线程正在执行查询或将结果返回客户端。
- connect,正在建立连接。
show full processlist最重要的字段是state,Most states correspond to very quick operations. If a thread stays in a given state for many seconds,there might be a problem that needs to be investigated.An action, event, or state that indicates what the thread is doing,表示线程处于某一状态下具体正在做什么,值的含义如下:
- Locked,在Mysql服务器层,线程正在等待锁,这里不包括innodb存储引擎实现的行锁
- Analyzing and statistics,线程正在收集存储引擎的统计信息,并生成查询的执行计划。
- Copying to tmp table [on disk],线程正在执行查询,并将结果放在临时表,带有on disk,表示mysql在将内存临时表存入磁盘。
- Sorting result,线程正在对结果集排序。
- Sending data,线程正在多个状态间传送数据,或者在生成结果集,或者向客户端返回数据。
1.2 查询缓存
在解析sql语句之前,mysql会从查询缓存中获取数据,具体是一个大小写敏感的哈希查找,sql语句必须和缓存中的sql语句完全匹配,否则不会命中,对于更新比较频繁的数据库,经常会导致缓存失效,建议将查询缓存关闭。
1.3 分析器
假如查询缓存没有命中,则会进入分析器,对sql语句进行词法和语法分析,并会进行预处理,以便知道此sql语句是要做什么。它会验证是否使用错误的关键字,关键字顺序是否正确等。并且,预处理会根据一些规则对解析树的合法性进行进一步校验,比如检查数据表和列是否存在,解析名字和别名,看看是否有歧义等,如下图1.2经常遇到的语法错误就在这一阶段
图1.2 sql语法错误提示
1.4 优化器
sql语句经过分析器分析合法,需要经过查询优化器转化成执行计划,查询优化器解决的是怎么做的问题,相同的sql语句,返回相同的结果集,有很多种执行方式,查询优化器就是要选择性能较好的一种。
mysql是基于成本的优化器,会预测一个查询使用某种计划时的成本,并选择其中成本最小的一种,可以通过Last_query_cost值查询到当前会话查询的成本。很多原因会导致mysql优化器选择错误的执行计划,比如:统计信息不准、不考虑其他并发执行的查询,执行计划太多,无法估算所有的计划等。
1.5 查询执行引擎
查询执行引擎根据优化器生成的执行计划(执行计划是一种数据结构,而不是字节码),调用存储引擎接口,完成执行计划中的所有操作。mysql将查询结果返回客户端是一个增量、逐步返回的过程。开始生成第一条结果,mysql就可以开始向客户端返回数据。这样做服务端无需存储太多结果,减小了服务端内存的消耗压力,另外,客户端也可以第一时间获得返回的结果。