秒杀系统在各种业务场景里面都会出现。对于电商公司商品秒杀会涉及到:会场、排期、招商、C端(商详店详)、金融等中台或业务线的通力合作,系统本身也会涉及到网关、风控、基础设施(db,cache,容器)多个环节。但是如果现在仅仅考虑1个固定商品的秒杀一天就一个固定场次,不涉及支付(0元就可以秒),用户限购1个,这样市场或营销的场景,现在看一下如何短平快实现一个高并发支持实现简单快捷的秒杀系统。

图里面的系统简单起见都认为已经有了,不需要再进行考虑了。

1. 开始具体的的设计
数据库一张表名字叫buy:
字段:id, item_id,user_id,number(秒杀序号)
uniq key: item_id,user_id
uniq key: item_id, number

2. 秒杀接口(登陆态必须):
input :userid,itemid
output: 是否秒杀成功

3. 核心逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    /**
     * -1无库存
     * @param userId 用户id
     * @param itemId 商品id
     * @param stock  商品库存
     * @return
     */
    public long buyNow(long userId, long itemId, long stock) {

        String nowNo = jedis.get(String.format("buynow_%d",itemId));
        if (nowNo != null && Long.valueOf(nowNo) >= stock) {
            // 没有库存了
            return  -1;
        }
        if(jedis.pfadd(String.format("buynow_u_%d",itemId),String.valueOf(userId)) > 0) {
            // 这个uid没来过
            long byno = jedis.incr(String.format("buynow_%d",itemId));
            if (byno <= stock) {
                // 获取到购买序号了

                // 插入表格返回id,插入失败返回null
                Long id = buyDao.insert(userId,itemId,byno);
                if (id !=null ) {
                    // 记录数据库成功
                    return id;
                }
               
            }
        }
        // 没有抢到
        return  -1;
    }

上面的逻辑主要看当前号是否已发放完了,没发放完,给没来过的用户发号。里面用到了hyperloglog和string两个redis的数据类型。但是需要这个kv系统是可靠的。
如果库存很小的情况下方法基本没有瑕疵,库存大的话,有些用户没来过会判断已经来过了。不会给他号。不过还好足够简单了。