博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
发奖控制数量的几种方式(锁,数据库锁,分布式锁,无锁)
阅读量:4079 次
发布时间:2019-05-25

本文共 3450 字,大约阅读时间需要 11 分钟。

在我们日常编码过程中,做营销类系统时,一定会遇到发奖、秒杀等业务,在这些业务中,奖品数量的控制尤为重要,如果没控制好,多发了奖品,会对运营成本造成超支,或者影响用户体验。本篇就介绍几种数量控制方案,供大家讨论。

一:通过锁控制数量

最简单直白的方式就是通过java自带的锁来控制数量的发放,伪代码如下:

synchronized(this) {        int count = countByPrizeId(prizeId);        if (count > totalCount) {            throw new BizException("奖品数量不足");        }        fetch(userId, prizeId);    }

这种模式的优点在于:代码简单易读,对于程序员的要求很低。

风险点分析

  1. synchronized只能在单机环境中生效,如果分布式/集群部署,则无法控制数量
  2. 程序中形成单点,所有线程都会竞争这个锁,若随着数据量增大,锁内部操作需要时间增长,则会导致大量线程阻塞,最终使服务宕掉。

二:通过数据库控制数量

针对synchronized只能在单机环境中使用的问题,此处考虑将锁放到jvm之外,这里就引入数据库行级锁控制:

比如在奖品表中加入已发放数量,每次通过更新数量,成功则插入用户流水,失败则返回奖品发完,如下:

UPDATE T_PRIZE SET FETCH_COUNT = FETCH_COUNT + 1 WHERE FETCH_COUNT < TOTAL_COUNT;

又或者不增加发放数量字段,通过mysql的insert into select方式能达到一样的效果:

INSERT INTO T_USER_PRIZE SELECT field FROM DUAL WHERE (SELECT COUNT(*) FROM T_USER_PRIZE WHERE T_PRIZE_ID = '1') < '${TOTAL_COUNT}'

这种模式的优点在于:容易理解,且针对业务量小的发奖来说,QPS能达到要求。

风险点分析

  1. 所有压力都堆到了数据库,一旦量超出预期,很容易造成数据库挂掉。

三:通过分布式锁控制数量

通过分布式锁控制数量是一种比较流行的方案,此处用redis做示例,伪代码如下:

// redis锁    private boolean tryLock(String key) {        redisTemplate.execute(connection -> {            while(此处定义获取锁失败重试次数){                if(connection.setNX(key, LocalDateTime.now().getTime() + EXPIRE_TIME)) {                    return true;                }                // 取出时间判断过期了,则新来的key获取锁                Long oldTime = connection.get(key);                if(oldTime > LocalDateTime.now().getTime() + EXPIRE_TIME)) {                    connection.set(key, LocalDateTime.now().getTime() + EXPIRE_TIME);                    return true;                }            }            return false;        })    }    // redis释放锁    private void deleteKey(String key) {        redisTemplate.execute(connection -> connection.del(key);    }    //调用    public void fetch(Long prizeId, String userId) {        // 每个奖品一个锁        String key = REDIS_KEY_PREFIX + prizeId;        try() {            if(tryLock(key)) {                int count = countByPrizeId(prizeId);                if (count > totalCount) {                    throw new BizException("奖品数量不足");                }                fetch(userId, prizeId);            }        } finally {            // 释放锁            deleteKey(key);        }    }

代码中大致写了通过redis实现分布式锁的方式,代码为在markdown中纯手打,如果错误请自行改正。

使用分布式锁有点在于:

  1. key分散,每个奖品一个锁,锁竞争变少。(当然,用synchronized也有锁分散方案)。
  2. redis操作都在内存中,效率高,不考虑数据库因素,单机能达到近十万级别QPS。

风险点分析:

  1. 数据量超过预期,一样会造成程序宕机。
  2. redis一旦挂掉或出现通信障碍,整个流程将会不可用(通过集群部署等方案解决)。

四:无锁控制数量

一旦加上锁,效率肯定会变低,这里介绍一种无锁控制数量的方式来实现,同样基于redis来实现,伪代码如下:

// 此方法中简化对redis操作的代码,只写出调用的哪个方法,不再具体写connection那些。    public void fetch(Long prizeId, String userId) {        // 每个奖品一个锁        String key = REDIS_KEY_PREFIX + prizeId;        if (redisTemplate.get(key) == null) {            // 未发奖时统计出数据为0,发奖后统计出具体发放数据            int count = countByPrizeId(prizeId);            // 注意此处使用setNX,不能使用set,否则会有并发问题。            redisTemplate.setNX(key, count);        }        // redis对于key中的值自增,并返回自增后的值        Long count = redisTemplate.incrr(key);        if (count > totalCount) {            throw new BizException("奖品数量不足");        }        fetch(userId, prizeId);    }

此方法原理为使用redis的incrr方法做一个奖品数量的计数器,每个请求进来都会调用自增拿到最新的数量,之后可进行校验。

此方式优点:未使用锁,效率更高。

风险点分析:

  1. 同样依赖于redis,redis挂掉会导致程序不可用。
  2. 在缓存未命中时去查库的操作,可能会造成同时大批请求进入db,导致db挂掉。可通过预先放入缓存并将缓存的失效时间设置超过活动时间的方式解决。
  3. 执行incrr方法之后,若程序抛出异常,可能会造成奖品少发的情况。

以上四种方式,各有优劣,我们可以根据自己的业务进行选择。技术始终是服务于业务的,比如我们做一个发奖活动,用户只有几十个人,那么肯定是选择直接使用synchronized即可,没必要去搞那些分布式锁等等东西。技术方案很多,选择适合我们当前业务场景的即可!


我的博客:www.scarlettbai.com

转载地址:http://brsni.baihongyu.com/

你可能感兴趣的文章
ROS为上位机与STM32为下位机串口通讯(一)
查看>>
github上搜了下有ROS uart方面的
查看>>
STM32和ROS的串口通信(这篇是公众号文章写得比较正规详细)
查看>>
全网最实用的STM32和ROS机器人的串口通信方案
查看>>
我觉得还是把ACfly的传感器的逻辑弄清楚,这样再去二次开发好一些。(折腾半天发现有很关键一部分没有开源,怪不得找不到,这让我很失望)
查看>>
freertos工程似乎都是先创建一个任务,再在这个任务里面创建其他任务,似乎就像任务树
查看>>
无人机的高度自适应
查看>>
别人对ACfly的评价
查看>>
还有你怎么判断ACfly是正常接收到了数据,怎么从ACfly端能看到实时的T265传给ACfly的位置数据。
查看>>
我觉得对双目VIO+无人机,单单靠VIO这边输出很好的位置信息还是不够的,无人机这边还是需要做做滤波,比如防止跳变什么的,保证无人机的稳定。
查看>>
英特尔RealSense激光雷达摄像头L515拆解分析
查看>>
优象光流使用的一些注意事项(转载)(光流数据要融合其他传感器使用比较好)
查看>>
mavlink里面有个关键词 msg
查看>>
mavlink消息帧里最重要的两个东西,一个是msgid;一个是payload
查看>>
【无人机开发】通讯协议MavLink详解
查看>>
B站这个讲mavlink的视频不错(弄懂了很多东西)
查看>>
*我发觉不管是mavlink还是传感器驱动都是基于串口协议的一个更高层的协议!!!!!!!(没有协议没有规则是没有办法进行通信的)
查看>>
STM32控制APM飞控(四)MAVLINK协议深入理解之数据结构
查看>>
STM32控制APM飞控(五)MAVLINK的C源码的解释及MAVLINK心跳包
查看>>
STM32控制APM飞控(二)MAVLINK源码集成到stm32工程中
查看>>