MySQL-三大日志

前言

redo log:为了持久化数据,当内存中的数据还没写入到磁盘而宕机时,会读取该日志持久化数据到磁盘

undo log:为了保证原子性,事务的操作都会记录一条相反的sql到该日志,出现错误就会根据该文件恢复事务之前的数据

binlog:为了复制和恢复数据,mysql重启可以通过该日志恢复

​ 以上三大日志是MySQL的原子性、持久性、数据一致性的保证。

bin log

bin log全称binary log,二进制日志文件,它记录了数据库所有执行的 DDLDML 等数据库更新的语句,但是不包含select或者show等没有修改任何数据的语句。它是MySQL级别的日志,也就是说所有的存储引擎都会产生bin log,而redo log或者undo log事务日志只有innoDB存储引擎才有。

可以使用show binary logs; 命令查看所有二进制日志列表。可以看到binlog 日志文件名为 文件名.00000 形式。*

​ 通过 show binlog events in ‘binlog.000008’ limit 10; 命令查看日志的具体内容。另外一定要指定 limit,不然查询出来的日志文件内容太多。另外, MySQL 内置了 binlog 查看工具 mysqlbinlog,可以解析二进制文件。

1、写入方式

​ binlog 通过追加的方式进行写入,大小没有限制。另外可以通过max_binlog_size参数设置每个 binlog 文件的最大容量,当文件大小达到给定值之后,会生成新的 binlog 文件来保存日志,不会出现前面写的日志被覆盖的情况。

写入流程

1)为了保证写的效率,会将事务的bin log先写到binlog cache中,这个cache位于事务线程的内存中,主要是一个事务的bin log不能被拆开,是一个整体。

**2)在提交事务的时候,将binlog cache中的数据统一写到文件系统缓存page cache中,**这个过程速度也很快

3)然后根据不同的策略,将文件系统缓存中的bin logfsync刷到磁盘中,这里的策略后面详细讲解。

2、3种binlog 的格式

1)Statement 模式 :每一条会修改数据的sql都会被记录在binlog中,如inserts, updates, deletes。
**2)Row 模式 (推荐): 为了解决Statement缺点,记录具体哪一个分区中的、哪一个页中的、哪一行数据被修改了。**但是适用于数据量比较大的场景,否则记录分区信息,查找规则,磁盘IO、网络带宽开销很大。

3)Mixed 模式 :Statement 模式和 Row 模式的混合。

​ **默认使用 Statement 模式,少数特殊具体场景自动切换到 Row 模式。**MySQL 5.1.5 之前 binlog 的格式只有 STATEMENT,5.1.5 开始支持 ROW 格式的 binlog,从 5.1.8 版本开始,MySQL 开始支持 MIXED 格式的 binlog。MySQL 5.7.7 之前,默认使用 Statement 模式。MySQL 5.7.7 开始默认使用 Row 模式。

相比较于 Row 模式来说,Statement 模式下的日志文件更小,磁盘 IO 压力也较小,性能更好有些。不过其准确性相比于 Row 模式要差。可以使用 show variables like ‘%binlog_format%’; 查看 binlog 使用的格式。

3、应用场景

binlog 最主要的应用场景是 主从复制和数据恢复 ,主从复制常见是主备、主主、主从都离不开binlog,需要依靠 binlog 来同步数据,保证数据一致性。

​ 可以使用show variables like ‘log_bin’;查看数据库是否启用 binlog 日志,默认是开启的。

1)主从复制的原理:
  • 主库将数据库中数据的变化写入到 binlog
  • 从库连接主库
  • 从库会创建一个 I/O 线程向主库请求更新的 binlog
  • 主库会创建一个 binlog dump 线程来发送 binlog ,从库中的 I/O 线程负责接收
  • 从库的 I/O 线程将接收的 binlog 写入到 relay log 中。
  • 从库的 SQL 线程读取 relay log 同步数据本地(也就是再执行一遍 SQL )。
2)Canal主从复制原理;
  • Canal 模拟 MySQL Slave 节点与 MySQL Master 节点的交互协议,把自己伪装成一个 MySQL Slave 节点,向 MySQL Master 节点请求 binlog;
  • MySQL Master 节点接收到请求之后,根据偏移量将新的 binlog 发送给 MySQL Slave 节点;
  • Canal 接收到 binlog 之后,就可以对这部分日志进行解析,获取主库的结构及数据变更。

4、binlog 的刷盘时机

​ **对于InnoDB存储引擎而言,事务在执行过程中,会先把日志写入到binlog cache中,只有在事务提交的时候,才会把binlog cache中的日志持久化到磁盘上的binlog文件中。**写入内存的速度更快,这样做也是为了效率考虑。

因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。

刷盘策略

biglog有3种刷盘策略, bin log的write和fsync时机是由参数 sync_binlog 控制。 MySQL5.7 之前, sync_binlog 默认值为 0。在 MySQL5.7 之后, sync_binlog默认值为 1。

  • 0:不去强制要求。

​ 表示每次提交事务都只 write,由系统自行判断什么时候执行fsync。虽然性能得到提升,但是机器宕机,page cache里面的 binglog 会丢失。

  • 1:每次提交事务的时候都要将binlog写入磁盘,也就是说都会执行fsync
  • N:每 N 个事务,才会将binlog写入磁盘(累积N个事务后才fsync)。

什么情况下会重新生成 binlog?

●MySQL服务器停止或重启;

●使用 flush logs 命令后;

●binlog 文件大小超过 max_binlog_size变量的阈值后。

bin log没写完,bin log 与redo log之间的一致性问题有什么解决方案?

​ InnoDB存储引擎使用两阶段提交方案。将redo log的写入拆成了两个步骤preparecommit

​ 假如现在写入bin log时MySQL发生异常,这时候的redo log还处于prepare阶段,重启MySQL后,根据redo log记录中的事务ID,发现没有对应的bin log日志,回滚前面已写入的数据。

​ 如果redo logcommit阶段发生移除,但是能通过事务id找到对应的bin log日志,所以MySQL认为是完整的,就会提交事务恢复数据。

数据恢复?

通过使用 mysqlbinlog 工具来恢复数据。

redo log

MySQL InnoDB 引擎使用 redo log 来保证事务的持久性。redo log 主要做的事情就是记录页的修改,比如某个页面某个偏移量处修改了几个字节的值以及具体被修改的内容是什么。redo log 中的每一条记录包含了表空间号、数据页号、偏移量、具体修改的数据,甚至还可能会记录修改数据的长度(取决于 redo log 类型)。

redo log包含两部分:内存中的日志缓冲(redo log buffer)和磁盘上的日志文件(redo logfile)。内存层面,默认16M,通过innodb_log_buffer_size参数可修改。

​ MySQL 每执行一条 DML 语句,先将记录写入 redo log buffer,后续某个时间点再一次性将多个操作记录写到 redo log file。

写入过程

在计算机操作系统中,用户空间( user space )下的缓冲区数据一般情况下是无法直接写入磁盘的,中间必须经过操作系统内核空间( kernel space )缓冲区( OS Buffer )。因此, redo log buffer 写入 redo logfile 实际上是先写入 OS Buffer ,然后再通过系统调用 fsync() 将其刷到 redo log file中。

流程:
  • 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝,产生脏数据
  • 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
  • 默认在事务提交后将redo log buffer中的内容刷新到redo log file,对redo log file采用追加写的方式
  • 定期将内存中修改的数据刷新到磁盘中(这里指的是内存缓冲池中的脏页 )

写入策略

当redo log空间满了之后又会从头开始以循环的方式进行覆盖式的写入。MySQL 支持三种将 redo log buffer 写入 redo log file 的时机,可以通过 innodb_flush_log_at_trx_commit 参数配置,各参数含义如下:

  • 0(延迟写):表示每次事务提交时都只是把 redo log 留在 redo log buffer 中,开启一个后台线程,每1s刷新一次到磁盘中 ;
  • 1(实时写,实时刷):表示每次事务提交时都将 redo log 直接持久化到磁盘,真正保证数据的持久性;
  • 2(实时写,延迟刷):表示每次事务提交时都只是把 redo log 写到 page cache,具体的刷盘时机不确定。

​ 除了上面几种机制外,还有其它两种情况会把redo log buffer中的日志刷到磁盘。

  • 定时处理:有线程会定时(每隔 1 秒)把redo log buffer中的数据刷盘。
  • 根据空间处理:redo log buffer 占用到了一定程度( innodb_log_buffer_size 设置的值一半)占,这个时候也会把redo log buffer中的数据刷盘。

重做日志缓冲(redo log)

重做日志(redo log)是数据库的重要组成,他保证了数据库宕机并重启后数据恢复的正确性。重做日志共有两个文件ib_logfile0和ib_logfile1。每个文件的大小一直且循环写入,也就是说先写ib_logfile0,写满之后再写ib_logfile1,又写满后,继续写ib_logfile0。

​ **redo log日志里面记录的主要是:表空间+数据页号+偏移量+修改几个字节的值+具体的值。**根据你修改了数据页里的几个字节的值,redo log就划分为了不同的类型,MLOG_1BYTE类型的日志指的就是修改了1个字节的值,MLOG_2BYTE类型的日志指的就是修改了2个字节的值,以此类推,还有修改了4个字节的值的日志类型,修改了8个字节的日志类型。如果修改了一大串的值,类型就是:MLOG_WRITE_STRING,就是代表你一下子在哪个数据页的某个偏移量的位置插入或者修改了一大串的值。

所以redo log看起来的结构如下:日志类型(类似MLOG_1BYTE之类的数据),表空间ID,数据页号,数据页中的偏移量,修改数据长度(MLOG_WRITE_STRING类型的才有),具体修改的数据。

当数据库修改页时,会将修改信息放入重做日志缓冲区,然后按一定频率(默认每秒一次)写入到重做日志文件,重做日志缓冲区一般不需要设置很大,只要保证每秒产生的事务量在这个缓冲区大小内即可,缓冲区大小可以通过参数innodb_log_buffer_size配置,默认为8MB

写入触发

​ 一般情况下,对于通常的事务提交,分为三个阶段:

  • 事务准备提交:
  • 事务提交过程中:
  • 事务提交完成:

​ **主要过程是,在事务提交时候, 会发生强制的将 redo log buffer的日志缓存的数据,强制写入 redo log file中, 通常会调用一次操作系统的fsync()的操作。**其中还会经过操作系统的内核空间, OS buffer ,因为MySQL的进行和日志缓存都工作在操作系统中环境下

本质:

事务提交的过程中,必须将日志缓存的数据持久化到磁盘的日志文件中,期间还需要经过操作系统的 “内核空间缓存区”,也就是OS Buffer区域,Redo log从用户空间的 Log buffer 写入磁盘的Redo Log文件时,需要要内核空间的OS buffer;日志文件,没有使用 O_DIRECT标识,如果有这个标识,就可以不经过这个os buffer的内核空间,直接写入磁盘数据;

问题

1)为什么不直接修改磁盘中的数据?

​ 因为直接修改磁盘数据的话,它是随机IO,修改的数据分布在磁盘中不同的位置,需要来回的查找,所以命中率低,消耗大,而且一个小小的修改就不得不将整个页刷新到磁盘,利用率低;

与之相对的是顺序IO,磁盘的数据分布在磁盘的一块,所以省去了查找的过程,节省寻道时间。使用后台线程以一定的频率去刷新磁盘可以降低随机IO的频率,增加吞吐量,这是使用buffer pool的根本原因。

2)redo log一定能保证事务的持久性吗?

​ 不一定,这要根据redo log的刷盘策略决定,因为redo log buffer同样是在内存中,如果提交事务之后,redo log buffer还没来得及将数据刷新到redo log file进行持久化,此时发生宕机照样会丢失数据。

​ 刷盘写入策略。

3)页修改之后为什么不直接刷盘呢?

​ InnoDB 页的大小一般为 16KB,而页又是磁盘和内存交互的基本单位。这就导致即使我们只修改了页中的几个字节数据,一次刷盘操作也需要将 16KB 大小的页整个都刷新到磁盘中。而且,这些修改的页可能并不相邻,也就是说这还是随机 IO。

采用 redo log 的方式就可以避免这种性能问题,因为 redo log 的刷盘性能很好。首先,redo log 的写入属于顺序 IO。 其次,一行 redo log 记录只占几十个字节。另外,Buffer Pool 中的页(脏页)在某些情况下(比如 redo log 快写满了)也会进行刷盘操作。不过这里的刷盘操作会合并写入,更高效地顺序写入到磁盘。

4)什么情况下会将缓冲区内容写入到重做日志中

1)主线程Master Thread每秒会写入一次

2)每个事务提交时会写入一次

3)当重做日志缓冲池剩余空间小于50%时,会写入一次

undo log

undo log也属于引擎层(innodb)的日志,由上可知,redo log 和undo log的核心是为了保证innodb事务机制中的持久性和原子性,事务提交成功由redo log保证数据持久性,而事务可以进行回滚从而保证事务操作原子性则是通过undo log 来保证的。

应用场景

1)事务回滚

​ 后台线程会不定时的去刷新buffer pool中的数据到磁盘,但是如果该事务执行期间出现各种错误(宕机)或者执行rollback语句,那么前面刷进去的操作都是需要回滚的,保证原子性,undo log就是提供事务回滚的。

2)MVCC

​ **在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 事务ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 回滚指针 找到 undo log 的日志版本链做比较,即遍历链表中的事务ID,直到找到满足条件的DBTRXID,这个DBTRXID所在的旧记录就是当前事务能看到的最新老版本数据。**从中分析出该行记录以前的数据版本是怎样的,从而让用户能够读取到当前事务操作之前的数据——快照读。

MVCC 的实现依赖于:隐藏字段(自增ID、事务ID、回滚指针)、Read View、undo log。

undo log 数据格式

undo log 数据主要分两类:

1)insert undo log

insert 操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该insert undo log可以在事务提交后直接删除,不需要进行purge操作。

2)update undo log

update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。

在InnoDB存储引擎中,undo log使用rollback segment回滚段进行存储,每个回滚段包含了1024个undo log segment。MySQL5.5之后,一共有128个回滚段。即总共可以记录128 * 1024个undo操作。

问题

1)undo log 如何保证事务的原子性?

​ 每一个事务对数据的修改都会被记录到 undo log ,当执行事务过程中出现错误或者需要执行回滚操作的话,MySQL 可以利用 undo log 将数据恢复到事务开始之前的状态。undo log 属于逻辑日志,记录的是 SQL 语句,比如说事务执行一条 DELETE 语句,那 undo log 就会记录一条相对应的 INSERT 语句。

2)除了保证事务的原子性,undo log 还有什么用?

InnoDB存储引擎中 MVCC 的实现用到了 undo log 。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo log读取之前的行版本信息,以此实现非锁定读取。

总结

binlog 是MySQL server层的日志,而redo log 和undo log都是引擎层(InnoDB)的日志,要换其他数据引擎那么就未必有redo log和undo log了。

它的设计目标是支持innodb的“事务”的特性,事务ACID特性分别是原子性、一致性、隔离性、持久性, 一致性是事务的最终追求的目标。隔离性是通过MVCC+锁机制来实现的,而事务的原子性和持久性则是通过redo log 和undo log来保障的。

​ redo log让InnoDB有了崩溃恢复的能力,binlog保证了MySQL集群架构的数据一致性。

​ 在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo log与binlog的写入时机不一样。

MySQL 三大日志中,bin log属于Server的,有缓冲buffer,缓冲区域在事务线程的内存中。redo log undo log 属于innoDB存储引擎,有缓冲buffer,在内存缓冲区域,由 LRU 链表和 Flush 链表管理。undo log 存储在引擎内部采用rollback segment回滚段进行存储。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值