一条SQL是怎么执行的

简介: 本文介绍了MySQL中读写语句的处理流程。读语句涉及Server服务层和存储引擎层,其中Server层包括连接器、缓存器、分析器、优化器和执行器;写语句则更复杂,涉及redo log、undo log和binlog三大日志模块,以InnoDB引擎为例详细解析了写操作的具体步骤。

读语句:

对于读语句来说会经过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中的数据,定时将数据写入磁盘


转载来源:https://juejin.cn/post/7164308461897908232

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
5月前
|
人工智能 Java 数据库连接
Mybatis执行流程
本文详细分析了 MyBatis 的执行流程,介绍了其核心组件如 SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession 的作用与实现原理,并通过源码解析了 SQL 语句的执行过程,包括动态代理、缓存机制及数据库查询的实现,帮助读者深入理解 MyBatis 的内部工作机制。
169 0
Mybatis执行流程
|
5月前
|
人工智能 缓存 安全
Java中的反射机制:深入探索与应用
Java反射机制是程序运行时动态获取类信息并操作类成员的特性,具备高度灵活性,但也伴随性能与安全风险。本文详解反射的基本用法、高级应用及最佳实践,助你掌握这一强大工具的正确使用方式。
164 0
|
7月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
310 0
|
5月前
|
存储 人工智能 安全
深入理解 go sync.Map - 基本原理
本文介绍了 Go 语言中 `map` 在并发使用时的常见问题及其解决方案,重点对比了 `sync.Mutex`、`sync.RWMutex` 和 `sync.Map` 的性能差异及适用场景。文章指出,普通 `map` 不支持并发读写,容易引发错误;而 `sync.Map` 通过原子操作和优化设计,在某些场景下能显著提升性能。同时详细讲解了 `sync.Map` 的基本用法及其适合的应用环境,如读多写少或不同 goroutine 操作不同键的场景。
232 1
|
5月前
|
存储 人工智能 缓存
SpringBoot离线应用的5种实现方式
在网络依赖日益加深的今天,离线应用的重要性不断上升。本文介绍了基于SpringBoot实现离线应用的五种方式,重点讲解了嵌入式数据库的实现原理与步骤,包括本地数据存储、操作缓存、资源本地化和状态管理等核心功能,分析了其优缺点及适用场景,帮助开发者在无网络环境下构建稳定可靠的应用。
289 0
|
6月前
|
设计模式 Oracle Java
java静态方法和实例方法有何不同
本文深入探讨Java中静态方法与实例方法的区别与应用,从概念、调用方式、内存管理到生命周期全面解析两者差异,并结合实际案例分析设计意图与最佳实践。无论是工具类设计还是性能优化,文章均提供详尽指导,帮助开发者根据具体场景选择合适的方法类型,提升代码效率与可维护性。
200 3
|
5月前
|
SQL 人工智能 Rust
Java 开发中Stream的toMap与Map 使用技巧
本文深入解析了 Java 中 `toMap()` 方法的三大问题:重复键抛出异常、`null` 值带来的风险以及并行流中的性能陷阱,并提供了多种替代方案,如使用 `groupingBy`、`toConcurrentMap` 及自定义收集器,帮助开发者更安全高效地进行数据处理。
259 0
|
7月前
|
人工智能 Shell 开发者
Python项目管理工具 PDM
PDM(Python Development Master)是一款现代化的Python包管理工具,基于PEP 582标准,无需虚拟环境即可实现依赖隔离。它支持PEP 621声明项目元数据,告别`setup.py`,并具备快速安装、简洁依赖管理和内置脚本系统等优势。通过简单命令如`pdm init`、`pdm add`和`pdm run`,用户可轻松完成项目初始化、依赖管理和运行。适合希望简化依赖管理、追求现代工具体验的开发者,尤其对传统工具如`pipenv`或`poetry`不满意的用户。
352 1
|
7月前
|
人工智能 数据库连接 API
掌握Python的高级用法:技巧、技术和实用性示例
本文分享了Python的高级用法,包括生成器、装饰器、上下文管理器、元类和并发编程等。生成器通过`yield`实现懒加载序列;装饰器用于增强函数功能,如添加日志或性能分析;上下文管理器借助`with`语句管理资源;元类动态定制类行为;并发编程利用`threading`和`asyncio`库提升任务执行效率。掌握这些高级概念可优化代码质量,解决复杂问题,提高程序性能与可维护性。
159 6
|
10月前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
263 3
Go 语言入门指南:切片

热门文章

最新文章