V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
WillingXyz
V2EX  ›  程序员

用从 binlog 中解析出的 id,立即向数据库(主库),会存在查不到的情况吗?

  •  
  •   WillingXyz · 2021-08-09 20:57:39 +08:00 · 2431 次点击
    这是一个创建于 1203 天前的主题,其中的信息可能已经有所发展或是发生改变。

    插入数据到数据库,然后监听 binlog,从 binlog 拿新插入的 id 查询数据库,有可能会查不到吗?

    参考下面的文章,这篇文章描述了该场景: https://www.git2get.com/av/109130438.html

    并且工作中也确实遇到过一次(没有从库)。

    上面的文章解释如下: 可以看到发起提交事务后主要经过 3 个阶段:

    1. redo 日志的 prepare 阶段,在这个阶段 innodb 会将 undo, redo 日志进行刷盘;
    2. binlog 的 prepare 和 commit 阶段,这里 binlog 的 prepare 其实什么也没有做,而在 commit 的时候刷新 binlog 到磁盘,在这个时候,其实事务是已经确定要提交了(无论后面是否发生宕机);
    3. redo 日志的 commit 阶段,这个时候会清除 undo 日志,把 redo 日志刷数据盘,也就是 mysql 的存储数据真正落库;

    他认为第二步就写入了 binlog,此时 canal 可以获取到 binlog,但事务还未提交,因为第 3 步还没执行。

    但是在我的理解中,数据是首先写到内存中的,并没有同步写入磁盘,第 3 步只是把 redo log 写入磁盘而已,不会同步写入数据到磁盘中(这里指写入 B+树中)。 所以,mysql 应该可以首先尝试从内存中获取,应该能获取到的吧。

    求大佬解答

    第 1 条附言  ·  2021-08-16 10:42:28 +08:00
    按我的理解,应该是 MVCC 原因。
    在 RC 级别下执行语句时,在语句过程中,只能查询到语句开始时已经提交的事务。
    上面因为第三步没有执行,导致事务未提交,因此查询不到。

    按理说,这种情况应该很少见,就像描述里的那篇文章说的一样,磁盘压力太大可能会导致该问题。
    并且,一般情况下,收到 binlog 后,有一整行的信息,不需要再反查数据库。
    14 条回复    2021-08-11 20:58:38 +08:00
    liprais
        1
    liprais  
       2021-08-09 21:06:38 +08:00
    跟你的隔离级别有关系
    WillingXyz
        2
    WillingXyz  
    OP
       2021-08-09 21:18:02 +08:00
    @liprais 隔离级别是 rc
    zavierx
        3
    zavierx  
       2021-08-09 22:18:23 +08:00
    我理解他的意思是 commit 步骤卡在调用 fsync 函数刷 redo 日志阶段,所以 commit 其实还没有完成。但是此时 binlog 日志已经记录了这个事务了,所以通过这个事务日志的 id 字段查不到还未 commit 完成的记录?
    Ehco1996
        4
    Ehco1996  
       2021-08-10 07:11:11 +08:00
    已经写入到 binlog 中的数据说明事务已经已经被提交了,无论 mysql 有没有落盘,这条记录都是能被查询到的

    redo/undo log 的机制就是在保证就算没落盘宕机了,也不会丢数据

    个人理解可能有不对的地方
    WillingXyz
        5
    WillingXyz  
    OP
       2021-08-10 09:31:34 +08:00
    @Ehco1996 我也是这么理解的,但怎么解释根据 id 查不到的情况呢
    jindeq
        6
    jindeq  
       2021-08-10 10:58:11 +08:00
    @WillingXyz id 是走索引的吧,索引树还没有添加这一条吧?索引跟写入记录是异步的
    WillingXyz
        7
    WillingXyz  
    OP
       2021-08-10 13:33:13 +08:00
    @jindeq 是走索引的。所以我比较困惑,即使第三步提交完成后,也不会同步写索引吧,但第三步提交后肯定可以查到的。
    simonlu9
        8
    simonlu9  
       2021-08-10 19:14:00 +08:00
    写到 binglog 不一定事务已经提交完成,mysql 是两阶段提交了,你读到的可能是 bin_log cache
    Ehco1996
        9
    Ehco1996  
       2021-08-10 19:42:32 +08:00
    @WillingXyz 你说根据 id 查询不到数据,查的是主库还是从库?
    morty0
        10
    morty0  
       2021-08-10 21:06:52 +08:00
    @simonlu9 如果 binlog 不等于事务提交完成, 那 canal 的设计岂不是是有问题的?
    cheng6563
        11
    cheng6563  
       2021-08-11 09:45:10 +08:00
    用 for update 查应该就行了吧。
    cheng6563
        12
    cheng6563  
       2021-08-11 09:49:56 +08:00
    @morty0 就算这样其实也没问题
    binlog 已经是写盘前最后一步了,这两步不原子也没办法解决。
    就算先写盘后写 binlog,也不是原子的还是可能有问题。
    想要更靠谱点大概可以在监听到修改后用 for update 查一次,可保最终一致性。
    WillingXyz
        13
    WillingXyz  
    OP
       2021-08-11 10:35:01 +08:00
    @Ehco1996 主庫
    Ehco1996
        14
    Ehco1996  
       2021-08-11 20:58:38 +08:00
    @WillingXyz 不好意思,刚看到标题是从主库读,从主库读的话是不会出现出现这种情况的,一定是事务先提交才会写 binlog (落盘)

    ----

    但是如果是订阅 mysql 的 binlog 的话那又是另外一回事了,如果 mysql 内部采用了两阶段提交( XA )的话
    是有可能先写 binlog,再 commit 的,即你的 slave 订阅者读到一行的写入之后( row write ),马上去主库查询这条 id 的记录,有可能此时 commit 还没被提交( server 的负载过大,还没来得及提交)

    具体我发现了一篇文章讲的还挺好的( https://blog.51cto.com/wangwei007/2323844
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5592 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 07:16 · PVG 15:16 · LAX 23:16 · JFK 02:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.