相见太晚 發表於 2023-6-7 00:00:00

字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?

<p>
        <img title="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" alt="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" border="0" src="https://zhuji.jb51.net/uploads/img/202305/d9fa3daae979bc3c15ecdf533965bd4e.jpg"></p>
<p>
        又是被自己菜醒的一天,总结面经看到这题目听都没听过,打开百度就像吃饭一样自然
</p>
<p>
        老规矩,背诵版在文末。点击阅读原文可以直达我收录整理的各大厂面试真题
</p>
<p>
        首先,咱需要明白的是,啥是持久化?
</p>
<p>
        听起来高大上,换句简单的话来说,就是把数据写到磁盘上,也成为落盘。
</p>
<p>
        那为啥要做持久化到磁盘?
</p>
<p>
        目的就是可以在数据丢失后进行恢复,保证数据不丢失。
</p>
<p>
        那么对于 MySQL 来说,只要 binlog 和 redolog 都能正确持久化到磁盘上,就可以保证数据不丢失了。
</p>
<p>
        由此引出文题,不过在讲 redolog 之前,我们还是有必要先来说一下 binlog 的持久化操作。
</p>
<h3>
        binlog 持久化
</h3>
<p>
        这里引入了一个新的概念:binlog cache
</p>
<p>
        从名字就能看出来,binlog cache 其实就是一片内存区域,充当缓存的作用。
</p>
<p>
        每个线程都有自己 binlog cache 区域,在事务运行的过程中,MySQL 会先把日志写到 binlog cache 中,等到事务真正提交的时候,再统一把 binlog cache 中的数据写到 binlog 文件中。(binlog cache 有很多个,binlog 文件只有一个!)
</p>
<p>
        事实上,这个从 binlog cache 写到 binlog 文件中的操作,并不就是落盘操作了,这里仅仅是把 binlog 写到了文件系统的 page cache 上(这一步对应下图中的 write 操作)。
</p>
<p>
        简单解释下文件系统的 page cache:
</p>
<p>
        CPU 如果要访问外部磁盘上的文件,需要首先将这些文件的内容拷贝到内存中,由于硬件的限制,从磁盘到内存的数据传输速度是很慢的,如果现在物理内存有空余,干嘛不用这些空闲内存来缓存一些磁盘的文件内容呢,这部分用作缓存磁盘文件的内存就叫做 page cache。
</p>
<p>
        很多同学看到这里可能觉得特别特别熟悉,是的,和 CPU 里的高速缓存是不是很像?两者其实都是利用的局部性原理,只不过高速缓存是 CPU 缓存内存的数据,而 page cache 是内存缓存磁盘的数据,这也体现了操作系统内存层次结构分级的思想。
</p>
<p>
        所以,最后需要把 page cache 中的数据同步到磁盘上,才算真正完成了 binlog 的持久化(这一步对应下图中的 fsync 操作)。一般情况下,我们认为 fsync 才占磁盘的 IOPS (Input/Output Operations Per Second)
</p>
<p>
        <img title="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" alt="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" border="0" src="https://zhuji.jb51.net/uploads/img/202305/8d85a687c9f7d1fbea12d5f98955a3a0.jpg"></p>
<p>
        write 和 fsync 的时机,是由参数 sync_binlog 控制的:
</p>
<ul>
<li>
                sync_binlog = 0,每次提交事务的时候,只进行 write,不进行 fsync
        </li>
        <li>
                sync_binlog = 1候,每次提交事务的时候,执行 write 和 fsync
        </li>
        <li>
                sync_binlog = N(N&gt;1),每次提交事务的时候,执行 write,累积 N 个事务后再执行 fsync
        </li>
</ul>
<p>
        可以看出来,如果业务场景涉及到的 IO 操作很多的话,可以适当增大 sync_binlog 的值,提高性能。但是也存在一定的风险,比如你设置成 100,万一在第 80 个事务提交的时候数据库宕机了,那这些事务的 binlog 日志由于没有执行 fsync,也就丢失了。
</p>
<h3>
        redolog 持久化
</h3>
<p>
        类比 binlog,在事务执行过程中,binlog 都是存在 binlog cache 中,redolog 也有这样一块内存区域,叫作 redolog buffer。
</p>
<p>
        在事务运行的过程中,MySQL 会先把日志写到 redolog buffer 中,等到事务真正提交的时候,再统一把 redolog buffer 中的数据写到 redolog 文件中。和 binlog 的落盘操作一样,这个从 redolog buffer 写到 redolog 文件中的操作,并不就是落盘操作了,这里仅仅是把 redolog 写到了文件系统的 page cache 上,最后还需要执行 fsync 才能够实现真正的落盘。
</p>
<p>
        <img title="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" alt="字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?" border="0" src="https://zhuji.jb51.net/uploads/img/202305/03431e7bce3fe663cdca78f259ce83d3.jpg"></p>
<p>
        解释下 redo log buffer?
</p>
<p>
        在一个事务的更新过程中,日志是要写多次的。比如下面这个事务:
</p>
<ol class="dp-sql">
<li class="alt">
                <span><span class="keyword">begin</span><span>; </span></span>
        </li>
        <li>
                <span><span class="keyword">insert</span><span> </span><span class="keyword">into</span><span> table1 ... </span></span>
        </li>
        <li class="alt">
                <span><span class="keyword">insert</span><span> </span><span class="keyword">into</span><span> table2 ... </span></span>
        </li>
        <li>
                <span><span class="keyword">commit</span><span>; </span></span>
        </li>
</ol>
<p>
        这个事务要往两个表 table1 和 table2 中插入记录,为了确保这个事务不被拆开,一次性的完整写入日志文件中,在插入数据的过程中,我们需要把生成的日志都先保存起来。redolog buffer 就是这么一个用来先存 redo 日志的地方。
</p>
<p>
        也就是说,在执行第一条 insert 语句的时候,redolog buffer 也就写入了这条记录的日志。
</p>
<p>
        不同于 binlog cache 每个线程都有一个,redolog buffer 只有那么一个。
</p>
<p>
        事实上,日志写到 redolog buffer 是很快的,wirte 到 page cache 也差不多,但是 fsync 持久化到磁盘的速度就慢多了,为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:
</p>
<ul>
<li>
                innodb_flush_log_at_trx_commit = 0,每次事务提交的时候,都只是把 redolog 留在 redolog buffer 中
        </li>
        <li>
                innodb_flush_log_at_trx_commit = 1,每次事务提交的时候,都执行 fsync 将 redolog 直接持久化到磁盘
        </li>
        <li>
                innodb_flush_log_at_trx_commit = 2,每次事务提交的时候,都只执行 write 将 redolog 写到文件系统的 page cache 中
        </li>
        <li>
                说了这么多,各位小伙伴们对 binlog 和 redolog 的持久化机制想必都有所了解了,我们来看文题:事务还没提交的时候,redolog 能不能被持久化到磁盘呢?
        </li>
</ul>
<p>
        先说答案,答案就是有可能。
</p>
<p>
        分析下 redolog 可能存在的三种状态(binlog 也差不多):
</p>
<ul>
<li>
                事务执行过程中,存在 MySQL 的进程内存中的 redolog buffer 中
        </li>
        <li>
                事务提交,执行 write 操作存在文件系统的 page cache 中,但是没有执行 fsync 操作持久化到磁盘
        </li>
        <li>
                事务提交,执行 fsync 操作持久化到磁盘
        </li>
</ul>
<p>
        至于为什么说事务还没提交的时候,redolog 也有可能被持久化到磁盘呢?
</p>
<p>
        InnoDB 有一个后台线程,每隔 1 秒轮询一次,具体的操作是这样的:调用 write 将 redolog buffer 中的日志写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。而在事务执行中间过程的 redolog 都是直接写在 redolog buffer 中的,也就是说,一个没有提交的事务的 redolog,也是有可能会被后台线程一起持久化到磁盘的。
</p>
<p>
        另外,除了后台线程每秒一次的轮询操作外,还有两种场景会让一个没有提交的事务的 redolog 写盘:
</p>
<ul>
<li>
                innodb_flush_log_at_trx_commit 设置是 1,这样并行的某个事务提交的时候,就会顺带将这个事务的 redolog buffer 持久化到磁盘
        </li>
</ul>
<p>
        举个例子,假设事务 A 执行到一半,已经写了一些 redolog 到 redolog buffer 中,这时候有另外一个事务 B 提交,按照 innodb_flush_log_at_trx_commit = 1 的逻辑,事务 B 要把 redolog buffer 里的日志全部持久化到磁盘,这时候,就会带上事务 A 在 redolog buffer 里的日志一起持久化到磁盘
</p>
<ul>
<li>
                redo log buffer 占用的空间达到 redolo buffer 大小(由参数 innodb_log_buffer_size 控制,默认是 8MB)一半的时候,后台线程会主动写盘。不过由于这个事务并没有提交,所以这个写盘动作只是 write 到了文件系统的 page cache,仍然是在内存中,并没有调用 fsync 真正落盘
        </li>
</ul>
<p>
        最后放上这道题的背诵版:
</p>
<ul>
<li>
                面试官:
        </li>
        <li>
                问题:事务还没提交的时候,redo log 能不能被持久化到磁盘呢?
        </li>
</ul>
<p>
        相关问题:MySQL 是如何保证数据不丢失的呢?
</p>
<p>
        小牛肉:事务还没有提交的时候,redo log 是有可能被持久化到磁盘的。
</p>
<p>
        redolog 的具体落盘操作是这样的:在事务运行的过程中,MySQL 会先把日志写到 redolog buffer 中,等到事务真正提交的时候,再统一把 redolog buffer 中的数据写到 redolog 文件中。不过这个从 redolog buffer 写到 redolog 文件中的操作也就是 write 并不就是落盘操作了,这里仅仅是把 redolog 写到了文件系统的 page cache 上,最后还需要执行 fsync 才能够实现真正的落盘。
</p>
<p>
        也就是说,redolog 其实存在三种状态:
</p>
<ul>
<li>
                事务执行过程中,存在 MySQL 的进程内存中的 redolog buffer 中
        </li>
        <li>
                事务提交,执行 write 操作存在文件系统的 page cache 中,但是没有执行 fsync 操作持久化到磁盘
        </li>
        <li>
                事务提交,执行 fsync 操作持久化到磁盘
        </li>
</ul>
<p>
        额为什么说事务还没提交的时候,redolog 也有可能被持久化到磁盘呢?
</p>
<p>
        主要有三种可能的原因:
</p>
<ul>
<li>
                第一种情况:InnoDB 有一个后台线程,每隔 1 秒轮询一次,具体的操作是这样的:调用 write 将 redolog buffer 中的日志写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。而在事务执行中间过程的 redolog 都是直接写在 redolog buffer 中的,也就是说,一个没有提交的事务的 redolog,也是有可能会被后台线程一起持久化到磁盘的。
        </li>
        <li>
                第二种情况:innodb_flush_log_at_trx_commit 设置是 1,这个参数的意思就是,每次事务提交的时候,都执行 fsync 将 redolog 直接持久化到磁盘(还有 0 和 2 的选择,0 表示每次事务提交的时候,都只是把 redolog 留在 redolog buffer 中;2 表示每次事务提交的时候,都只执行 write 将 redolog 写到文件系统的 page cache 中)。举个例子,假设事务 A 执行到一半,已经写了一些 redolog 到 redolog buffer 中,这时候有另外一个事务 B 提交,按照 innodb_flush_log_at_trx_commit = 1 的逻辑,事务 B 要把 redolog buffer 里的日志全部持久化到磁盘,这时候,就会带上事务 A 在 redolog buffer 里的日志一起持久化到磁盘
        </li>
        <li>
                第三种情况:redo log buffer 占用的空间达到 redolo buffer 大小(由参数 innodb_log_buffer_size 控制,默认是 8MB)一半的时候,后台线程会主动写盘。不过由于这个事务并没有提交,所以这个写盘动作只是 write 到了文件系统的 page cache,仍然是在内存中,并没有调用 fsync 真正落盘
        </li>
</ul>
<p>
        原文链接:https://mp.weixin.qq.com/s/GqYlRorRbYcnY_YwnCKZ-g
</p>
頁: [1]
查看完整版本: 字节一面:事务还没提交的时候,Redolog 能不能被持久化到磁盘呢?