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命令,它可以指定多个参数。