笔记 笔记
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

EasT-Duan

Java 开发
首页
  • 开发工具
  • Java Web
  • Java 进阶
  • 容器化技术
  • Java 专栏

    • Java 核心技术面试精讲
    • Java 业务开发常见错误 100 例
  • 数据库专栏

    • MySQL 实战 45 讲
    • Redis 核心技术与实战
  • 安全专栏

    • OAuth 2.0 实战课
  • 计算机系统
  • 程序设计语言
  • 数据结构
  • 知识产权
  • 数据库
  • 面向对象
  • UML
  • 设计模式
  • 操作系统
  • 结构化开发
  • 软件工程
  • 计算机网络
  • 上午题错题
在线工具 (opens new window)

购买兑换码请添加

添加时候请写好备注,否则无法通过。

  • 23-MySQL是怎么保证数据不丢的?

    • binlog 的写入机制
      • redolog 的写入机制
        • 面试题
        EasT-Duan
        2024-03-20
        随笔
        目录

        23-MySQL是怎么保证数据不丢的?

        欢迎来到我的 ChatGPT 中转站,极具性价比,为付费不方便的朋友提供便利,有需求的可以添加左侧 QQ 二维码,另外,邀请新用户能获取余额哦!最后说一句,那啥:请自觉遵守《生成式人工智能服务管理暂行办法》。

        正文

        # binlog 的写入机制

        事务执行过程中,先把日志写入到 binlog cache 中,事务提交后再把 binlog cache 写入到 binlog。无论这个事务有多大都要保证一次性写入。

        binlog cache 是系统给分配的内存空间,每个线程一个。binlog_cache_size 是控制每个线程分配的 binlog cache 大小。如果超过这个数值就要暂存到磁盘。但是中间有个过程。

        • 首先 binlog cache 会将数据 write 到 page cache 中。

          笔记

          page cache 是操作系统用于缓存磁盘数据的一种机制。当应用程序需要读取磁盘上的数据时,操作系统会先查看这些数据是否已经在 page cache 中。如果在,就直接从内存中读取,这比从磁盘读取要快得多,同样,当应用程序需要写入数据到磁盘时,操作系统通常会先将数据写入到 page cache,然后在适当的时候再将数据从 page cache 写入到磁盘。

        • 然后在适当的时机 fsync 到磁盘,这个过程是要消耗 IO 的。

        这个 write 和 fsync 的时机判定是由参数 sync_binlog 来控制的。

        • sync_binlog = 0:代表每次提交的事务只 write,而不 fsync。
        • sync_binlog = 1:代表每次提交的事务都会 fsync。
        • sync_binlog = N:write N 次后进行一次 fsync。

        一般来说这个值设置在 100-1000 之间,不要设置成 0,一旦设置成 0,写入磁盘的时机就完全依赖于系统了,这种虽然能够很大限度上提高 IO 但是一旦崩溃,就会造成事务丢失。设置成 N 和这个道理也是一样的。

        # redolog 的写入机制

        redo log 的日志是要先写入到 redo log buffer 中的,而 redo log buffer 并不是每一次生成后都写入到磁盘中。

        也不怕丢失,因为事务没有提交,即使丢失了也无所谓。

        那反过来说却有些不同,如果事务没有提交,也会有一部分数据被持久化到磁盘中。

        redo log 也有三种状态

        1. 存在 redo log buffer 中。
        2. write,但没有持久化,也就是写到 page cache 里。
        3. 持久化到磁盘中。

        innodb_flush_log_at_trx_commit 参数

        • 为 0 的时候每次事务提交都存在 redolog buffer 中。
        • 为 1 的时候每次事务提交都将 redolog 持久化到磁盘。
        • 为 2 的时候表是每次提交都把 redolog 写入到 page cache。

        说回没提交的事务被写入到磁盘的问题,innodb 后台有一个线程,每隔一秒会把 redo log buffer 中的日志,调用 write 写到文件系统的 page cache,然后调用 fsync 持久化到磁盘。所有的 redolog 都是写入在 redolog buffer 中的,所以会出现这种情况。

        还有两种方式也会让没提交的事务被写入到磁盘

        1. redo log buffer 占用即将达到 innodb_log_buffer_size 的一半的时候,后台线程会主动写盘(到 page cache)。
        2. 并行任务提交的时候,顺带将这个事务的 redolog buffer 持久化到磁盘。比如线程 A 在事务里写入了一些数据到 redolog buffer,innodb_flush_log_at_trx_commit 设置的值为 1,这个时候线程 B 正好提交了事务,那么就会立刻将 redolog buffer 中的数据写入到磁盘,会连带着 A 的没提交的数据一起。

        由于两阶段提交,redolog 会先 prepare,然后在写 binlog,在最后把 redo log commit 了。

        但如果把 innodb_flush_log_at_trx_commit 设置为 1 ,那么 redo log 在 prepare 阶段就会持久化,因为崩溃恢复要依赖于这个逻辑。

        如果说崩溃恢复后看到 redo log 中有 commit 的标识就说明已经提交了不用管。

        如果只有完整的 prepare,就需要根据 binlog 的日志来进行判断,如果 binlog 中有完整的事务信息,那么就会根据 redo log 回滚所有未完成的事务,并使用 binlog 来执行已提交但未完成的事务了。

        就是根据这个特性,innodb 认为可以不用每次提交就写盘,只用写入 page cache 就能完成了。

        双 1 策略就是 ync_binlog 和 innodb_flush_log_at_trx_commit 都设置成 1,即一个完整事务提交之前需要等待两次刷盘,一次是 redo log prepare 阶段,一次是 binlog 落盘。

        如果 MySQL 的 TPS 能够跑到 2 万的话岂不是每秒就有 4 万次写磁盘?

        并不是,Mysql 会用到组提交的机制。

        首先要明确的是 LSN(日志逻辑序号),LSN 是递增的,在每个事务提交的是时候才会生成,保证事务的顺序性。

        比如多个并发事务都 prepare 了,第一个提交的事务 LSN 为 50,当第一个事务要去 redo log fsync 的时候这个组里已经有三个事务了,LSN 也变成了 150 了,那么事务 1 去写盘的时候带的 LSN 就是 150 了,等于 150 之前的事务都会被持久化到磁盘。

        所以如果 fsync 越晚,写的次数就越少。

        并且 Mysql 这里也用到了一个优化,之前说过,整个流程是

        但是这里的写 binlog 也是个动作,而真正的是

        • binlog 先 write 到 page cache。

        • 然后再 fsync。

        这里做的优化就是 redolog 的 fsync 会推到到 binlog write 之后,以等待更多的事务写入,然后将积累的数据一起 fsync。

        binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 可以来修改 binlog 组提交的效果。

        • binlog_group_commit_sync_delay:表示延迟多少微秒后才调用 fsync。
        • binlog_group_commit_sync_no_delay_count:表示积累多少次后才调用 fsync。

        注意

        切记这里是或的关系,二者满足一就会 fsync。所以如果 binlog_group_commit_sync_delay 设置为 0 那么 binlog_group_commit_sync_no_delay_count 就会失效。

        从上面的概念也能窥探出 WAL 机制的优化了,看似写了两个日志,实则是 binlog 和 redolog 都是顺序写,本身就比随机写要快很多,加上组提交两者大量的减少了 IO 的操作。

        # 面试题

        Q:如果你的 MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

        A:针对这问题,可以考虑一下方案。

        1. 设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 的参数,就是故意让写入等待,减少写盘次数。
        2. 将 sync_binlog 设置为 N,或者将 innodb_flush_log_at_trx_commit 设置为 2,就是让他们多写 page cache,但是都有丢数据的风险。

        Q:执行一个 update 语句以后,我再去执行 hexdump 命令直接查看 ibd 文件内容,为什么没有看到数据有改变呢?

        A:因为采用 WAL 机制,可能只写完了 redo log 或者 buffer 里,或者写入到了 page cache 里但还没有 fsync 呢。所以看不到。

        Q:为什么 binlog cache 是每个线程自己维护的,而 redo log buffer 是全局共用的?

        A:因为 binlog 中记录的是一个事务完整的执行语句,用来做主从备份,所以如果出现混乱可能导致事务的不一致性,然而 redolog 不需要,因为每个事务都有自己的标识比如 LSN。即使出现并发问题也没关系。

        Q:事务执行期间,还没到提交阶段,如果发生 crash 的话,redo log 肯定丢了,这会不会导致主备不一致呢?

        A:不会,因为二阶段提交,如果这个 redo log 已经丢失,那么 binlog 的日志还在 binlog buffer 中,还没有 fsync 呢所以备库不会不一致。

        Q:如果 binlog 写完盘以后发生 crash,这时候还没给客户端答复就重启了。等客户端再重连进来,发现事务已经提交成功了,这是不是 bug?

        A:不是,如果 binlog 已经写完后崩溃了,说明这个事务已经提交了,只是客户端没有收到成功的消息,但在逻辑上是已经成功了,所以恢复后看到修改成功了,是对的。

        #MySQL
        上次更新: 2025/04/12, 05:37:39
        最近更新
        01
        Reactor 核心
        02-24
        02
        前置条件
        10-30
        03
        计算机网络
        09-13
        更多文章>
        Theme by Vdoing | Copyright © 2019-2025 powered by Vdoing
        • 跟随系统
        • 浅色模式
        • 深色模式
        • 阅读模式