读语句:
对于读语句来说会经过Server服务层及存储引擎层
Server层会依次访问连接器,缓存器,分析器,优化器,执行器
ini
代码解读
复制代码
连接器 -> 用于校验请求源的身份标识及权限,你的连接方式可以是命令行、图形界面、不同编程语言所使用的MySQL连接器
# 比如你遇到的1045 - Access denied for user就是由连接器在身份校验失败后抛出的异常
# 当程序连接到MySQL后太长时间没有执行后续动作会被判定为Sleep状态(show processlist -> Command)连接器就会将连接客户端关闭(这个时间由wait_timeout参数决定),此时在次执行语句则会抛出异常(Lost connection to MySQL server during query)
# 当连接成功后连接器会从权限表中查询当前连接用户所拥有的权限,如果在连接过程中被授予了新的权限并不会当即更新
# 因为MySQL在执行过程中临时使用的内存是管理在连接对象里面的,所以在有多个长连接线程时服务器的内存占用会很高,此时最好可以定时断开后重连
缓存器 -> 用于缓存查询结果,下次执行SQL语句如果命中缓存则直接返回查询结果(MySQL8中已经废除了这个功能)
# 当MySQL在内存缓存(你可以把它们理解key-value)中获取到SELECT语句执行结果则会直接返回结果给客户端而不在执行后续过程
# 你可以使用 show variables like '%query_cache%' -> have_query_cache 查看是否开启了查询缓存
# 如需开启查询缓存可在配置文件中[mysqld]下新增query_cache_size = 20M,query_cache_type = on即可
# 除非是静态表很长时间不会更新数据,不然不建议使用缓存,因为只要是一旦这个表中内容被更新缓存也会随机失效,弊大于利
分析器 -> 用于SQL语句及词法分析
# 比如你遇到的ERROR 1064 (42000) - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near xxxxx就是由分析器在校验语句失败后抛出的异常
# 你也可以把分析器理解为一个路由调度器,不同的语句由分析器调度给下游
# 当然,分析器还会抛出其他异常,如
# ERROR 1054 (42S22): Unknown column '字段' in 'field list' 字段不存在
# ERROR 1305 (42000): FUNCTION [database].[function] does not exist 函数不存在
优化器 -> 用于生成执行计划,选择索引(这部分只介绍读关于读语句的优化)
# 优化器会在表中有多个索引(包括主键,单列,组合索引)时决定使用哪个索引,或者语句中出现多个表关联时决定各个表的连接顺序
执行器 -> 用于操作存储引擎获取最终查询结果
# 在经过分析器知道要干什么及优化器知道要怎么干后执行器首先会判断这个连接用户对这些要查询的表有没有操作权限,如不存在会抛出异常(ERROR 1142 (42000): SELECT command denied to user 'xxx'@'xxx' for table 'xxx')
# 以"select id, file1, file2, file3 from table where id = 100"语句为例,执行器的操作流程是这样的
# 调用存储引擎的接口取这个表的第一行
# 判断id如果是100则将这行加入结果集,不是则跳过
# 重复这个逻辑直到最后一行(你可以在explain -> rows中看到执行器重复了多少次)
Server涵盖了MySQL大多数核心服务,如内置函数、存储过程、触发器、视图等
存储引擎层则取决于数据表在创建时选择的存储引擎
写语句:
注意:写语句的解释以InnoDB引擎为背景
对于写语句相对于读语句会稍微复杂,首先要知道MySQL的三个重要日志模块redo log(重做日志)、undo log(撤消日志)、binlog(归档日志),
redo log - 重做日志(redo log能够保证MySQL在任何时间段突然奔溃,重启后以前提交的记录都不会丢失,也就是crash-safe功能)
perl
代码解读
复制代码
(1):InnoDB是Innobase Oy公司所开发在以插件形式加入MySQL,InnoDB使用了redo log 来实现crash-safe能力,redo log是InnoDB引擎特有的
(2):redo log记录的是结果,某个数据页某条记录做了什么修改,记录修改结果
(3):redo log是循环写的,空间固定会用完,用完就刷盘再清空
undo log - 撤销日志
bash
代码解读
复制代码
(1):undo log用于回滚事务,直接从undo log中取到原始值
(2):undo log根据不同的隔离级别获取到的数据是不同的
binlog - 归档日志
arduino
代码解读
复制代码
(1):binlog是MySQL的Server层实现的,所有引擎都可以使用
(2):binlog并不具备crash-safe功能
(3):binlog记录的是原始逻辑,也就是修改的过程,所以binlog只能用于归档
(4):binlog是追加写入的,文件写到一定大小后会切换到下一个,不会覆盖之前的日志
然后,我们在来看MySQL写语句的执行顺序,与读语句相同的是写语句也会依次经过连接器、分析器、优化器、执行器,
不同点在于执行器层,以一条update语句为例
perl
代码解读
复制代码
(1):执行器首先会调用引擎在Buffer Pool中读取数据,
|
(2):如果Buffer Pool中没有找到相关的数据则在磁盘中读取当前语句所需数据
|
(3):在undo log中写入更新前的旧值以便回滚数据
|
(4):更新Buffer Pool中的值
|
(5):将新的数据写入redo log Buffer
|
(6):准备提交事务,出于prepare准备阶段,将修改写入redo log磁盘文件中
|
(7):准备提交事务,处于prepare准备阶段,将修改写入binlog磁盘文件
|
(8):提交事务,写入commit标记到redo log中,事务状态有prepare准备阶段修改为commit提交阶段
|
(9):后台线程读取Buffer Pool中的数据,定时将数据写入磁盘