• 数据库并发获取资源并更新状态的时候如何加锁使每人获取的资源不冲突


    今天电台问了个问题,说的是批量获取某些数据的时候有并发,那么怎么保证大家获取的数据不冲突.

    模拟场景:

    我们鸡蛋饼摊位里面有100个饼,现在有好几个人都来买东西,他们之中A要3份,B要5份,C要2份,我们当然希望按顺序一个个发,避免冲突,但是又希望大家等待的时间少,应该怎么做呢?

    比如说1号到100号饼,我们最理想的方案就是 A要3份 那我就给他 1 2 3号饼,B要5份,那我就给他 4 5 6 7 8号饼,C要2份,那我就给他 9 10 号饼,但是这是理想的方案,想让程序知道你要这么做,就要站在更高的角度提前分配资源,并设定好这些东西给谁.

    A 1号饼
    A 2号饼
    A 3号饼
    B 4号饼
    B 5号饼
    B 6号饼
    B 7号饼
    B 8号饼
    C 9号饼
    C 10号饼

    这是我们理想的情况下直接给你计算好了要怎么样分配,到时候直接A 查出自己要1号 2号 3号,B查出自己要的4号 5号 6号 7号 8号就行了C 查出自己要的9号 10号.

    问题来了 怎么样才能提前算好?

    那我至少得知道A 要几个 B要几个 C要几个,然后计算顺序分配,怎么知道呢?我们让这个请求分为两部分 第一部分 告诉我你要几个 第二部分我给你这几个数据的id

    方案1: redis顺序排队加锁

    进来的时候告诉我你要几个,对redis的某个变量加锁 然后更新现有变量值,将我排队进去,指出我是A要3个,给我最前面的3个数据,查出来了1 2 3 号饼给我,B来了,发现A还在锁里面,那他就等着.直到A结束,B拿到锁告诉你我要5个,然后给你5个,C也一样等B结束才能拿到锁.那这样的话是不是并发高了,大家就都在等锁,10个人排队买饼的故事出现了,某个人买的时候磨磨唧唧还在绑定微信支付银行卡,那完了,大家在后面骂娘了,真下头,哼!

    这样我们能不能做个改进,A既然都知道我拿1 2 3 号饼,B来的时候我不给你1 2 3号饼不就行了,我给你 4 5 6 7 8号饼不就完了? 于是出现了我们进一步的方案2

    方案2: redis并发加锁

    A进来的时候查一下能否获取一个当前最大id的锁,如果要到了,查询id>0的3个饼,锁定1 2 3号饼 放进A的redis key,然后释放最大id的锁,同步更新最新的最大id是3号,那么B来的时候,就直接按照id>3查出5个给B进行锁定,放4 5 6 7 8进行锁定到B的一个redis key,最大id变为8,同理C获取id>8的9号 10号进行锁定.然后各自去更新自己的数据库

    可是你有没有发现一个问题,他们其实还是有一个小小的排队,排的是什么呢?就是这个最大id,这个最大id是不能同步修改的,只能有一个人在修改,就像卖饼的时候有个人看到你付钱成功了才给你拿饼.所以这里还是有一些等待的虽然这样的冲突和等待会比方案1更小,但不意味着它不存在.

    方案3: redis list出栈

    我们知道redis pop出数据是需要自己内部排队的,不会造成并发抢占到同一条资源,那么我们直接把100个饼的编号一下放进redis的list,A来了,给他 pop 3次,B来了给他 pop 5次,C来了给他pop 2次,自然而然,大家都拿到了自己对应的id,然后自己去更新数据库就行了,不过这里有个问题,由于是同时并发,所以可能会出现 A拿到的饼不是1 2 3号 而是 1 3 6号,B拿到的饼是 2 4 7 8 10 号,C拿到的饼是 5 9 号,虽然号码上不连续了,但是个数是对的,倒也没啥问题,大家几乎同时拿到自己想要的id,自己去做自己的更新吧,互不影响了.

    方案4: mysql前置更新

    郭大师提出了一个Mysql前置更新方案,就是先用mysql 去强制更新数据,更新成功,获得抢占的id,再执行后面的逻辑,把更新数据库步骤提前,其核心SQL为

    update table_name set used = 1 , uid = 'A' where used = 0 order by id asc limit 20

    就是说提前抢20个给你,进行更新前置,这样把问题交给mysql做,其实也是可行的.但是我没测试过这样做mysql那边会有会有压力,应该来说mysql也能处理好这种事情,这个方案和我之前写的这个有点类似

    https://www.cnblogs.com/lizhaoyao/p/9199540.html

    方案5: 其实还有个简单但是不安全的办法,但是这样id会不连续

    A来的时候一次性取出100条数据的id,打乱顺序,随机抽3个给A 

    B来的时候一次性取出100条数据的id,打乱顺序,随机抽5个给B

    C来的时候一次性取出100条数据的id,打乱顺序,随机抽2个给B

    这样能简单的帮你避免一部分redis锁的抢占和等待,当你抢到这个id后要去数据库查一下看看是不是都是没问题的,如果是 再update 更新,如果有问题,那你就再来一次即可.

    这些方案中,有容易理解的方法1,依赖Redis比较严重的方法2 3,有依赖数据库严重的方法4,有并发量小的时候效率最高的方法5但是具体选什么,还是要看你们的业务场景是不是够复杂,并发是不是高,锁的时间是不是长,mysql配置是不是好,自己去做一个选型衡量吧!

  • 相关阅读:
    百度地图之自动提示--autoComplete
    h5之scrollIntoView控制页面元素滚动
    angular之interceptors拦截器
    angular之$broadcast、$emit、$on传值
    前端基础入门(一)-HTML-HTML基础
    改进自定义博客
    自定义博客主题
    使用JavaScript策略模式校验表单
    【经典面试题】圣杯布局以及双飞翼布局原理
    [JavaScript设计模式]惰性单例模式
  • 原文地址:https://www.cnblogs.com/lizhaoyao/p/15830929.html
Copyright © 2020-2023  润新知