请教分布式下如何用锁确保更新不丢失? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
请不要在回答技术问题时复制粘贴 AI 生成的内容
supuwoerc

请教分布式下如何用锁确保更新不丢失?

  •  
  •   supuwoerc Aug 28, 2024 2027 views
    This topic created in 610 days ago, the information mentioned may be changed or developed.

    场景是这样的,我想要用 golang 的 NewSingleHostReverseProxy 来代理一个第三方系统的接口,然后在拿到接口响应的时候记录这次调用到数据库(按照用户每天每个资源类型来统计次数),程序部署到多个节点上,请求记录表大概是这样子,我需要确保更新不丢失,数据表大概是下面这样,因为我没做过高并发和分布式场景下的业务,我想的是表里扩展 version 来用乐观锁来处理,更新失败的话重试,但是重试也不知道要重试几次,重试失败应该怎么办,请教大家最好用那种方式?

    更新/插入逻辑(伪代码):

     err := dao.DB.Where("`uid` = ? AND `date` = ? AND `type` = ?", uid, today, type).FirstOrInit(&record).Error if err != nil { return err } if record.RequestKey != "" { record.RequestCount++ } else { record.RequestCount = 1 } return dao.DB.Save(&record).Error 

    表大致的字段(伪代码):

    CREATE TABLE `sys_request_record` ( `uid` varchar(32) NOT NULL COMMENT '用户 uid', `type`int(11) NOT NULL COMMENT '请求资源类型', `date` date NOT NULL COMMENT '日期', `request_count` int(11) NOT NULL DEFAULT '0' COMMENT '请求次数', `created_at` bigint(20) NOT NULL DEFAULT '0' COMMENT '创建时间', `updated_at` bigint(20) NOT NULL DEFAULT '0' COMMENT '更新时间', `deleted_at` bigint(20) NOT NULL DEFAULT '0' COMMENT '删除时间', PRIMARY KEY (`uid`,`type`,`date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 
    4 replies    2024-08-29 14:21:16 +08:00
    fengYH8080
        1
    fengYH8080  
       Aug 28, 2024   1
    这种场景我不去搞数据库持久化层,我弄分布式锁去锁线程,一般用 Redlock 。
    你这个功能锁的 key 会定义为 uid_type_date ,然后网络服务中请求没有抢到锁且超过重试次数后就拒绝这次请求,返回系统繁忙之类的。而你这个场景的差别就是必须要重试到成功为止,可以加个 redis 做缓存层去搞重试,先记录到缓存中,保证缓存 get set 原子性就可以不用去在意重试多少次,失败的同时又有请求过来就一直往缓存加次数就好,成功就清 0 ,甚至数据实时性不高还可以定时处理,减轻数据库负担。
    原理就是这样,里面一下细节就需要自己斟酌了,例如缓存 key 的定义,时间交叉点的问题。
    csys
        2
    csys  
       Aug 28, 2024   1
    1. 不要用乐观锁去应对高并发场景
    2. 尽量不要用锁去应对高并发场景
    3. 尽量追加数据而非更新数据

    几个方案:
    1. SQL: Update `sys_request_record` SET `request_count`=`request_count` + 1;
    2. redis: INCR sys_request_record:$uid:$type
    3. 每次调用时发送可持久化的消息(如 kafka ),订阅消息进行统计

    你不可能保证更新绝对不丢失
    MoYi123
        3
    MoYi123  
       Aug 28, 2024   1
    一般是这样搞的.
    1. 把完整的请求通过消息队列存到 OLAP 数据库里, 在离线的情况下用 count(*) group by 查.
    2. 在线数据直接对数据库 update x = x + 1 where ...
    3. 如果数据量太大, update 性能不行, 就本地或者 redis 里存一下, 攒个 n 次或者 5s 的定时器触发
    一次性 update x = x + n
    4. 因为有离线数据库, 可以比较在线库是不是漏数据了, 有 bug 就找出来修一下, 一般就是服务器重启的时候丢的.
    wangliran1121
        4
    wangliran1121  
       Aug 29, 2024   1
    我倾向于 @csys 的方案 3 ,本身你这个业务场景要上锁就很难理解。。。
    About     Help     Advertise     Blog     API     FAQ     Solana     1235 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 52ms UTC 17:28 PVG 01:28 LAX 10:28 JFK 13:28
    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