• 请不要在回答技术问题时复制粘贴 AI 生成的内容
maxxfire
V2EX  ›  程序员

同时写文件和数据库,如何保证数据一致性?

  •  1
     
  •   maxxfire · Jun 25, 2019 · 8348 views
    This topic created in 2693 days ago, the information mentioned may be changed or developed.
    有一个事务需要做 2 件事:在文件系统中存储一个文件,同时在数据库生成一条记录。
    两者没有先后顺序的要求,但是要满足要么都成功,要么都失败。
    怎么才能做到这样,满足一致性(比如在突然断电情况下,也不影响结果),或者有什么措施能够尽量使数据可靠?
    38 replies  •  2024-09-06 11:40:44 +08:00
    sun1991
    sun1991
    Jun 25, 2019
    1
    考虑数据一致性, 何不把文件存到数据库里去?
    iwong0exv2
    iwong0exv2
    Jun 25, 2019 via Android
    2
    @sun1991 还要考虑查询啊
    ace12
    ace12
    Jun 25, 2019
    3
    版本
    des
    des
    Jun 25, 2019 via Android
    4
    是否有竞争、读写频率、不一致的接受程度、是否可以删除不说一下?
    corvofeng
    corvofeng
    Jun 25, 2019 via Android
    5
    只存到数据库, 定时刷到文件里面, 类似缓存的用法不行吗?
    maxxfire
    maxxfire
    OP
    Jun 25, 2019
    6
    @des 单线程操作,可以删除,性能要求不高
    iwong0exv2
    iwong0exv2
    Jun 25, 2019 via Android
    7
    可参考 4 楼的思路,需要楼主自己完善下。
    另外再给个也许可行的方案:
    - 先写文件,再插入数据库。
    - 两个操作都成功才算成功;其中一个失败就回滚。
    - 读的时候以数据库表记录为准,数据库没有记录就算无效数据。
    Vegetable
    Vegetable
    Jun 25, 2019
    8
    @iwong0exv2 +1

    你直接考虑查询,实际上查不到就意味着操作是失败的,查得到就是成功的.所以你只需要保证数据库里的记录,都有对应的文件,而不需要严格保证每个文件都对应了数据库的条目.
    只有这样你才能保证突然断电了也不会出现查询的时候出错,因为写数据库是最后一步.

    但是如果是对现有文件进行修改的话就比较麻烦咯
    linbiaye
    linbiaye
    Jun 25, 2019❤️1
    9
    1.计算文件 md5 ; 2.记日志(比如某张表中插入一条记录包含 md5, filepath ;或者日志文件 -> md5 为文件名,内容 filepath ); 3. 写文件; 4. 写文件成功后写表; 5. 如果 2 个写都成功或者任意一个不成功则删除日志。6. 系统恢复或者重启后检查日志,如果文件 md5 值不匹配认为则删除脏数据,若匹配则跳转到第 4 步或者跳转到第 5 步。
    carlclone
    carlclone
    Jun 25, 2019
    10
    这完全就是 mysql 里 redolog 和 binlog 的场景 , 两阶段提交
    mysql 写完了先处于 prepare 状态 , 如果文件系统写入成功了则提交 , 没成功事务就回滚了
    GavinAlison
    GavinAlison
    Jun 25, 2019
    11
    @linbiaye +1

    1. 计算文件的 md5
    2. 记录操作日志,比如这条记录的信息,包括文件的 md5, filename, filePath, meta 信息
    3. 开始写入数据库,先记录入库的日志,写入数据库中,利用数据库的事物保证写入成功,失败记录失败日志
    4. 开始写入文件,先记录写文件日志,在写入文件,更具 md5 值,从 mongodb 中查询对应的数据,如果有删除,排除中断上传的问题,重新上传, 失败重试,记录写入重试次数,败次数超过 5 次,写入失败日志
    5. 成功之后,返回主程序,记录成功日志。
    ihciah
    ihciah
    Jun 25, 2019
    12
    2PC
    vindurriel
    vindurriel
    Jun 26, 2019 via iPhone
    13
    begin; insert; err=writeFile(); if err then rollback; else commit;
    wweir
    wweir
    Jun 26, 2019 via Android
    14
    强一致是一定做不到,只能考虑最终一致。这样的话,写个事后检查就可以了
    wweir
    wweir
    Jun 26, 2019 via Android
    15
    不得不说,不懂装懂的好多……
    jorneyr
    jorneyr
    Jun 26, 2019
    16
    写之前保存一个开始事务的标记,都成功后再保存一个操作成功的记录,如果失败就回滚 (回滚逻辑根据你的业务逻辑来实现),和分布式事务差不多一个道理。
    Huelse
    Huelse
    Jun 26, 2019
    17
    一般选择单线程,先文件后数据库,成功标记可选择返回或者数据库记录。也可不返回,进行事后检查确认两者
    rrfeng
    rrfeng
    Jun 26, 2019
    18
    要看写文件是什么场景,如果是对象存储,只有添加新文件而没有更新操作,那么完全可以写文件然后写数据库,因为多写了文件并不影响一致性(多了一些没有索引的文件而已,而且如果重新上传被覆盖也没问题)

    如果要考虑删除、更新文件操作,那么就不一样了,2pc 或者其他
    leon0318
    leon0318
    Jun 26, 2019 via Android
    19
    @wweir 老哥的方案是?
    opengps
    opengps
    Jun 26, 2019
    20
    先把文件写成功在把结果存数据库会不会太慢?
    文件一般是允许多不能少,数据库则要求尽可能一一对应
    janxin
    janxin
    Jun 26, 2019
    21
    同时写缓存和数据库,怎么保证一致性?
    jaskle
    jaskle
    Jun 26, 2019 via Android
    22
    事务回滚,写完文件再提交,写文件用写完后移动改名的方法,瞬间完成,若是失败回滚即可。但是要注意,频繁断电无法保证,甚至提交到数据库的内容都可能会丢失,经历过。
    linyinma
    linyinma
    Jun 26, 2019
    23
    Pos 机冲正概念应该你能用到: ( 1 )写冲正文件;( 2 )做些事情( A、B、C...);( 3 )删除冲正文件;

    ( 1 )做事情前修需要检测冲正文件,文件存在就要回滚(防止上一次掉电等原因事务未做完);
    ( 2 )事务完整就删冲正文件;
    BBCCBB
    BBCCBB
    Jun 26, 2019
    24
    用 2pc, tcc 这种思路。
    petelin
    petelin
    Jun 26, 2019 via iPhone
    25
    Wal 日志
    tabris17
    tabris17
    Jun 26, 2019
    26
    预写日志,失败回滚
    Mirana
    Mirana
    Jun 26, 2019
    27
    写数据库失败了 需要删除文件?
    Mitt
    Mitt
    Jun 26, 2019 via iPhone
    28
    @wweir 你哪句话里看到说要强一致性了
    prasanta
    prasanta
    Jun 26, 2019 via Android
    29
    只能保证最终一致性,看你们的不一致时间窗口容忍度
    IsaacYoung
    IsaacYoung
    Jun 26, 2019
    30
    single source of truth
    anyuhanfei
    anyuhanfei
    Jun 26, 2019
    31
    这难道不是类似上传图片的基本操作?在逻辑层做判断呗
    MeteorCat
    MeteorCat
    Jun 26, 2019 via Android
    32
    文件一致性用文件的 hash 值就行了
    DonaldY
    DonaldY
    Jun 26, 2019
    33
    话说计算文件 md5,不得把文件读取到服务器本地嘛,或者分块计算?
    DonaldY
    DonaldY
    Jun 26, 2019
    34
    歪楼。

    话说计算文件 md5,不得把文件读取到服务器本地嘛,或者分块计算?

    请教下,大家一般怎么处理的?
    husinhu
    husinhu
    Jun 26, 2019 via iPhone
    35
    两个操作对应两个 fsync() 所以本质上要把这两个操作一起 commit。即便是 TxF 也不能保证能一起 rollback。要是我做就简单的 db as log,加一个 column 叫 filetxid,每次写完就写一行就 append 文件以 filetxid 作为前缀写完 fsync()落物理存储。每次恢复以 db 为准,如果文件末尾损坏做 replay。
    husinhu
    husinhu
    Jun 26, 2019 via iPhone
    36
    楼主是要存储文件不是写文件,我看错了嘿嘿,不过方法类似,db as wal log
    troywinter
    troywinter
    Jun 27, 2019
    37
    TCC,最终一致性,2pc 对业务不透明而且实现难度大,https://queue.acm.org/detail.cfm?id=1394128 参考 BASE 模型
    chaleaochexist
    chaleaochexist
    Sep 6, 2024
    38
    大佬遇到了同样的问题, 请问最后是如何解决的?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   4513 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 109ms · UTC 03:04 · PVG 11:04 · LAX 19:04 · JFK 22:04
    ♥ Do have faith in what you're doing.