V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
cc959798
V2EX  ›  问与答

mysql 为什么不能仅仅用 binlog 保证不丢数据

  •  
  •   cc959798 · 2018-11-26 09:14:27 +08:00 · 4481 次点击
    这是一个创建于 2167 天前的主题,其中的信息可能已经有所发展或是发生改变。

    mysql 配置每次事务 commit binlog 都写磁盘感觉也不会丢数据,为什么要使用 redo log 一块保证 crash safe 呢?仅仅用 binlog 不可以吗

    20 条回复    2018-11-27 09:57:00 +08:00
    msg7086
        1
    msg7086  
       2018-11-26 09:27:19 +08:00
    我刚刚随便谷歌了一下,看到不少文章说这个的。你也可以试试看。
    简单说,binlog 发生在 redolog 之后。
    cc959798
        2
    cc959798  
    OP
       2018-11-26 09:49:50 +08:00
    @msg7086 是不少,我也看了一些,但是可能理解的都不太对,binlog 发生在后面这个我也知道,但是就是感觉没有 redo 也能保持不丢数据,只是不能保证未提交到事务不能恢复,但是感觉未提交到事务不恢复就不恢复呗,反正未提交
    opennet
        3
    opennet  
       2018-11-26 10:14:14 +08:00
    monsterxx03
        4
    monsterxx03  
       2018-11-26 10:24:37 +08:00
    楼主应该是认为 MySQL 每次写入数据的时候直接写入磁盘(ibd 文件), 但实际上写的是 WAL(write ahead log), 然后客户端就收到成功的返回了, 因为数据真正落地是随机写, 写 WAL 是顺序写, 要快得多. binlog 的生成应该在数据正真落地之后.

    crash 重启后, ibd 文件不一定是最新的, 需要用 WAL 里的 redo log 来恢复数据.
    cc959798
        5
    cc959798  
    OP
       2018-11-26 12:35:42 +08:00
    @monsterxx03 不是,我是说写 log,binlog 也是 log,也能恢复数据不是吗?为什么还需要 redo log 来恢复数据,仅仅是为了恢复未提交的事务吗
    cc959798
        6
    cc959798  
    OP
       2018-11-26 12:36:47 +08:00
    @opennet 看过了,但是没说为什么必须要用 redo log 和 binlog 结合方式来恢复数据,我觉得用 binlog 也是可以的
    ebony0319
        7
    ebony0319  
       2018-11-26 12:39:36 +08:00 via Android
    先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。
    但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
    然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

    先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1 ”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。
    但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
    然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

    先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1 ”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。
    但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。
    然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

    先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1 ”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。
    ebony0319
        8
    ebony0319  
       2018-11-26 12:40:30 +08:00 via Android
    我取的也是 林晓斌 的原话,已经解释非常清楚了。
    cc959798
        9
    cc959798  
    OP
       2018-11-26 14:32:35 +08:00
    @ebony0319 大佬你复制多了,这些我也是看明白了,我的意思 如果把 binlog 放在事务 commit 之前写入的话,这样也可以保证数据不丢失,比如 log 写了,然后断电,这个时候虽然没 commit,但是 commit 是最后一步了,也可以算作已经 commit 了,这样可以恢复数据,如果是 log 写之前就断电,ccommit 还没提交,就不会有新的这条数据,也会是合理的


    换句话说,我们不写日志,commit 时 直接写数据到磁盘(虽然是有性能问题),但是也是可以保证数据断电不丢失(除非磁盘坏了)
    GaryZ
        10
    GaryZ  
       2018-11-26 15:05:27 +08:00
    我觉得是数据恢复的时候是否能保持一致性的问题吧
    monsterxx03
        11
    monsterxx03  
       2018-11-26 15:23:17 +08:00
    @cc959798 大概明白你的意思了, MySQL 支持不只一种存储引擎, 各种不同的存储引擎都能开 binlog (用于获取 ongoing change, master/slave), 但 redo/undo log 是 innodb 专属的. 它们工作在不同层面上.

    如果设计一个 innodb 专属的 MySQL, 的确可以只保留一种 log, 比如 aws 的 aurora db, master/slave 之间就是通过 redo log 同步的( 当然 binlog 也可以开)
    ebony0319
        12
    ebony0319  
       2018-11-26 16:19:02 +08:00 via Android
    是不是可以这样理解。每种成功的概率是 0.9,那么两份备份失败的概率等于 1-0.1*0.1=0.99 ?
    ebony0319
        13
    ebony0319  
       2018-11-26 16:20:54 +08:00 via Android
    而且 redo log 是 Innodb 特有的。
    cc959798
        14
    cc959798  
    OP
       2018-11-26 17:23:17 +08:00
    @monsterxx03 嗯嗯,这些都知道,redo 是在存储引擎层面的,但是就是奇怪,binlog 可以实现断电恢复,为什么还要加个 redo log
    msg7086
        15
    msg7086  
       2018-11-26 17:24:32 +08:00
    binlog 没有 redo log 那么牛逼。
    redo log 是事务边跑边写的,commit 的时候只要 mark 一下就行了。binlog 得要整个事务确定 commit 或者 rollback 才会一次性写入,可靠性哪有 redo log 高。


    @ebony0319 你可以理解成 binlog 成功的概率是 0.8,而 redo log 成功的概率是 0.98 。因为有个 0.8 的 binlog 所以把 0.98 的 redo log 去掉是很得不偿失的。
    cc959798
        16
    cc959798  
    OP
       2018-11-26 17:25:46 +08:00
    @ebony0319 仅仅是这样吗?如果 binlog 每次事务都写磁盘的话理论上数据也是不会丢失的,没写成功不让 commit,这样不就可以了。难道仅仅是因为存储引擎不能控制 binlog 或者说存储引擎不关心 binlog 而作的一种妥协?
    monsterxx03
        17
    monsterxx03  
       2018-11-26 17:52:27 +08:00
    有点钻牛角尖了额, 你先做了个假设: 把 binlog 提前到 commit 之前写,然后就能用 binlog 来做 crash recover.

    问题是目前 binlog 和 redo log 的实现格式完全不同(binlog 根据你需求可选三种呢). 刷盘机制也完全不同. 虽然我不了解这块代码的实现, 但工程上硬用 binlog 来实现 crash recovery 感觉是不行的.(你想想还有 semi sync 的同步方式呢, 同步这块相当复杂)

    binlog 的设计目的是为了让外部系统获取数据库内的数据变更,可以理解成一个 expose 的接口.

    redo 是 innodb 为了达成 acid, crash recovery 的一种内部实现手段.

    存储引擎的确不能控制 binlog, 也不应该关心 binlog, 这不算妥协, 是 by design.
    monsterxx03
        18
    monsterxx03  
       2018-11-26 18:01:04 +08:00
    还有目前的 binlog 并不能实现断电恢复, binlog 可以做 point-in-time recovery 这个一般是写坏数据了用来修数据的.
    cc959798
        19
    cc959798  
    OP
       2018-11-26 18:40:48 +08:00
    @monsterxx03 嗯嗯你这么说感觉也是有道理的
    julyclyde
        20
    julyclyde  
       2018-11-27 09:57:00 +08:00
    binlog 其实是个外挂式的东西
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1859 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 16:32 · PVG 00:32 · LAX 09:32 · JFK 12:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.