MySQL中的查询语句执行分析
select * from T where id =1;
基本架构
示意图
MySQL大致可以分为Server层和存储引擎层
Server层包括:连接器、查询缓存、分析器、优化器、执行器等,Server层是通用的,可以配合不同的存储引擎使用。
存储引擎层负责数据的存储和提取,其支持:InnoDB(默认的存储引擎)、MyISAM、Memory等多个存储引擎。
连接器
负责客户端和服务端的连接,验证用户名和密码,获取拥有的权限信息,如果在获取连接之后做出权限变更,对于未关闭的连接,是感知不到权限的变化的。
连接关闭时间由参数wait_timeout
控制,默认值是8小时。
连接分为短连接和长连接,区别在于,长连接存活时间比较长,可以供更多的查询使用,而短连接经过几次查询就会断开了,下次查询需要重新获取连接。
由于建立连接的过程比较耗时,建议使用长连接,但是过多的使用长连接会导致内存占用会变大。两个解决问题的办法:
- 定期断开长连接释放内存资源.
- 执行mysql_reset_connection来重新初始化连接资源,使其恢复到刚刚创建完连接的状态,且不需要重新连接和权限验证等。
查询缓存
MySQL在拿到一个查询语句后,首先会查询缓存中是否存在之前执行过的同样的sql,如果命中则直接返回缓存中过的查询结果,如果没有命中再继续后面的过程,在返回给客户端之前,MySQL会对当前查询结果进行一个缓存,key就是sql语句,value就是查询结果。
由于缓存的失效非常频繁,只要有表执行了更新语句,那么这个表的缓存就会立马失效,从而造成了,维护缓存成本极高维护的东西,却很少能被使用到。缓存只适合使用在静态表中,其维护频率极低。
可以通过设置参数:query_cache_type
设置未:DEMAND,这样就不会使用缓存功能了,如果要使用缓存则可以在SQL中显示指定 SQL_CACHE,例如:select SQL_CACHE * from T where id=10;
Mysql8开始,缓存功能被完全移除了。
分析器
如果缓存命中失败,下一步就是‘词法分析’, 分析执行语句的正确性,表明字段名是否存在,经常报错类似: ‘use near’…
优化器
在经过分析器之后,需要对sql语句本身进行优化,例如:在包含多个索引时使用哪个索引,关联表多个时,决定各个表的连接顺序
执行器
经过优化器,就要开始真正的执行查询语句了,在这里首先还是先判断当前登录用户是否有该表的相应才做权限,如果缓存命中,在返回缓存结果之前,也会进行权限验证。
执行器会根据表的引擎定义,使用相应的引擎提供的接口执行才做。如果查询中没有用到索引,查询引擎就会从第一行开始逐条取出记录,如果满足查询条件就放在结果集中,如果不满足则跳过,直到取完最后一行,查询结束,然后讲结果集返回到客户端,结束查询语句。
对于包含索引的查询,执行逻辑类似,第一次调用的是取满足条件的第一行这个接口’之后循环满足条件的下一行这个接口,这些接口都是引擎提供好的。
在慢查询中的row_examined
表示这个语句执行过程中扫描了多少行,就是在执行器调用引擎获取数据行的时候累加的结果,这个和引擎扫描的行数并不是完全相同的,有时候可能一次调用引擎,引擎内部扫描了多行数据。
更新语句的执行分析
其大致流程和查询语句的执行过程类似,不同之处在于,跟新语句在建立连接之后会清空即将更新到的表的缓存,其还包括两个日志模块,redo log(重做日志),binlong(归档日志).
redo log
MySQL中常用的WAL(Write-Ahead Logging), 更新过程会先写日志,在写磁盘。
当有一条更新语句的时候,InnoDB引擎会先把记录写在redo log里面,并更新内存,这个时候更新就算完成了,InnoDB会在比较系统比较空闲的时候,将日志中的操作记录更新到磁盘中区。
由于InnoDB中的redo log大小是固定的,如果redo log写满了,就会先处理一部分的redo log到磁盘中去,然后将处理完成的redo log清楚,这样就可以继续在redo log中记录操作日志了。Mysql之所以有crash-safe能力,就是依赖redo log.
binlog
redo log是InnoDB特有的日志,而bin log是Mysql Server层的日志。
crash-safe是没有crash-sage能力的。
两种日志比较:
- redo log: InnoDB特有, binlog是MySQL server层实现的,所有引擎都可以使用。
- redo log是物理日志,记录的是 ‘在某个数据页上做了什么修改”; binlog 是逻辑日志,记录的这个语句的原始逻辑,比如: ‘给id=xx的这一条记录的某个字段b执行一个xx操作‘
- redo log是循环写入的,空间用完就会开始执行擦除动作,而binlog是可以追加写入的,不会删除和被覆盖。
更新语句执行流程:
- 执行器先通过引擎查找到要修改的数据记录,优先在内存中查找,如果找到直接返回,如果没找到就从磁盘中找,找到后先读入到内存中,然后在返回给执行器。
- 然后对引擎返回的数据执行更新操作,然后再调用引擎把更新后的数据写入到内存中。
- 引擎拿到更新后的数据,首先更新到内存中区, 然后把操作记录记录到redo log中,此时redo log 处于prepare状态,等待事务提交。
- 执行器生成binlog,并把binlog 写入磁盘。
- 执行器调用引擎的提交事务接口, 然后引擎把redo log的状态更新为:commit状态,完成更新过程。
示意图
两阶段提交
redo log的两阶段:
- prepare
- commit
为了保证redo log和binlog的逻辑一致,所以才有了’两阶段提交‘。
如果在记录两次日志中间发生了crash。会导致通过日志恢复出来的库的状态不一致,而且这不仅仅是用在数据恢复中,还会用在系统扩容,如果日志有问题就会导致库的集群中出现主从不一致的现象。
两个重要参数:
innodb_flush_log_at_trx_commit
为1 ,表示每次事务的redo log都直接持久化到磁盘。保证日志的完整性。sync_binlog
为1,表示每次事务的binlog都持久化到磁盘,保证日志的完整性。
来源:CSDN
作者:leejiliang
链接:https://blog.csdn.net/leejiliang/article/details/104570196