同时写文件和数据库,如何保证数据一致性? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
maxxfire
V2EX    程序员

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

  •  1
     
  •   maxxfire 2019-06-25 21:02:29 +08:00 7410 次点击
    这是一个创建于 2304 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有一个事务需要做 2 件事:在文件系统中存储一个文件,同时在数据库生成一条记录。
    两者没有先后顺序的要求,但是要满足要么都成功,要么都失败。
    怎么才能做到这样,满足一致性(比如在突然断电情况下,也不影响结果),或者有什么措施能够尽量使数据可靠?
    38 条回复    2024-09-06 11:40:44 +08:00
    sun1991
        1
    sun1991  
       2019-06-25 21:12:14 +08:00
    考虑数据一致性, 何不把文件存到数据库里去?
    iwong0exv2
        2
    iwong0exv2  
       2019-06-25 21:15:19 +08:00 via Android
    @sun1991 还要考虑查询啊
    ace12
        3
    ace12  
       2019-06-25 21:17:03 +08:00
    版本
    des
        4
    des  
       2019-06-25 21:19:04 +08:00 via Android
    是否有竞争、读写频率、不一致的接受程度、是否可以删除不说一下?
    corvofeng
        5
    corvofeng  
       2019-06-25 21:19:40 +08:00 via Android
    只存到数据库, 定时刷到文件里面, 类似缓存的用法不行吗?
    maxxfire
        6
    maxxfire  
    OP
       2019-06-25 21:22:21 +08:00
    @des 单线程操作,可以删除,性能要求不高
    iwong0exv2
        7
    iwong0exv2  
       2019-06-25 2:36:41 +08:00 via Android
    可参考 4 楼的思路,需要楼主自己完善下。
    另外再给个也许可行的方案:
    - 先写文件,再插入数据库。
    - 两个操作都成功才算成功;其中一个失败就回滚。
    - 读的时候以数据库表记录为准,数据库没有记录就算无效数据。
    Vegetable
        8
    Vegetable  
       2019-06-25 21:41:53 +08:00
    @iwong0exv2 +1

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

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

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

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

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

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

    请教下,大家一般怎么处理的?
    husinhu
        35
    husinhu  
       2019-06-26 20:01:47 +08:00 via iPhone
    两个操作对应两个 fsync() 所以本质上要把这两个操作一起 commit。即便是 TxF 也不能保证能一起 rollback。要是我做就简单的 db as log,加一个 column 叫 filetxid,每次写完就写一行就 append 文件以 filetxid 作为前缀写完 fsync()落物理存储。每次恢复以 db 为准,如果文件末尾损坏做 replay。
    husinhu
        36
    husinhu  
       2019-06-26 20:04:25 +08:00 via iPhone
    楼主是要存储文件不是写文件,我看错了嘿嘿,不过方法类似,db as wal log
    troywinter
        37
    troywinter  
       2019-06-27 20:52:29 +08:00
    TCC,最终一致性,2pc 对业务不透明而且实现难度大,https://queue.acm.org/detail.cfm?id=1394128 参考 BASE 模型
    chaleaochexist
        38
    chaleaochexist  
       2024-09-06 11:40:44 +08:00
    大佬遇到了同样的问题, 请问最后是如何解决的?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5419 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 59ms UTC 08:46 PVG 16:46 LAX 01:46 JFK 04:46
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86