基于redis实现的商品浏览量数据维护

60 阅读1分钟

日常开发中,经常会遇到商品浏览量,文章阅读量,帖子点赞量等等,一般的实现就是每操作一次,数据库记录+1。

这种操作理论上是可以实现的,但是在高用户的情况下,频繁更新数据库,导致数据库压力过大导致的数据库挂掉却是得不偿失。

在网上看到有使用redis的自增value去实现,实现方案大致是服务启动时,通过@PostConstruct读取数据库中商品的游览量,存到缓存中,后续涉及到的游览量的读取,修改都在redis中操作。

但是如果商品的数据量过大,会占用redis过多内存,冷热数据不分离也会导致冷门商品占用redis内存资源。

最终的实现方案是在商详的时候存入到redis,这样能避免全部商品数据写入redis,占用redis内存。最后通过定时任务回写到数据库,删除redis缓存,释放内存。

实例代码如下

public ProductVo queryClientProductDetail(Long id) throws CommonException {
        // 获取商品详情
        ProductVo productVo = queryProductDetail(id);
​
        // 从缓存取游览量
        String KEY = RedisConstants.PRODUCT_VISIT_COUNT_PRE + "storeId:" +  productVo.getStoreId() + "productId:" + productVo.getId();
        Number visitCount = redisCache.getCacheObject(KEY);
        if (Objects.nonNull(visitCount)) {
            productVo.setVisitCount(visitCount.longValue());
        } else {
            redisCache.setCacheObject(KEY, productVo.getVisitCount());
        }
​
        // 游览量自增
        redisCache.incr(KEY, 1);
​
        return productVo;
}

定时任务回写

@Slf4j
@Component("productvisitCountAutoSyncTask")
public class ProductvisitCountAutoSyncTask {
​
    @Autowired
    private RedisCache redisCache;
​
    @Autowired
    private ProductServiceImpl productService;
​
    public void runProductvisitCountAutoSyncTask() {
        Collection<String> keys = redisCache.keys(RedisConstants.PRODUCT_VISIT_COUNT_PRE + "*");
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }
​
        Map<String, Number> visitCountMap = keys.stream().collect(Collectors.toMap(key -> key, key -> redisCache.getCacheObject(key)));
​
        if (MapUtils.isEmpty(visitCountMap)) {
            return;
        }
​
        List<Product> updateList = Lists.newArrayList();
        visitCountMap.forEach((key, value) ->
            updateList.add(new Product().setId(Long.valueOf(key.substring(key.lastIndexOf(":")+1))).setVisitCount(value.longValue()))
        );
​
        productService.updateBatchById(updateList);
​
        redisCache.deleteObject(keys);
    }
}