红包(商品)库存秒杀系统

来源:互联网 发布:柠檬网络tv 编辑:程序博客网 时间:2024/04/29 00:46

下面是我的设计思路:

有不妥的地方请帮忙指正!


1,第一次将mysql中的库存数据取出放入redis缓存中

2,并发请求的时候,每一个线程进来自动将redis中的库存自动-1(redis的自减操作为原子操作),然后判断结果是否>=0

3,当结果>=0说明该线程有资格获得红包(线程进来就分配库存,<0 肯定是库存不足的情况)

4,有资格获得红包的用户 执行获得红包的操作(如果获得失败,将redis中的库存+1)

5,当步骤3 判断已经没有库存的时候,应该要将redis中的库存+1 ;(因为会过一段时间同步的mysql数据库中,要保证redis中的数据准确性)

6,当有线程进来领取红包的时候,说明库存有了变化,则插一条异步请求 :同步数据到mysql的接口 (并且是5分钟之后执行)

7,针对单个用户多并发的情况,只有将用户的ID+红包的ID作为主键存在redis中,并且设置有效时间为3秒(多少秒根据需求设定,比如这里是,在3秒里面我只接受你一次请求)第一个线程进来reids 线程数=1,后面进来的自增;有且只有redis线程数=1的时候,才让线程往下面走;

总结:利用redis的自增自减操作的原子性;将库存的读写操作在某个时间点执行,并不需要每次变更都去读写数据库(这样对性能影响很大);同步操作异步执行;

下面是示例部分代码:


    private boolean receiveCoupon (String userId ,long templateId  ) throws BusinessException {             boolean flag = false;             long count = 0;             count = jedisService .incrCounterCache( "cpsCoupon" +templateId , userIdOrtelphone , 1);             if (count > 0) { //一个用户一个模板3秒内只能领取一次,预防并发问题                   jedisService .expire("cpsCoupon" +templateId , userIdOrtelphone , 3);//expire是设置缓存有效期            }            );             if (count == 1) {                   flag = insertCouponNew(userId,templateId);            }             return flag ;      }


     private boolean insertCouponNew(String userId,long templateId ) throws BusinessException {                //做一些校验判断                      ........               //检查库存并且发送红包              checkRepertoryAndSend(userId,templateId);             return true ;      }



public void checkRepertoryAndSend (String userId  ,long templateId  ) throws BusinessException {             //从数据库查询  红包库存               ccTmplate.getRepertory() 是库存               ........            //判断缓存中有没有库存的数据,如果没有,就将查出来的放进缓存          Object rep = jedisService .exists(AppCacheEnum. COUPON_REPERTORY.toString(), templateId );             if (!(boolean )rep ){                   int ttl = 60*60*24*30*12; // 存个一年;                   jedisService .putCounterCache(AppCacheEnum. COUPON_REPERTORY.toString(), templateId , ccTmplate .getRepertory(), ttl);             }               //每个线程进程进来都自动-1;分配一个库存;incrCounterCache 就是redis 的incr操作,它是原子操作             if (jedisService.incrCounterCache(AppCacheEnum. COUPON_REPERTORY .toString(),templateId ,-1)>=0){ //有库存可以购买                                   //这里执行发放红包操作;如果红包发放是吧 则需要将缓存-1                                   jedisService .incrCounterCache(AppCacheEnum. COUPON_RECEIVE_NUM.toString(), templateId ,1);                   //同步任务 当第一个线程进来,缓存COUPON_KC_SYNC执行自增操作==1;则发一个同步的操作,并且设置为5分钟之后执行                   if (jedisService .incrCounterCache(AppCacheEnum. COUPON_KC_SYNC.toString(), templateId ,1)==1){                         int ttl = 300;                         //这个缓存的主要目的就是来判断时间是否过了5分钟                         jedisService .expire(AppCacheEnum. COUPON_KC_SYNC.toString(), templateId ,ttl );                         //加上同步任务并且是5分钟之后                        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss" );                        Date time = DateUtil.getDateByAdd( "n" , 5, new Date());//5分钟之后同步一下分钟                        Long focusVal = Long.valueOf( formatter.format( time ));                       //下面是插入一条定时任务,在5分钟之后执行的同步库存操作                         ......   这个定时任务就不详细说了;                  }            } else  { //刚刚减掉了 这里没发红包库存给加回来                   jedisService .incrCounterCache(AppCacheEnum. COUPON_REPERTORY.toString(), templateId ,1);                    //判处库存不足的异常                   throw new BusinessException(BusinessCode.coupon_has_clear .getCode(),BusinessCode. coupon_has_clear.getMsg());            }      }

  public int syncRepertory (long id ){                       return cpsCouponTemplateMapper .syncRepertory( id,库存数量);      }





0 0
原创粉丝点击