select * from table where id = 1;
简单的分,MySQL 可以分为服务层和引擎层两部分。
服务层又包含,连接器、查询缓存、分析器、优化器、执行器等部分。
引擎层则真正的负责数据的存储和提取,支持 InnoDB、MyISAM、Memory 等引擎。
连接器
建立连接,权限验证。
- 登录:
mysql -h127.0.0.1 -P3306 -uroot -proot。 - 验证账户,登录失败会返回
Access denied for user的错误。 - 获取权限,在链接断开前,哪怕修改了权限也会按照建立连接时的权限执行(修改后的权限仅对新链接生效)。
- 建立连接后不做任何操作,连接就处于空闲状态,可以用
show processlist;查看 Command 列的状态来查看连接状态。 - 客户端如果长时间没有操作,连接器会自动断开。超时参数由
wait_timeout控制,默认值是8小时。 - 数据库连接分长连接和短连接,长连接:多次查询用同一个连接。短连接:每次执行完很少的查询就断开连接。建立连接是比较麻烦的过程,尽量使用长连接。
- 但是长连接使用会有一些问题,MySQL 在执行过程中使用的内存被管理在连接对象里面。过多的长连接会导致内存占用过高,甚至 OOM。解决方案:
- 定期断开重连。
- MySQL 5.7 及以上版本,可以执行
mysql_reset_connection来重新初始化连接资源。
查询缓存
MySQL 接收到一个查询请求后会先去检查缓存,是否之前被执行过,执行过的语句与结果会以 key-value 的结构缓存在内存中,key 是查询的语句,value 是查询的结果。如果缓存命中,则直接返回结果。如果缓存没有命中,则正常执行操作,执行完毕后结果会被缓存,方便下次查询。
但是对于写入远大于查询的表不建议使用查询缓存,缓存失效是非常频繁的事情,只要数据有更新,缓存就会被清空(数据一致性)。可以将参数query_cache_type设置为DEMAND,这样查询语句就默认不使用缓存,但可以用SQL_CACHE显示的指定某语句启用缓存,例如:select SQL_CACHE * from table where id = 1;
注意:MySQL 8.0 后查询缓存模块整个被删掉了。
分析器
如果一个查询没有命中缓存,则开始真正的执行过程,MySQL 会对 SQL 语句做解析。
- 分析器首先做词法分析,识别 SQL 语句中的关键词。
- 之后分析器会做语法分析,根据上一步的解析到的关键词,语法分析器会判断这个 SQL 语句是否满足语法要求。语法不对则会报
You have an error in your SQL syntax的错误。 - 同时,对于表是否存在,列是否存在也在这一步做检查。
优化器
经过了分析器,MySQL 已经知道 SQL 语句要干嘛了,在执行语句之前还要经过优化器的处理。优化器的作用是决定查询的时候用哪些索引,连表查询的时候各个表的连接顺序,决定了语句的执行方案。
执行器
最后执行语句的地方。
在执行前会先判断一下有没有表的查询权限,如果没有会返回没有权限的错误,(如果命中缓存,会在缓存返回前做权限校验)。如果有权限,则打开表根据表的引擎定义去使用对应引擎提供的接口。
分析select * from table where id = 1;最终的引擎执行流程:
- 调用 InnoDB 的接口获取表的第一行,判断 id 值是不是1,是则将这行存到结果集,否则跳过。
- 调用引擎接口获取下一行,重复判断逻辑直到最后一行。
- 执行器将最终结果集返回给客户端。
以上 SQL 执行过程中获取过的行数总和就是该条 SQL 执行时的扫描行数rows_examined
有索引的表,执行逻辑也差不多。
