Redis在削峰和预热以及秒杀中的应用

Redis在削峰和预热以及秒杀中的应用

Redis的作用

将庞大的数据库请求拦截在真正访问数据库前
将访问量大的请求数据,放到缓存中
radis是削峰和防止秒杀超卖

削峰

介绍

在没有缓存时访问数据库并同步到缓存中,有缓存时直接访问缓存。
把大量的请求拦截在数据库访问前避免了用户请求无法及时响应,同时也避免了对数据库造成很大压力

案例

log.info("获取连接对象");
RedisCommands commands = RedisUtils.getCommands();
log.info("从redis中获取数据");
User user = (User) commands.get("user:"+id);
if (user == null){
    log.info("redis中没有数据,从数据库拿出数据");
    user = dao.getByIdUser(id);
    log.info("从redis中写入数据");
    commands.set("user:"+id,user);
}
return user;

预热

介绍

在有预料到有庞大数据量的前提的时候,我可以提前把数据放到ready

预热是秒杀的前置要求。做的操作就是把数据库的。数据详情和库存单独放开两个地方存储。然后由于内存数据库是单线程,所以线程安全的,只要检测到是负数,那就告知用户没有库存。

案例

List<Commodity> list = dao.getAllCommodity();
Map<String, Integer> repertoryMap = new HashMap<>();
list.forEach(commodity -> {
    template.opsForValue().set("commodity:"+commodity.getId(), commodity);
    repertoryMap.put("repertory:" + commodity.getId(), commodity.getRepertory());
});
template.opsForValue().multiSet(repertoryMap);

秒杀

介绍

防止超卖

在秒杀中的应用就是当有大量请求需要访问数据库修改时,首先先修改radis,看他能否成功修改radis,如果可以,那视为秒杀成功。如果失败,则返回那个错误给用户啊,由于radis是单线程,不会出现这边减完然后那边就是没检测到继续减,所以说能保证一个线程安全性。
其中一个体现就是在ready是有大量并发的时候,当你减到负一,并且,然后后面要执行一个增加一但。有仔细观察的话,他在并发结束之前都是长期保持在负数的,因为你刚做完检之后的操作,就千万给下一个线程操作,他不会执行自增的操作

这个操作需要使用到原子减,redis核心只有一个线程在工作,所以不可以会有线程安全的问题

案例

// 如果结果<0,返回异常给用户
if (template.opsForValue().decrement("repertory:"+commodityId) < 0) {
    log.info("该商品已经买完了");
    // 在并发时redis扣除后的库存为负数,所以要将redis自增回来
    template.opsForValue().increment("repertory:"+commodityId);
    throw new RuntimeException("该商品已经买完了");
} else {
    // 生成订单
    orderFormDao.addOrderFormDao(commodityId);
    // 两种方法
    // 在支付后,同步数据库的库存
    // 使用消息队列,发送消息给异步同步数据库的服务器处理
    // 不允许超卖发生
    commodityDao.updateRepertoryByIdAfter(commodityId);
}

笔记

秒杀:修改 set 库存 – 1 where 库存 > 0
保证数据安全性,使用悲观锁
减库存不能放到程序中,这种线程安全要求高的操作放到数据库中操作
昨天讲的是削峰(被动),今天讲的是预热(主动)
预热需要锁定(减去)数据库中的库存
商品详情和商品库存分开缓存的,格式:商品:id 详情和商品库存:id 库存
redis:线程安全(单线程)和原子性(原子+-)
使用原子减操作不会产生线程安全的问题