Loading

Mysql日志-初步学习

02 | 日志系统:一条SQL更新语句是如何执行的?

一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块,最后到达存储引擎。

MySQL 可以恢复到半个月内任意一秒的状态

img

--转自:《MySQL实战45讲》

MySQL 8.0 已经将查询缓存模块给去掉了,因为在server层保持与引擎层的数据一致有代价

原因之一就是因为在一个表上有更新的时候,跟这个表有关的查询缓存会失效,所以这条语句就会把表 T 上所有缓存结果都清空。这也是一般不建议使用查询缓存的原因。

日志系统中重要的是 redo log(重做日志) 与 binlog(归档日志),其中还有 undo log 日志,其他的日志不做展开。

Redo log(重做日志) - 记录数据被修改后的样子

redo 日志文件是 InnoDB 特有的,是存储引擎级别的,不是 MySQL 级别的

《MySQL实战45讲》的例子来理解 - 记账

一个掌柜的,有一个总的账本(binlog),一个展示板(Redo log),两者都用来记录客人们的赊账信息

增加一个展示板的操作,是因为忙碌的时候,客人还账与赊账通过翻阅账本来记录是十分麻烦的一件事情。展示板就是用来节约时间、提高效率的,这是原因之一。

在 MySQL,也是先写 日志文件(顺序IO),再写 归档日志(随机IO )。

顺序IO是指读写操作的访问地址连续。在顺序IO访问中,HDD所需的磁道搜索时间显着减少,因为读/写磁头可以以最小的移动访问下一个块。数据备份和日志记录等业务是顺序IO业务。

随机IO是指读写操作时间连续,但访问地址不连续,随机分布在磁盘的地址空间中。产生随机IO的业务有OLTP服务,SQL,即时消息服务等。

ps:顺序IO 比 随机IO 快多了

日志文件以 **日志文件组 **的形式出现,以 ib_logfile[数字] 的形式命名,在写入时按照数字顺序写入,如果写入到最后一个文件,那就重新转到ib_logfile0继续写。

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。

下图所示

img

--转自:《MySQL实战45讲》

checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录(对应日志的脏页)更新到数据文件(数据库中 - 磁盘文件)。

redo日志只是为了系统崩溃后恢复脏页用的,如果对应的脏页已经刷新到了磁盘,那么该redo日志也就没有存在的必要了,那么它占用的磁盘空间就可以被后续的redo日志所重用

也就是说:判断某些redo日志占用的磁盘空间是否可以覆盖的依据就是它对应的脏页是否已经刷新到磁盘里。

脏页:更新完数据(指对 Buffer Pool中的页(pages)下的数据)之后,Buffer Pool缓冲池中的中的数据就会和数据库中的数据库不一致,那就是说Buffer Pool 中的数据成了脏数据

Buffer Pool:

Buffer Pool (缓冲池)是 InnoDB 存储引擎中非常重要的 内存结构,类似 Redis 一样的作用,起到一个缓存的作用。MySQL 的数据最终是存储在磁盘中的,如果没有 Buffer Pool,那么每次的数据库请求都会磁盘中查找,这样必然会存在 IO 操作。但是有了 Buffer Pool,只有第一次在查询的时候会将查询的结果存到 Buffer Pool 中,这样后面再有请求的时候就会先从缓冲池中去查询,如果没有再去磁盘中查找,然后在放到 Buffer Pool 中,如下图

尽量减少耗时的 IO 操作,提升效率与准确性是目标

write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

binlog (归档日志)

最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe (崩溃重启,数据不会丢失)的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。

这两种日志有以下三点不同。

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

undo日志文件:记录数据被修改前的样子

在准备更新一条SQL语句的时候,该条语句对应的数据已经被加载到 Buffer pool 中了,实际上这里还有这样的操作,就是在将该条语句加载到 Buffer Pool 中的时候同时会往 undo 日志文件中插入一条日志,也就是将 id=1 的这条记录的原来的值记录下来,便于事务失败后进行回滚

更新语句的执行流程简析

1、执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

2、执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

3、引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

4、执行器生成这个操作的 binlog,并把 binlog 写入磁盘。

5、执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

img

--转自:《MySQL实战45讲》

再补充一个流程 - 对照着看:

img

--转载:MySQL数据库:SQL语句的执行过程

(1)准备更新一条 SQL 语句

(2)MySQL(innodb)会先去缓冲池(Buffer Pool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载 到缓冲池(Buffer Pool)中

(3)在加载到 Buffer Pool 的同时,会将这条数据的原始记录保存到 undo 日志文件中

(4)innodb 会在 Buffer Pool 中执行更新操作

(5)更新后的数据会记录在 redo log buffer 中

(6)MySQL 提交事务的时候,会将 redo log buffer 中的数据写入到 redo 日志文件中,刷磁盘可以通过 innodb_flush_log_at_trx_commit 参数来设置:
        0:每秒将 redo log buffer 中的数据将以写入到日志文件中,同时flush到磁盘。在机器crash并重启后,会丢失一秒的事务日志数据
        1:每次事务提交时,将 redo log buffer 中的数据写入日志文件,并同时flush到磁盘。在机器crash并重启后,不会丢失事务日志
        2:每次事务提交时,将 redo log buffer 中的数据写入日志文件,并每秒flush一次到磁盘。在机器crash并重启后,有可能丢失数据
        
(7)myslq 重启的时候会将 redo 日志恢复到缓冲池中

Ps:主要是下方的参考+自己的见解,感觉记录的比较乱,见谅

学习资料:

posted @ 2021-08-17 20:32  zhixlin  阅读(47)  评论(0编辑  收藏  举报