美烦资源网

专注技术文章分享,涵盖编程教程、IT 资源与前沿资讯

Mysql中的bin log、redo log、undo log的区别

最近在整理面试题,在看mvcc的时候看到了undolog,今天索性把这三个log都记录一遍。

MySQL 的逻辑架构

说之前先说一下MySQL的基本架构,MySQL 主要分为两层:Server 层和存储引擎层

Server 层的主要作用:与客户端建立连接、分析SQL、优化SQL和执行 SQL。MySQL 的大多数核心功能模块都在Server 层实现。在Server 层主要包括连接池、执行器、优化器、解析器、预处理器等。另外,所有的内置函数和所有跨存储引擎的功能(如存储过程、触发器、视图等)都在 Server 层实现。

存储引擎层的主要作用:负责数据的存储和提取。常用的存储引擎有 InnoDB、MyISAM等,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB。我们常说的索引数据结构,就是由存储引擎层实现的。

MYSQL的Buffer Pool(缓冲池)

我们知道,InnnoDB 的数据都是放在磁盘上的,InnoDB 操作数据有一个最小的逻辑单位,叫做页。我们对于数据的操作,不是每次都直接操作磁盘,因为磁盘的速度太慢了。InnoDB 使用了一种缓冲池的技术,也就是把磁盘读到的页放到一块内存区域里面。这个内存区域就叫 Buffer Pool。

下一次读取相同的页,先判断是不是在缓冲池里面,如果是,就直接读取,不用再次访问磁盘。

修改数据的时候,先修改缓冲池里面的页。内存的数据页和磁盘数据不一致的时候,我们把它叫做脏页。InnoDB 里面有专门的后台线程把 BufferPool 的数据写入到磁盘,每隔一段时间就一次性地把多个修改写入磁盘,这个动作就叫做刷脏。

BufferPool 是 InnoDB 里面非常重要的一个结构,它的内部又分成几块区域。我们要说的Redo log就与这个缓存池有关。

InnoDB 内存结构和磁盘结构

BufferPool 主要分为:Buffer Pool、Change Buffer、redo logbuffer,另外还有一个AdaptiveHash Index。

Redo Log

如果 BufferPool 里面的脏页还没有刷入磁盘时,数据库发生了宕机或者重启,此时会导致这些数据的丢失。

为了避免这个问题,InnoDB 把所有对页面的修改操作专门写入一个日志文件,并且在数据库启动时从这个文件进行恢复操作。

这个文件就是Redo Log,Redo Log 并不是每一次都直接写入磁盘,这就涉及到我们在上一章节说的Redo LogBuffer了,它是专门用来保存即将要写入日志文件的数据,认 16M。当我们提交事务时,Log Buffer会将数据写入Redo Log中。

Redo Log 的特点:

  1. Redo Log 是 InnoDB 存储引擎实现的,并不是所有存储引擎都有;
  2. 不是记录数据页更新之后的状态,而是记录这个页做了什么改动,属于物理日志;
  3. Redo Log 的大小是固定的,前面的内容会被覆盖。
  4. Redo Log保证了事务的持久性

Undo Log

本章节参考:
https://cloud.tencent.com/developer/article/2378614

除了 Redo Log之外,还有一个跟修改有关的日志,叫做 Undo Log(撤销日志或回滚日志),记录了事务发生之前的数据状态,分为 insert Undo Log 和 update Undo Log(由于查询操作(SELECT)并不会修改任何记录,所以在查询操作执行时,并不需要记录相应的 undo log 。)。如果修改数据时出现异常,可以用 Undo Log 来实现回滚操作(保持原子性)。

  • insert undo log:插入Undo日志是指在插入操作中生成的Undo日志。由于插入操作的记录只对当前事务可见,对其他事务不可见,因此在事务提交后可以直接删除,无需进行purge操作。
  • update undo log:更新Undo日志是指在更新或删除操作中生成的Undo日志。更新Undo日志可能需要提供MVCC机制,因此不能在事务提交时就立即删除。相反,它们会在提交时放入Undo日志链表中,并等待purge线程进行最终的删除。删除操作只是设置一下老记录的 DELETED_BIT,并不真正将过时的记录删除,为了节省磁盘空间,InnoDB有专门的purge线程来清理 DELETED_BIT 为true的记录。

当我们插入一条数据时,除了插入我们的数据外,还额外插入了DB_TRX_ID(当前事务ID)、DB_ROW_ID(行ID)、DB_ROLL_PTR(指针),默认隐藏。

当不同事务或者相同事务对同一条数据进行更新操作时,会使该记录行的 undo log 成为一条链表,链首就是最新的记录,链尾就是最早的旧记录

我们来举个详细的例子:有个事务A插入了一条新记录:insert into user(id, name) values(1, "小明')

现在来了一个事务B对该记录的name做出了修改,改为 "小王"。

在事务B修改该行数据时,数据库会先对该行加排他锁,然后把该行数据拷贝到 undo log 中作为旧记录,即在 undo log 中有当前行的拷贝副本.

拷贝完毕后,修改该行name为 "小王,并且修改隐藏字段的事务ID为当前事务B的ID, 并将回滚指针指向拷贝到 undo log 的副本记录,即表示我的上一个版本就是它,事务提交后,释放锁。

此时又来了个事务C修改同一个记录,将name修改为 "小红"。

在事务C修改该行数据时,数据库也先为该行加锁,然后把该行数据拷贝到 undo log 中,作为旧记录,发现该行记录已经有 undo log 了,那么最新的旧数据作为链表的表头,插在该行记录的 undo log 最前面,如下图:

关于 DB_ROLL_PTR 与 Undo日志 的配合工作,具体流程如下:

  1. 在更新或删除操作之前,MySQL会将旧值写入Undo日志中。
  2. 当事务需要回滚时,MySQL会根据事务的Undo日志记录,通过 DB_ROLL_PTR 找到对应的Undo日志。
  3. 根据Undo日志中记录的旧值,MySQL将旧值恢复到相应的数据行中,实现数据的回滚操作。

比方说现在想回滚到事务B,name值为 "小王" 的时候,只需通过 DB_ROLL_PTR 顺着列表找到对应的 Undo日志,将旧值恢复到数据行即可。

通过 DB_ROLL_PTR 和 Undo日志 的配合工作,MySQL能够有效地管理事务的一致性和隔离性。Undo日志的使用也使得MySQL能够支持MVCC,从而提供了高并发环境下的读取一致性和事务隔离性。

Undo Log的作用

  • 事务回滚:当事务需要回滚时,MySQL可以通过Undo日志中的旧值将数据还原到事务开始之前的状态,保证了事务回滚的一致性。
  • MVCC实现:MVCC 是InnoDB存储引擎的核心特性之一。通过使用Undo日志,MySQL可以为每个事务提供独立的事务视图,使得事务读取数据时能看到一致且符合隔离级别要求的数据版本。

update的更新过程:

有了 Redo Log 和 Undo Log,我们来总结一下一个 Update 操作的流程。

UPDATE user set name = 'lizhengi' where id=1;
  1. 在执行前需要:① 连接器连接数据库;② 分析器通过词法分析和语法分析知道这是一条更新语句;③ 优化器决定要使用的索引等;④ 执行器负责具体的执行过程;
  2. 事务开始,从内存(buffer poll)或磁盘(data file)取到包含这条数据的数据页,返回给 Server 的执行器;
  3. Server 的执行器修改数据页的这一行数据的值为 Server 的执行器修改数据页的这一行数据的值为小王
  4. 记录 name=小红(原值)到 Undo Log;
  5. 记录 name=小王 到 Redo Log;
  6. 调用存储引擎接口,记录数据页到 buffer pool(修改 name= 小王);
  7. 事务提交。

Bin Log(Binary Log)

与前面说的不同,Bin Log的实现并不在存储引擎层,而是在Server层。它记录了所有对MySQL数据库执行的数据修改语句(如INSERT,UPDATE,DELETE)和数据库的结构变更语句(如CREATE,ALTER,DROP)。同时,每条记录内容都包含了该语句执行的精确时间(可以理解为事件的发生时间)

binlog 有三种格式:

  • Statement:每一条会修改数据的 SQL 都会记录在 binlog 中。
  • Row:不记录 SQL 语句上下文信息,仅保存哪条记录被修改。
  • Mixed:Statement 和 Row 的混合体。

Statement

Statement 模式只记录执行的 SQL,不需要记录每一行数据的变化,因此极大的减少了 binlog 的日志量,避免了大量的 IO 操作,提升了系统的性能。

但是,正是由于 Statement 模式只记录 SQL,而如果一些 SQL 中 包含了函数,那么可能会出现执行结果不一致的情况。比如说 uuid() 函数,每次执行的时候都会生成一个随机字符串,在 master 中记录了 uuid,当同步到 slave 之后,再次执行,就得到另外一个结果了。所以使用 Statement 格式会出现一些数据一致性问题。

Row

从 MySQL5.1.5 版本开始,binlog 引入了 Row 格式,Row 格式不记录 SQL 语句上下文相关信息,仅仅只需要记录某一条记录被修改成什么样子了。

Row 格式的日志内容会非常清楚地记录下每一行数据修改的细节,这样就不会出现 Statement 中存在的那种数据无法被正常复制的情况。

不过 Row 格式也有一个很大的问题,那就是日志量太大了,特别是批量 update、整表 delete、alter 表等操作,由于要记录每一行数据的变化,此时会产生大量的日志,大量的日志也会带来 IO 性能问题。

Mixed

从 MySQL5.1.8 版开始,MySQL 又推出了 Mixed 格式,这种格式实际上就是 Statement 与 Row 的结合。

在 Mixed 模式下,系统会自动判断 该 用 Statement 还是 Row:一般的语句修改使用 Statement 格式保存 binlog;对于一些 Statement 无法准确完成主从复制的操作,则采用 Row 格式保存 binlog。

Mixed 模式中,MySQL 会根据执行的每一条具体的 SQL 语句来区别对待记录的日志格式,也就是在 Statement 和 Row 之间选择一种。

Binlog主要用于以下两个目的:

主从复制: Binlog用于实现MySQL的主从复制,即从服务器可以读取主服务器的Binlog来保持与主服务器的同步。主服务器上的所有数据改变(比如增,删,改)都写入二进制日志,然后从服务器获取这些日志,然后在它的本地数据据进行重放这些日志,从而达到数据的一致性。从而实现负载均衡和故障转移。

数据恢复: 当数据库出现故障时,可以使用Binlog进行点时间恢复。通过回放Binlog日志记录,可以将数据库状态恢复到某一特定时间点的状态,从而可以恢复数据并最小化数据丢失。



简单总结一下,binlog是server层的实现,主要是记录数据修改日志,同于主从复制和数据恢复;redo log和undo log是存储引擎层的实现,redo log保证事务的持久性,而undo log保证事务的一致性和隔离性,同时undo log也是MVCC中的重要一环。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言