MySQL学习笔记(一):一条sql查询语句是如何执行的?

August 18, 2021

先放一张MySQL架构图,有助理解,一条sql查询语句经历了从上到下的以下过程。

整体可分为两大块:Server层和存储引擎层。所有跨存储引擎层的功能都在Server层实现,如内置函数(日期、时间、数学、加密函数等)、存储过程、触发器、视图等。

连接器

作用:建立连接、检查权限、维持和管理连接

建立连接时会去查询是否有相应权限,之后的所有权限判断逻辑都依赖于此时读到的权限。这也就意味着如果在建立连接后再对用户的权限做更改,只会在重新连接后才生效。

连接建立后如果没有后续动作,该连接就处于空闲状态,可以在 show processlist 中看到它。Sleep表示当前系统中有一个空闲连接。

image-20211026100717263

如果长时间没有动作,连接就会断开。由参数wait_timeout控制,默认是8小时。

image-20211026100919509

长连接 vs 短连接

长连接和短连接是一种行为,并不是由某个参数控制。如果一个连接建立后,一直有后续行为,它就会一直维持连接状态,这就是长连接。如果建立连接做出某些动作以后就断开,它就是短连接。

长连接后续的所有操作使用的内存都是管理在该长连接的对象里,直到连接断开才会释放。所以有时候执行一些比较大的占用内存的操作后,长连接一直累积,可能会导致OOM,MySQL重启。

短连接风暴

数据库连接数突然暴涨,超过了最大连接数 max_connections(默认值151),数据库会拒绝接下来的连接请求,并提示 Too many connections

这个值并不是设的越大越好,因为建立连接的成本是很高的,需要经过三次握手、各种权限的判断等。如果设的过大,会适得其反,将大量资源消耗在建立连接上,已经拿到连接的线程反而得不到 CPU 资源。和线程池是一个道理。

可通过查看 Max_used_connections status变量,历史最高同时连接数,作为参考。[MySQL服务器最大连接数怎么设置才合理]

除此之外,可以杀掉一些占着连接不工作的线程

可通过 show processlist 查看,杀掉 Command=Sleep 的线程;或者更详细点可以查看 information_schema.innodb_trx,来选择要 kill 掉的连接。

优先断开事务外空闲太久的连接,不行的话再考虑断开事务内空闲太久的连接。

Innodb_trx 有个字段 trx_rows_modified,表示此次事务更新的行数,为0的话可放心kill,否则会造成事务回滚。

查询缓存

MySQL 8 以后将整个缓存模块删除了,不再提供查询缓存功能。

缓存顾名思义是用来提高查询速度的,但是由于缓存失效太频繁,只要对表有更新,会导致整个表的缓存失效,然后又要重新建立缓存,所以最后权衡下来把缓存模块拿掉了。

但是对于静态表,读远远多于写的表,比如配置表,可以使用缓存。

对于还提供查询缓存的版本,可以将 query_cache_type 设置为 DEMAND,即按需使用,在需要的时候加上 SQL_CAHCE 即可。

1
select SQL_CACHE * from test.person;

分析器

主要作用: 词法分析和语法分析。

词法分析:分析每个单词代表什么含义,比如 select 代表查询,t 表示表名,id 表示列名。

语法分析:分析语句是否符合sql语法规范,经常看见的 You have an error in your SQL syntax 就是在这个阶段。

优化器

主要作用:决定选择哪个索引或者join的连接顺序。

不同的索引、不同的表连接顺序会对执行效率有很大影响。

执行器

主要作用:调用存储引擎提供的接口完成操作,如 取 id=1 的数据。存储引擎是以插件的形式接入MySQL的,可以把存储引擎看作一个黑盒,它对外提供了很多接口,只需要调用即可。

mysql.slow_log 慢查询日志中有一个值: row_examined,表示 Server 层扫描的行数。注意:Server 层扫描行数和引擎内部扫描行数不一定相等。