点评-Redis

Interceptor拦截器

在哪些地方会使用到:
1.登录校验拦截器:没有登录不能访问部分页面
2.刷新拦截器:使用redis存储用户信息,每次请求需要记录刷新redis到期时间
3.刷新拦截器和登录校验拦截应该分开,用户访问不需要登录的页面也需要刷新到期时间

  1. 用户输入手机号,将验证码缓存到redis,并且进行对比
  2. 登录后以uuid为token,用hash的方式缓存用户信息到redis中,并且保存到ThreadLocal中,方便controller使用
  3. 前端通过拦截器每次请求需要在header中携带token,后端拦截器进行判断,并且更新redis到期时间
    public void addInterceptors(InterceptorRegistry registry) {
        // LoginInterceptor 登录拦截器
        registry.addInterceptor(new LoginInterceptor()).
                excludePathPatterns(
                        "/shop/**",
                        "/voucher/**",
                        "/shop-type/**",
                        "/upload/**",
                        "/blog/hot",
                        "/user/code",
                        "/user/login"
                ).order(2);
        // 刷新拦截器 先执行
        registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).addPathPatterns("/**").order(1);

feed

业务场景:关注的明星发布了新的微博
利用ZSet,将需要发送的消息存放到Redis,可以使用List或Set,由于我们需要排序,可以利用ZSet的score属性

		//将内容信息发送到所有粉丝的邮箱内
        for (Follow fan : users) {
            String KEY = "feed:" + fan.getUserId();
            stringRedisTemplate.opsForZSet().add(KEY, blog.getId().toString(), System.currentTimeMillis());
        }

使用滚动分页,以时间戳(分数)为判断依据,每次查询比上次查询小的时间,就可以避免查询的时候发布新的文章,导致下标改变查询到重复数据。需要注意偏移量是需要跳过的个数,防止相同值会重复

// r..s(userid, 最小时间戳, 上一页最小的值=本页最大的值, 偏移量, 取多少个);
Set<ZSetOperations.TypedTuple<String>> typedTuples = 
	stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(userId, 0, max, offset, 2);

BitMap

业务场景:签到,BitMap节省内存
redis使用String类型实现BitMap

  		//setBit(利用用户信息和日期的值拼装key,offset下标从0开始,true代表签到)
        stringRedisTemplate.opsForValue().setBit(userId + day, now.getDayOfMonth() - 1, true);

HyperLogLog-UV统计

业务场景:UV统计:用户访问量,一个用户多次访问只计算一次,百万统计不超过16k,底层是概率算法,数据小部分误差

// add(名称,插入的数组)
stringRedisTemplate.opsForHyperLogLog().add(KEY, values);

锁定库存

/**
     * 为某个订单锁定库存
     * @param vo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean orderLockStock(WareSkuLockVo vo) {

        /**
         * 保存库存工作单详情信息
         * 追溯
         */
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(vo.getOrderSn());
        wareOrderTaskEntity.setCreateTime(new Date());
        wareOrderTaskService.save(wareOrderTaskEntity);


        
        //找到每个商品在哪个仓库都有库存
        List<OrderItemVo> locks = vo.getLocks();

        List<SkuWareHasStock> collect = locks.stream().map((item) -> {
            SkuWareHasStock stock = new SkuWareHasStock();
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);
            stock.setNum(item.getCount());
            //查询这个商品在哪个仓库有库存
            List<Long> wareIdList = wareSkuDao.listWareIdHasSkuStock(skuId);
            stock.setWareId(wareIdList);

            return stock;
        }).collect(Collectors.toList());

        //2、锁定库存
        for (SkuWareHasStock hasStock : collect) {
            boolean skuStocked = false;
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareId();

            if (org.springframework.util.StringUtils.isEmpty(wareIds)) {
                //没有任何仓库有这个商品的库存
                throw new NoStockException(skuId);
            }

            //1、如果每一个商品都锁定成功,将当前商品锁定了几件的工作单记录发给MQ
            //2、锁定失败。前面保存的工作单信息都回滚了。发送出去的消息,即使要解锁库存,由于在数据库查不到指定的id,所有就不用解锁
            for (Long wareId : wareIds) {
                //锁定成功就返回1,失败就返回0
                Long count = wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
                if (count == 1) {
                    skuStocked = true;
                    WareOrderTaskDetailEntity taskDetailEntity = WareOrderTaskDetailEntity.builder()
                            .skuId(skuId)
                            .skuName("")
                            .skuNum(hasStock.getNum())
                            .taskId(wareOrderTaskEntity.getId())
                            .wareId(wareId)
                            .lockStatus(1)
                            .build();
                    wareOrderTaskDetailService.save(taskDetailEntity);

                    //TODO 告诉MQ库存锁定成功
                    StockLockedTo lockedTo = new StockLockedTo();
                    lockedTo.setId(wareOrderTaskEntity.getId());
                    StockDetailTo detailTo = new StockDetailTo();
                    BeanUtils.copyProperties(taskDetailEntity,detailTo);
                    lockedTo.setDetailTo(detailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",lockedTo);
                    break;
                } else {
                    //当前仓库锁失败,重试下一个仓库
                }
            }

            if (skuStocked == false) {
                //当前商品所有仓库都没有锁住
                throw new NoStockException(skuId);
            }
        }

        //3、肯定全部都是锁定成功的
        return true;
    }
    <select id="listWareIdHasSkuStock" resultType="java.lang.Long">
        SELECT
            ware_id
        FROM
            wms_ware_sku
        WHERE
            sku_id = #{skuId}
          AND stock - stock_locked > 0
    </select>

    <update id="lockSkuStock">
        UPDATE wms_ware_sku
        SET stock_locked = stock_locked + #{num}
        WHERE
            sku_id = #{skuId}
          AND ware_id = #{wareId}
          AND stock - stock_locked > 0
    </update>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值