如果以用户为单位上锁,你们会怎么实现这个功能? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
abcbuzhiming
V2EX    Java

如果以用户为单位上锁,你们会怎么实现这个功能?

  •  
  •   abcbuzhiming 2019-01-29 14:59:24 +08:00 6012 次点击
    这是一个创建于 2517 天前的主题,其中的信息可能已经有所发展或是发生改变。
    有这么个场景。用户日程安排,要保证时间段设定的唯一性(时间段不能冲突),所以没法在数据库层面上用唯一约束解决问题。一定得想办法在应用层解决。但是同时,每个用户的日程彼此独立,所以不会发生冲突。因此,这个场景是 一个用户可能会和自己发生资源争抢(当压力比较大的时候,用户可能提交两个一样的时间段数据到数据库去),但是用户和用户之间不会发生资源争抢的问题。因此,如果单独在方法阶段加锁,对性能损失就很大因为它对应的其实是用户争抢资源问题。而我希望的是,是针对用户本身进行加锁,也就是 A 用户如果已经提交了一次设定时间段的请求,在此次提交没处理完前,因为种种原因 A 用户再次提交了请求,那么这个请求只能等待上一次请求处理完,才能进行处理。同时,其它用户的请求不受影响
    32 条回复    2019-02-12 19:27:12 +08:00
    fkmc
        1
    fkmc  
       2019-01-29 15:16:37 +08:00
    redisson 针对用户 时间 加锁 怎么样
    没有分布式 那就 ConcurrentMap 存一个写锁
    lhx2008
        2
    lhx2008  
       2019-01-29 15:17:09 +08:00 via Android
    一般用数据库版本号 update set xxx, version=5 where vrrsion=4
    或者用 redis 锁,用 setnx 和 del 的脚本
    jimrok
        3
    jimrok  
       2019-01-29 15:18:19 +08:00
    Actor 模式下不用加锁,但我怕你去看 Akka 后,就逃掉了。
    abcbuzhiming
        4
    abcbuzhiming  
    OP
       2019-01-29 15:23:14 +08:00
    @wccc 没有看懂你的方案,如何针对时间加锁呢?
    @lhx2008 我这个问题里不光有 update,insert 也是大头,insert 时就不好用版本号解决了
    @jimrok Actor 本质是用单线程消息队列躲开了锁的问题。我不太喜好这个方案
    fkmc
        5
    fkmc  
       2019-01-29 15:30:02 +08:00
    @abcbuzhiming #4 我有一个问题 时间段大小是固定的吗?
    lihongjie0209
        6
    lihongjie0209  
       2019-01-29 15:34:01 +08:00
    使用锁, 多个线程拿到锁再操作 >>>>> 临界区单线程操作

    使用队列, 多线程写入队列作为生产者, 单线程操作数据库作为消费者 >>>>>>>> 临界区单线程操作






    剩下的就是查 API 喽
    TomVista
        7
    TomVista  
       2019-01-29 15:41:33 +08:00
    开始时间 结束时间

    用户选择开始时间,传到服务器上,这之后,用户没有选对应结束时间之前,产生了其他日程时间,

    如果新的日程包含第一个日程的开始时间,不允许用户操作.如果新的日程在第一个日程的开始时间之后,并且未接收到结束时间,提示用户去操作完第一个日程.

    另外用数据库约束实现这个需求,本身极不明智.
    abcbuzhiming
        8
    abcbuzhiming  
    OP
       2019-01-29 16:04:24 +08:00
    @wccc 当然不是固定的,固定的话就好解决多了
    abcbuzhiming
        9
    abcbuzhiming  
    OP
       2019-01-29 16:07:24 +08:00
    @TomVista 额,你可能理解错了,开始时间和结束时间是在客户端选好后一起提交服务器的,服务器要判断用户选的时间段是否和已经有的日程发生冲突。另外,主要问题是需要考虑高负载下由于后端不能很快的响应客户端,造成客户端多次提交问题
    你会用什么方式来约束呢?
    fkmc
        10
    fkmc  
       2019-01-29 16:10:10 +08:00
    @abcbuzhiming #8 我这个是针对用户级别加锁 让提需求的人去死.......
    abcbuzhiming
        11
    abcbuzhiming  
    OP
       2019-01-29 16:34:59 +08:00
    @wccc 如何加锁,我感兴趣的是这个
    timsims
        12
    timsims  
       2019-01-29 16:38:41 +08:00
    我的理解是,LZ 意思是用户会在同时多次提交相同(或不同)的日程时间段,所以现在 LZ 要解决的是时间段冲突的问题?

    那锁的粒度就是用户 id , 把所有日程提交的操作全都串行来处理可以吗?
    TomVista
        13
    TomVista  
       2019-01-29 16:52:15 +08:00
    抱歉,想不出来,坐等答案,顺便找一个前端仔祭天
    abcbuzhiming
        14
    abcbuzhiming  
    OP
       2019-01-29 16:52:19 +08:00
    @timsims 串行就是用队列,问题是不可能每个用户都给一个队列,明显是有浪费的。用队列就涉及到用几个的问题,我觉得挺烦这个,所以才想到针对用户 id 进行加锁的策略
    fkmc
        15
    fkmc  
       2019-01-29 16:53:28 +08:00
    redis 分布式锁 相关的框架 例如 Rlock
    或者本地加锁 用一个 ConcurrentMap 存一下锁 ...
    fashy
        16
    fashy  
       2019-01-29 16:54:43 +08:00
    12306 行程冲突问题
    pabupa
        17
    pabupa  
       2019-01-29 17:30:59 +08:00 via Android
    用户提交请求>查询数据库,验证是否冲突>根据验证结果,返回响应。
    不就是这么个流程吗?我理解你的意思是如何避免用户重复请求,和锁没关系吧。
    那:
    让前段在没有相应之前禁用按钮;后台在查询数据库这一步做缓存。
    是这么个道理吧……⊙⊙
    fkmc
        18
    fkmc  
       2019-01-29 17:33:17 +08:00
    @pabupa #17 验证冲突的时候,需要加锁,
    后端还是要做验证的 无论前端做不做验证
    timsims
        19
    timsims  
       2019-01-29 17:34:50 +08:00
    @abcbuzhiming 不用上队列,拿不到锁的操作可以等待,等待超时失败就让它失败咯,让用户重新提交就好

    另外用队列的方案,怎么会想到每个用户给一个队列,所有用户公用一个队列也可以啊, 如果你有多队列的话,同一个用户都分配在相同的队列也能保证串行
    zy445566
        20
    zy445566  
       2019-01-29 17:36:59 +08:00
    你这个没必要加锁,想办法搞用队列成顺序执行吧,如果是 nodejs 队列都能剩了,这样性能消耗会小很多
    fkmc
        21
    fkmc  
       2019-01-29 17:37:05 +08:00
    @timsims #19 单个队列 串行的化 还不如直接一个全局的锁吧.....
    pabupa
        23
    pabupa  
       2019-01-29 17:44:40 +08:00 via Android
    @wccc (⊙o⊙)哦,,我的
    prolic
        24
    prolic  
       2019-01-29 17:46:21 +08:00
    hash 分桶,打到队列或者机器内,然后串行就行了
    guyeu
        25
    guyeu  
       2019-01-29 18:18:21 +08:00   1
    这个显然应该是串行化队列来处理,当然没必要每个用户一个队列。了解一下一致性哈希,把同一用户的所有操作扔到同一个队列去处理就行了。
    limuyan44
        26
    limuyan44  
       2019-01-29 18:29:57 +08:00 via Android
    怎么感觉是防重复提交的问题
    fishfisher
        27
    fishfisher  
       2019-01-30 11:48:03 +08:00
    @fashy12306 也有这个问题,我同时用多个抢票软件去提交订单,应该就是并发了,抢到了两张时间冲突的票,
    用一个软件就只有行程冲突的提示。。。
    fishfisher
        28
    fishfisher  
       2019-01-30 11:50:43 +08:00
    @fanshy ,,,12306 也有这个问题,我同时用多个抢票软件去提交订单,应该就是并发了,抢到了两张时间冲突的票,
    用一个软件就只有行程冲突的提示。。。
    fishfisher
        29
    fishfisher  
       2019-01-30 11:52:37 +08:00
    @fashy 晕,手残老是 @不到正确的人
    deming
        30
    deming  
       2019-01-30 12:14:08 +08:00
    锁的粒度控制在用户身上就可以,多个用户同时进来,互不影响,因为锁的粒度在 userId 身上。
    利用工具将 一个 userId 并发进来的时候,因为锁的存在也是互斥的。伪代码:
    ```
    addTask(param...){
    long userId = getUserId();

    //get lock
    lockValue=genLockValue();
    if(!getLock(userId,lockValue)){
    return "some message";
    }
    //do business
    ...
    releseLock(userId,lockValue);

    }


    getLock(userId,lockValue){
    int retryTime=5;
    do{
    retryTime--;
    redis.setNx(genKey(userId),lockValue);
    }while(retryTime>0);
    }
    releseLock(userId,lockValue){
    释放锁的时候判断 key 是这个 key,lockValue 也是这个我的 lockValue 才释放。
    否则会错误释放。
    }
    ```
    abcbuzhiming
        31
    abcbuzhiming  
    OP
       2019-01-31 14:09:47 +08:00
    @deming 你的想法就是我的想法。不过,一定非得上 redis 才能实现这个功能? Java 自己的锁搞不定?
    PazuLee
        32
    PazuLee  
       2019-02-12 19:27:12 +08:00
    拆分成两个问题:
    1. 重复提交:增加 token 机制解决,每次提交校验 token 是否相同;前段增加等待的提示,实现方式比较多。
    2. 1 之后,剩下问题是如何判断历史 N 条数据与当前新插入数据是否重叠,如果存储只有 MySQL,则必须取出来吧?如果记录数较大,DB 扛不住,可以考虑在缓存中维护一份 user 级别的已提交时间的数据,保存条记录中的 start_time & end_time

    以上~应该能解决了吧
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2482 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 38ms UTC 05:48 PVG 13:48 LAX 21:48 JFK 00:48
    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