秒杀系统要如何设计? (6)

  StringBuilder lua = new StringBuilder();
  lua.append("if (redis.call(\'exists\', KEYS[1]) == 1) then");
  lua.append("    local stock = tonumber(redis.call(\'get\', KEYS[1]));");
  lua.append("    if (stock == -1) then");
  lua.append("        return 1;");
  lua.append("    end;");
  lua.append("    if (stock > 0) then");
  lua.append("        redis.call(\'incrby\', KEYS[1], -1);");
  lua.append("        return stock;");
  lua.append("    end;");
  lua.append("    return 0;");
  lua.append("end;");
  lua.append("return -1;");

该代码的主要流程如下:

1. 先判断商品id是否存在,如果不存在则直接返回。

2. 获取该商品id的库存,判断库存如果是-1,则直接返回,表示不限制库存。

3. 如果库存大于0,则扣减库存。

4. 如果库存等于0,是直接返回,表示库存不足。

7 分布式锁

之前我提到过,在秒杀的时候,需要先从缓存中查商品是否存在,如果不存在,则会从数据库中查商品。如果数据库中,则将该商品放入缓存中,然后返回。如果数据库中没有,则直接返回失败。

 

大家试想一下,如果在高并发下,有大量的请求都去查一个缓存中不存在的商品,这些请求都会直接打到数据库。数据库由于承受不住压力,而直接挂掉。

 

那么如何解决这个问题呢?

 

这就需要用redis分布式锁了。

7.1 setNx加锁

使用redis的分布式锁,首先想到的是setNx命令。

if (jedis.setnx(lockKey, val) == 1) {
   jedis.expire(lockKey, timeout);
}

用该命令其实可以加锁,但和后面的设置超时时间是分开的,并非原子操作。

假如加锁成功了,但是设置超时时间失败了,该lockKey就变成永不失效的了。在高并发场景中,该问题会导致非常严重的后果。

那么,有没有保证原子性的加锁命令呢?

7.2 set加锁

使用redis的set命令,它可以指定多个参数。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzwywj.html