V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
TomVista
V2EX  ›  问与答

没有锁,事务,如何保证+1 操作

  •  
  •   TomVista · 2020-12-03 16:30:40 +08:00 · 2386 次点击
    这是一个创建于 1451 天前的主题,其中的信息可能已经有所发展或是发生改变。

    订单表里有个字段 count, 表示用户的第几个订单

    在没有锁,事务的前提下,

    新增订单的时候怎么保证 count 是正确的.

    不要问为啥没有锁和事务,问就祭天,

    第 1 条附言  ·  2020-12-03 17:25:20 +08:00

    支持事务,

    但是事务的隔离级别未知,

    事务内部上下文隔离

    select count form ;
    
    insert count ; // 这里拿不到上一句的count
    
    

    好像用处不太大

    第 2 条附言  ·  2020-12-04 09:02:14 +08:00
    所有的数据库操作都是在前端使用 http 进行请求,所以有些后端能用的东西,到这里就不合适了,
    目前看到的队列和唯一索引比较合适,

    乐观锁和 cas, 因为 count 需要前端额外查询一遍,也没办法保证业务的正确性
    39 条回复    2020-12-04 17:11:24 +08:00
    konar
        1
    konar  
       2020-12-03 16:36:48 +08:00
    if (insertSucceeded)
    set count = count + 1
    konar
        2
    konar  
       2020-12-03 16:37:57 +08:00
    @konar 哦 count 在订单表不是用户表啊,那忽略吧
    oott123
        3
    oott123  
       2020-12-03 16:47:06 +08:00 via Android   ❤️ 2
    unique(user_id, count)
    这样至少不会重复
    TomVista
        4
    TomVista  
    OP
       2020-12-03 17:03:34 +08:00
    @oott123

    这个可行,

    select count(*) form order where order.user = user;

    insert count to order;

    大不了冲突几次,反正都没有事务 锁了,

    我这里是能执行数据库语句的,然后写个文档备注一下.

    感谢老哥,如果没有更好的方案就这个了
    duwan
        5
    duwan  
       2020-12-03 17:15:27 +08:00
    串行执行被
    tabris17
        6
    tabris17  
       2020-12-03 17:19:21 +08:00   ❤️ 1
    user,count 加唯一索引
    TomVista
        7
    TomVista  
    OP
       2020-12-03 17:20:17 +08:00
    @duwan

    我这里 请求部分 只能做到,发个 request, 等着 response, 没有办法串行. 条件不允许.
    haoz1w0w
        8
    haoz1w0w  
       2020-12-03 17:29:05 +08:00
    非要用 count 么 用插入时间倒排不就算出来了
    haoz1w0w
        9
    haoz1w0w  
       2020-12-03 17:29:21 +08:00
    哦 是正排
    fish267
        10
    fish267  
       2020-12-03 17:30:43 +08:00
    放 redis? 我瞎说的
    tabris17
        11
    tabris17  
       2020-12-03 17:33:17 +08:00
    还有个笨办法,分两步操作:

    先 insert,count 字段留空;
    然后再 update orderTab set count=(select count(*) from orderTab where user_id=? and id<=?) where id=?
    TomVista
        12
    TomVista  
    OP
       2020-12-03 17:44:08 +08:00
    @haoz1w0w emm,不合适

    insert order

    select count

    update order_count

    这个好像不太保准,有小概率,count 丢失,然后回滚和异常处理,目前这个技术栈也没法处理,unique(user_id, count) 要稳妥,方便
    TomVista
        13
    TomVista  
    OP
       2020-12-03 17:45:16 +08:00
    @fish267 不行,就我一个菜鸡,绝不给自己增加工作量
    TomVista
        14
    TomVista  
    OP
       2020-12-03 17:45:58 +08:00
    @tabris17

    #12

    这个不太妥
    zzzmh
        15
    zzzmh  
       2020-12-03 17:47:08 +08:00
    我一直用 update table set count = count + 1
    不过据说也不是最稳的
    Immortal
        16
    Immortal  
       2020-12-03 17:49:51 +08:00
    这不是乐观锁就可以实现的么
    TomVista
        17
    TomVista  
    OP
       2020-12-03 17:53:11 +08:00
    @Immortal 选技术栈做不到锁,就很尴尬,还有一个半残的事务机制,
    yao978318542
        18
    yao978318542  
       2020-12-03 18:26:43 +08:00
    redis 队列
    tabris17
        19
    tabris17  
       2020-12-03 18:30:39 +08:00 via iPhone
    @TomVista 两步走是最稳妥的方法,唯一索引插入冲突还要做异常处理,更加麻烦
    redtea
        20
    redtea  
       2020-12-03 18:36:57 +08:00
    这个字段存数据库有什么用?我感觉显示时可以实时算出来。
    退货、交易关闭场景考虑到了没有。
    Immortal
        21
    Immortal  
       2020-12-03 18:41:42 +08:00   ❤️ 1
    @TomVista #17
    建议查询下乐观锁在数据库中的应用
    TomVista
        22
    TomVista  
    OP
       2020-12-03 18:46:10 +08:00
    @Immortal 确实可行
    rambo92
        23
    rambo92  
       2020-12-03 18:46:20 +08:00   ❤️ 1
    试试无锁思想:CAS?

    `update xx set count = count + 1 where id = xxx count = excepted_count`
    如果 update 行数不等于 1, 重试该动作,直到成功
    TomVista
        24
    TomVista  
    OP
       2020-12-03 18:47:08 +08:00
    @tabris17 我的想法是 唯一冲突 后,报个网络错误,然后用户重新点一次
    TomVista
        25
    TomVista  
    OP
       2020-12-03 18:47:51 +08:00
    @redtea 和订单的自增 id 一起生成 订单的 流水号
    TomVista
        26
    TomVista  
    OP
       2020-12-03 18:59:30 +08:00
    @rambo92 我搞不定这个
    TomVista
        27
    TomVista  
    OP
       2020-12-03 19:29:04 +08:00
    @rambo92 我这边每次数据库改动都是一个 http 请求 , 我仔细研究了一下,我这个场景不合适用这个,
    sampeng
        28
    sampeng  
       2020-12-03 19:32:18 +08:00 via iPhone
    扔队列,消费者交叉执行。始终只有一个人在 insert 或者 update
    cxshun
        29
    cxshun  
       2020-12-03 19:35:42 +08:00
    没有锁的情况下只有用 CAS 了,先查询原 count,再更新 count 的同时判断目前的 count 是不是等于之前的 count 。
    wellsc
        30
    wellsc  
       2020-12-03 19:46:46 +08:00 via iPhone
    Cas,跳表,日志数据库
    seth19960929
        31
    seth19960929  
       2020-12-03 20:25:13 +08:00 via Android
    这不明显是用乐观锁吗😮
    rambo92
        32
    rambo92  
       2020-12-03 21:57:59 +08:00 via Android
    @TomVista 和是否 http 请求无关啊。
    TomVista
        33
    TomVista  
    OP
       2020-12-04 08:47:59 +08:00 via Android
    @rambo92 那可能我理解错了,我再看看
    Immortal
        34
    Immortal  
       2020-12-04 09:36:34 +08:00
    “乐观锁和 cas, 因为 count 需要前端额外查询一遍,也没办法保证业务的正确性”
    select for update
    老哥知识面还有待拓宽
    rambo92
        35
    rambo92  
       2020-12-04 10:43:12 +08:00
    "所有的数据库操作都是在前端使用 http 进行请求,所以有些后端能用的东西,到这里就不合适了,
    目前看到的队列和唯一索引比较合适,

    乐观锁和 cas, 因为 count 需要前端额外查询一遍,也没办法保证业务的正确性"

    大家都散了吧。。。
    TomVista
        36
    TomVista  
    OP
       2020-12-04 10:57:14 +08:00
    @Immortal 明白了,我后续再去看看,哪里理解错了
    haoz1w0w
        37
    haoz1w0w  
       2020-12-04 16:09:12 +08:00
    @TomVista 我的意思是实时计算
    TomVista
        38
    TomVista  
    OP
       2020-12-04 16:31:10 +08:00
    @haoz1w0w 场景不允许

    count 目前只用于生成订单号, 订单号有检索需求,比如 cms 中的订单查找
    PiersSoCool
        39
    PiersSoCool  
       2020-12-04 17:11:24 +08:00
    单机
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2647 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:51 · PVG 19:51 · LAX 03:51 · JFK 06:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.