一.思路
将秒杀商品的信息存入redis中,减少对数据库在瞬发时的压力,读取商品信息及减少库存时运用缓存,当库存为0时再同步到数据库,产生的订单也是先存入缓存中,当用户支付成功后再存入数据库当中。
二.代码实现
public void getSecKill() {
HashMap<Long,Production> linkedHashMap=secKillMapper.getSecKill()
.stream()
.collect(Collectors.toMap(Production::getGoods_id, v->v,(v1, v2)->v1,HashMap::new));
for(Map.Entry<Long,Production> entry:linkedHashMap.entrySet()){
redisUtils.putHashValue(Production.class.getSimpleName(),entry.getKey().toString(), entry.getValue());
for(int i=0;i<entry.getValue().getStock_count();i++){
String id= entry.getKey().toString();
redisUtils.putListData(SECKILL_ID+id,id);
}
}
}
说明:这里只是个demo所以我们用了固定的数据,正常情况下要根据各种条件筛选出符合商品进行储存
Production.class.getSimpleName():为商品信息队列,Key为商品的id,Value为商品的详细信息
SECKILL_ID:为一个final常量,在这里就用SECKILL_ID作为List名,一个商品有多少库存则存入多少个,key为商品固定前缀加id,value为id,这么设置是因为,redis的ListQuequ队列有个特性,就是此队列的size有多少则只能由多少个线程能拿到值,简单地说就是为了防止多卖,假设库存为5则只有5个线程能拿的到。
public void toOrder(String account, String good_id) {
//判断该用户是否已下单(是否在缓存队列中)
if (!redisUtils.hasSetKey(USER_RECORD_ID,account)){
//判断是否还有商品(redis中的list的list.size是几就只能有几个线程拿到)
if(null!=redisUtils.getListData(SECKILL_ID+good_id)){
//将成功的秒杀成功的用户存入redis中
redisUtils.setSetData(USER_RECORD_ID,account);
//将订单存入redis中
OrderRecord record=new OrderRecord(good_id,account);
redisUtils.putListData(OrderRecord.class.getSimpleName(),record);
executor.execute(orderCreate);
} else {
System.out.println("此商品已售完");
}
} else {
System.out.println("你的订单正在等待支付。。");
}
}
USER_RECORD_ID:为redis中的setQueue,此队列的特点为不能存入相同的值,防止一个用户多次点击(无论点多少次在缓存中只有一次的值)
OrderRecord.class.getSimpleName():为订单队列,存入的为预订单,里面包含商品id及用户id
这里还运用了线程池:ThreadPoolTaskExecutor
@Component
public class OrderCreate implements Runnable{
@Resource
private RedisUtils redisUtils;
@Resource
private secKillMapper secKillMapper;
@Override
public void run() {
OrderRecord record=redisUtils.getListData(OrderRecord.class.getSimpleName());
if(null!=record){
Production production= redisUtils.getHashData(Production.class.getSimpleName(),record.getId());
Order order=new Order();
order.setUser_id(record.getUserId());
order.setSeller_id(production.getSeller_id());
order.setMoney(production.getCost_price());
order.setCreate_time(new Date());
order.setStatus("0");
redisUtils.putHashValue(Order.class.getSimpleName(),record.getUserId(),order);
synchronized (OrderCreate.class){
production= redisUtils.getHashData(Production.class.getSimpleName(),record.getId());
production.setStock_count(production.getStock_count()-1);
if(production.getStock_count()<=0){
secKillMapper.updateGood(record.getId());
redisUtils.deleteHashValue(Production.class.getSimpleName(),record.getId());
} else {
redisUtils.putHashValue(Production.class.getSimpleName(),record.getId(),production);
}
}
}
}
}
总结代码思路
Production.class.getSimpleName():商品信息队列
SECKILL_ID:商品库存队列
USER_RECORD_ID:用户秒杀成功队列
OrderRecord.class.getSimpleName() :用户订单队列
首先将所有符合条件的商品信息存入 商品信息队列 ,根据每个商品的库存数量存入相应的 商品库存队列,当用户进行秒杀时先去 用户秒杀成功队列 查看是否有该用户存在,没有的话 去 商品库存队列 取想要秒杀的商品的id,如果还有的话,把用户信息及商品id 存入 用户订单队列