巅峰战队|详解一条查询select语句和更新update语句的执行流程( 四 )


返回结果最后 , 将查询出得到的结果返回Server层 , 如果开启了缓存 , Server层返回数据的同时还会写入缓存 。
MySQL将查询结果返回是一个增量的逐步返回过程 。 例如:当我们处理完所有查询逻辑并开始执行查询并且生成第一条结果数据的时候 , MySQL就可以开始逐步的向客户端传输数据了 。 这么做的好处是服务端无需存储太多结果 , 从而减少内存消耗(这个操作可以通过sql _buffer_result来提示优化器 , 和上文说的force index , straight_join一样都是人为强制优化器执行我们想要的操作) 。
一条update语句的执行流程一条更新语句 , 其实是增 , 删 , 查的综合体 , 查询语句需要经过的流程 , 更新语句全部需要执行一次 , 因为更新之前必须要先拿到(查询)需要更新的数据 。
Buffer PoolInnnoDB的数据都是放在磁盘上的 , 而磁盘的速度和CPU的速度之间有难以逾越的鸿沟 , 为了提升效率 , 就引入了缓冲池技术 , 在InnoDB中称之为Buffer Pool 。
从磁盘中读取数据的时候 , 会先将从磁盘中读取到的页放在缓冲池中 , 这样下次读相同的页的时候 , 就可以直接从Buffer Pool中获取 。
更新数据的时候首先会看数据在不在缓冲池中 , 在的话就直接修改缓冲池中的数据 , 注意 , 前提是我们不需要对这条数据进行唯一性检查(因为如果要进行唯一性检查就必须加载磁盘中的数据来判断是否唯一了)
如果只修改了Buffer Pool中的数据而不修改磁盘中数据 , 这时候就会造成内存和磁盘中数据不一致 , 这种也叫做脏页 。 InnoDB 里面有专门的后台线程把 Buffer Pool 的数据写入到磁盘 ,每隔一段时间就一次性地把多个修改写入磁盘 , 这个动作就叫做刷脏 。
那么现在有一个问题 , 假如我们更新都需要把数据写入数据磁盘 , 然后磁盘也要找到对应的那条记录 , 然后再更新 , 整个过程 IO 成本、查找成本都很高 。 为了解决这个问题 , InnoDB就有了redo log,并且采用了Write-Ahead Logging(WAL)方案实现 。
redo logredo log , 即重做日志 , 是InnoDB引擎所特有,主要用于崩溃修复(crash-safe) 。
Write-Ahead Logging(WAL)Write-Ahead Logging , 即先写日志 , 也就是说我们执行一个操作的时候会先将操作写入日志 , 然后再写入数据磁盘 , 那么有人就会问了 , 写入数据表是磁盘操作 , 写入redo log也是磁盘操作 , 同样都是写入磁盘 , 为什么不直接写入数据 , 而要先写入日志呢?这不是多此一举吗?
设想一下 , 假如我们所需要的数据是随机分散在不同页的不同扇区中 , 那么我们去找数据的时候就是随机IO操作 , 而redo log是循环写入的 , 也就是顺序IO 。 一句话:刷盘是随机 I/O , 而记录日志是顺序 I/O , 顺序 I/O 效率更高 。 因此先把修改写入日 志 , 可以延迟刷盘时机 , 进而提升系统吞吐
redo log是如何刷盘的InnoDB中的 redo log是固定大小的 , 也就是说redo log并不是随着文件写入慢慢变大 , 而是一开始就分配好了空间 , 空间一旦写满了 , 前面的空间就会被覆盖掉 , 刷盘的操作是通过Checkpoint实现的 。 如下图:
巅峰战队|详解一条查询select语句和更新update语句的执行流程check point 是当前要覆盖的位置 。 write pos是当前写入日志的位置 。 写日志的时候是循环写的 , 覆盖旧记录前要把记录更新到数据文件 。 如果write pos和 check point 重叠 , 说明redo log 已经写满 , 这时候需要同步redo log刷到磁盘中 。
bin logMySQL整体来看 , 其实就有两块:一块是 Server 层 , 它主要做的是 MySQL功能层面的事情;还有一块是引擎层 , 负责存储相关的具体事宜 。 上面讲的redo log是InnoDB 引擎特有的日志 , 而Server 层也有自己的日志 , 称为 binlog(归档日志) , 也叫做二进制日志 。


推荐阅读