结合查询、更新粗谈Mysql基础架构和binlog、redolog

重点知识点:

  1. Mysql基础架构组成,包括了连接器、分析器、查询缓存(8.0版本移除)、优化器、执行器以及最下层的存储引擎。
  2. 针对InnoDB引擎,介绍更新操作。基于 redolog 和 binlog 的两阶段提交是如何实现 crash-safe的呢?

一条查询语句的执行过程

1. 与mysql建立链接-连接器

连接器负责跟客户端建立连接、获取权限、维持和管理连接。
首先就建立TCP的连接了,然后校验用户名密码,鉴权成功会从权限表中读取出拥有的权限,之后的权限校验都会依赖于它。这意味着,客户端建立连接后,再修改这个客户端的权限是不会生效的。

-- 查询客户端状态,其中Command为Sleep的表示,空闲连接。
-- 空闲时间超过 8小时 将会被断开。可以通过 wait_timeout 设置。
show processlist;

建立连接是相对耗时的,因此尽量用长连接(使用连接池)。但MySQL执行过程中使用的内存是管理在连接对象中的,==大量的长连接累积可能导致内存飙升==。解决方案:1.定时断开长链接,如执行了占用大内存的查询后,程序自动判断断开。2.Mysql5.7版本后,在执行了大操作后,通过执行 mysql_reset_connection来重新初始化资源,这个过程不会断开连接。

2. 内存中存在结果-查询缓存

如果查询缓存里面存在,那么就直接返回。但是==查询缓存非常容易失效,这导致缓存命中率很低,增加不必要的负担,除非你的业务就是有一张静态表==。例如,在一个表上做任何更新操作,那所有的缓存都会失效。可以通过设置 query_cache_type关闭查询缓存,MySQL8.0已经移除了这一部分。

3. 检查SQL是否有语法错误-分析器

检查SQL语法是否错误,查询的表和列是否存在等等。这里也会对权限的 precheck 。

4. 尝试优化SQL-优化器

相同结果的SQL,但执行效率可能会天差地别。优化器作用,就是根据各种指标信息,选择一个最优的方案。例如:1.多个索引时,应该选择那个。2.多表join时,先后顺序又是怎样。当然你也可以干预优化过程,有时候会通过force index 强制使用某个索引。

5. 执行查询操作-执行器

分析器的结果告诉了MySQL要做什么,优化器告诉MySQL如何去更好的做。执行器重点负责实施查询过程。首先执行器判断是否具有这个表的查询权限。鉴权完毕,根据表的引擎定义,调用下层的数据引擎定义的接口。数据引擎,根据条件扫描数据,每当发现满足条件的就交给执行器,重复这个过程。最终执行器将结果集返回给客户端。在慢日志中经常会看到一个row_examined字段,它表示,执行器每次调用引擎获取数据行的累加。==row_examined 并不等于 引擎扫描的行数,一般执行器调用一次接口,引擎会扫描多行的==。

一条更新语句的执行过程

下面的执行过程是基于InnoDB数据引擎的,与查询类似,更新也需要连接器,分析器,优化器和执行器,不再重复描述,直奔主题。例如下面的一个更新语句:

update t1 set col1 = 1 where id = 1;

1. 根据 id=1 将数据从磁盘加载到内存中,如果已经在内存中存在则不需要加载。
2. 在内存中 col1 更改为1
3. 调用InnoDB引擎接口,将内存中的行数据,写入到redolog中,数据处于 prepared状态。

因为IO操作成本是非常高的,所以引入了 Write-Ahead logging(简称WAL) 技术,简单说WAL就是,先写入日志中,之后根据一定策略将内存中数据在刷进磁盘当中。至于说redolog 的 prepared状态,是为了通过两阶段提交实现 crash-safe,后面来总结,redo log 、binlog 和两阶段提交。

4. 写入redo log成功后,在写入 server层的 binlog中。
5. 最终在将redolog 状态改变为 commit状态

至此,一条基于InnoDB的更新语句就完成了。下面,详细的介绍一下更新过程中的,redolog binlog 和两阶段提交实现。

redo log 和 bin log 介绍

redo log

  1. redo log是InnoDB引擎特有的。
  2. redo log的大小是固定的,循环写,可以配置多个文件。例如配置4个 1G 大小的文件,从第一个文件开始写,当写到第四个文件最后的时候,又会回到第一个文件开头写。有两个指针,一个write pos 指向当前写入的位置,一个check pos,写入时,移动write pos,输入磁盘移动check pos。当 write pos 追上 check pos代表写满了,不能再执行写入操作,必须执行刷盘。
  3. 引入redo log重要的一点,是为了降低实时刷盘的成本。先写入内存日志,之后根据一定策略将数据刷入到磁盘中,这种机制,又叫做 Write-Ahead log
    (WAL)。
  4. redo log 是有状态的,这一点设计主要是为了实现crash-safe能力。只有commit状态的数据才能被刷入磁盘中。
  5. redo log是物理日志,相比于逻辑日志。物理日志只会记录在某个数据块的修改,不会记录SQL的逻辑。

bin log

  1. bin log 是 Mysql Server 层的,不管什么存储引擎,数据最终都会在bin log中记录。
  2. bin log 是 追加写,写满一个文件之后,则创建另一个接着写。因此借助bin log 可以将数据库恢复到任何一个状态。
  3. bin log 记录的逻辑日志。
  4. bin log 的存在重点是为了,数据备份和数据同步。例如主从同步,从库便是监听主库的bin log。又例如一些数据传输中间件,都是利用bin log 将数据从一个地方,传输到另一个地方的。

redo log VS bin log

  1. redo log 长度固定,循环写,bin log 长度不固定,追加写可以有很多。
  2. redo log 是InnoDB特有的,bin log是Mysql Server层的,所有引擎公用的。
  3. redo log 具有prepared 和 commit的状态
  4. redo log 是一种物理日志,bin log则是逻辑日志。

逻辑日志和物理日志:例如记录SQL语句,它就是逻辑日志,可读性强,具有执行的语法逻辑。物理日志,就比较直接,直接指明在某个磁盘空间的修改,例如将,让xxx地址设置成xxx。记录了最终的结果,至于这个结果是如何产生的,就不可知了。


两阶段提交。

前面提到,redo log 是有状态的。更新过程需要两个阶段才能提交。

  1. ==第一步==写入 redo log 状态为 prepared
  2. ==第二步==写入 bin log
  3. ==第三步==修改 redo log 状态为 commit

这样做主要是为了保证在出现异常时(不包括redo log出现了丢失,关键磁盘损坏),bin log 和 redo log的数据一致,俗称crash safe。

试想,假如没有两阶段提交,先写入 redo log ,再写入 bin log,但在写入bin log之前,Mysql异常了。导致 redo log里面有 bin log中没有,那么对 Mysql 主从来说,主库存在了,从库却没有,这是多么诡异的事情呀。(先写 bin log 再写 redo do,也会存在不一致问题)。

为什么两阶段提交就可以解决redo log 和 bin log得不一致问题实现crash safe呢?

  1. 第一步成功,第二步之前异常,此时mysql 重启后,读取redo log 发现是 prepared,去看bin log 发现没有,直接回滚掉这条数据。
  2. 第一步成功,第二步成功,第三部之前失败,此时mysql重启后,读取redo log 发现是 premared状态,但是bin log 中已经有了,则修改为 commit状态并直接提交。

第一步,第二步,第三步的含义请往上瞅。

可能有人会质疑,即使redo log 没有状态,异常重启是,mysql 对比 redo log 和 bin log 差异,然后裁剪彼此差异,也能保持两者一致。但这是有问题,redo log 是一个循环写的结构,如果没有状态,redo log 一旦刷入磁盘,那这部分日志就可以被另外的日志覆盖。这就导致在redo log 没鱼状态的情况下,redo log 和 bin log 差异对比是不严谨,不正确的。

你可能感兴趣的:(结合查询、更新粗谈Mysql基础架构和binlog、redolog)