Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
git-zjx opened this issue Jul 16, 2019 · 3 comments
Open

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

git-zjx opened this issue Jul 16, 2019 · 3 comments
Assignees
Labels
MySQL MySQL MySQL实战45讲 MySQL实战45讲笔记

Comments

@git-zjx
Copy link
Owner

git-zjx commented Jul 16, 2019

更新语句的执行流程

更新语句的执行流程和查询语句的执行流程类似,区别在于更新语句会使查询语句的查询缓存失效,同时还会涉及到两个日志模块:redo log (重做日志)和 bin log (归档日志)

redo log(重做日志)

当有一条记录需要更新时,InnoDB 引擎就会先把记录写到 redo log 中,并更新内存,此时更新算是完成了,之后,InnoDB 引擎会在适当的时候将记录写入磁盘中。

这里涉及到 WAL 技术(Write-Ahead Logging),先写日志,再写磁盘

redo log 的大小是固定的,比如可以配置为一组 4 个文件,每个文件的大小是 1 GB,那么 redo log 可以记录 4 GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下图:

image

write pos 是当前记录位置,一边写一边后移;checkpoint 是当前要擦除的位置,也是往后推移并循环的,擦除记录前要把记录更新到数据文件;write pos 和 checkpoint 之间的部分用来记录新的操作。如果 write pos 追上了 checkpoint 就需要停下先擦除一些记录,把 checkpoint 推进下

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

bin log(归档日志)

bin log 是 MySQL 的 Server 层维护的一种二进制日志,主要是用来记录对mysql数据更新或潜在发生更新的SQL语句,并以"事务"的形式保存在磁盘中

redo log 和 binlog 的区别

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

update 语句执行流程

update T set c=c+1 where ID=2;
  1. 执行器先查找引擎取 ID=2 这一行
  2. 执行器拿到引擎给的行数据,把这个值加上1,得到新的一行数据,再调用引擎接口写入这行新数据
  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时,redo log 处于 prepare 状态,然后告知执行器执行完成,随时可以提交事务
  4. 执行器生成这个操作的 binlog ,并把 binlog 写入磁盘
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成 commit 状态,更新完成

2e5bff4910ec189fe1ee6e2ecc7b4bbe

两阶段提交

两阶段提交主要是为了保证 redo log 和 binlog 保持一致

假设当前 ID=2 的行,字段 c 为 0,语句写完第一个日志后,第二个日志还没写完期间发生了 crash:

  1. 先写 redo log 后写 binlog
    由于 redo log 写完之后,系统即使 crash,仍可以恢复数据,这时 c=1。但由于 binlog 没有写完,之后需要使用这个 binlog 恢复临时库时,恢复出来的 c=0,与原库不同
  2. 先写 binlog 后写 redo log
    由于 redo log 还没写完,系统恢复后 c=0,但 binlog 里已经记录了,之后用 binlog 恢复时,恢复出来的 c=1,与原库不同

redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致

数据恢复

当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:

  1. 首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
  2. 然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻
@git-zjx git-zjx self-assigned this Jul 16, 2019
@git-zjx git-zjx added MySQL MySQL MySQL实战45讲 MySQL实战45讲笔记 labels Jul 16, 2019
@git-zjx git-zjx removed their assignment Jul 16, 2019
@git-zjx git-zjx self-assigned this Dec 16, 2019
@git-zjx
Copy link
Owner Author

git-zjx commented Dec 16, 2019

写 redo log 也是写 io,同样耗费性能,怎么是优化呢?

redo log是顺序写,并且可以组提交,还有别的一些优化,收益最大是这两个因素

@git-zjx
Copy link
Owner Author

git-zjx commented Dec 16, 2019

binlog 为什么说是逻辑日志呢?

逻辑日志可以给别的数据库,别的引擎使用,已经大家都讲得通这个“逻辑”;
物理日志就只有“我”自己能用,别人没有共享我的“物理格式”

@git-zjx
Copy link
Owner Author

git-zjx commented Dec 16, 2019

redo log 和 binlog 分别记录的是什么?

redo log 记录数据页做了什么改动
binlog 的 statement 格式记录 sql 语句,row 格式记录行的内容,记录更新前和更新后两条信息

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
MySQL MySQL MySQL实战45讲 MySQL实战45讲笔记
Projects
None yet
Development

No branches or pull requests

1 participant