写点什么

Redis 应用之缓存实现,java 异步编程实战 pdf

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:2022 字

    阅读完需:约 7 分钟

Goods goods = goodsMapper.selectByPrimaryKey(goodsId);


if(goods!=null){


// 将结果保存到缓存中


redisTemplate.opsForValue().set(String.valueOf(goodsId),goods,60,TimeUnit.MINUTES);;


}


return goods;


}


2.缓存方式




缓存中的数据在 redis 中的存储方式有两种,一种是永久存在,不设置过期时间,第二种是设置过期时间。这两种方式都需要尽可能的保证数据的一致性(和数据源中的数据保持同步)。

2.1 不设置过期时间

当我们将缓存数据的 key 设置为永久存在时会存在数据同步和内存消耗逐渐增大的情况,解决方式如下:


数据同步


  1. 禁止直接操作数据源,避免因数据源直接被改动而造成缓存数据不一致的问题

  2. 如果有其他系统操作同一个数据源,这种情况肯定会产生数据不一致的情况。

  3. 系统执行 DML 操作时,应该将缓存中对应的数据删除。用户下一次相关请求时直接从数据源中获取。


内存消耗


随着业务的增多,缓存数据必然会越来越多,所占用的内存也随之增多,系统的压力也会变大,这时一种方式是给 key 设置过期时间,但是过期时间长短不太好把握,这时我们可以通过设置 redis 最大内存来实现,并让 Redis 按照一定的规则淘汰不需要的缓存键,这种方式在 redis 只作为缓存使用时非常实用。


具体实现方式:修改 redis 配置文件(redis.conf)中的 maxmemory 参数既可,限制 Redis 最大可用内存大小(单位字节),当超出了这个限制时 Redis 会依据 maxmemory-policy 参数指定的策略来删除不需要的 key 直到 Redis 占用的内存小于指定内存。



| 规则 | 说明 |


| :-- | :-- |


| volatile-lru | 使用 LRU 算法删除一个 key(只对设置了过期时间的 key 有效) |


| allkeys-lru | 使用 LRU 算法删除一个 key |


| volatile-lfu | 使用 LFU 算法删除一个 key(只对设置了过期时间的 key 有效) |


| allkeys-lfu | 使用 LFU 算法删除一个 key |


| volatile-random | 随机删除一个 key(只对设置了过期时间的 key 有效) |


| allkeys-random | 随机删除一个 key |


| volatile-ttl | 删除过期时间最近的一个 key |


| noeviction | 不删除 key,只返回错误 |


LRU:(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”


LFU:(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

2.2 设置过期时间

对保存到 Redis 中的 key 设置过期时间,但同样也会遇到问题,比如过期时间怎么设置,内存资源同样也会过大。


内存资源


同样需要设置 maxmemory 来限制 redis 使用的最大内存和配置 maxmemory-policy 来指定删除策略。


过期时间设置


过期时间不要设置统一固定的时间,比如 60 分钟,这样会造成相同时间点大量缓存被清空,数据库访问量突然增大的情况,我们应该对过期时间设置合理范围内的随机值。比如:采取不同分类商品,缓存不同周期。在同一分类中的商品,加上一个随机因子。这样能尽可能分散缓存过期时间,而且,热门类目(女装)的商品缓存时间长一些,冷门类目(图书)的商品缓存时间短一些,也能节省缓存服务的资源。


public Goods searchArticleById(Long goodsId){


Object object = redisTemplate.opsForValue().get(String.valueOf(goodsId));


if(object != null){// 缓存查询到了结果


return (Goods)object;


}


// 开始查询数据库


Goods goods = goodsMapper.selectByPrimaryKey(goodsId);


if(goods!=null){


Random random = new Random();


// 将结果保存到缓存中


if(goods.getGoodsCategory().equals("女装")){


int time = 3600 + random.nextInt(3600);


// 热门商品


redisTemplate.opsForValue()


.set(String.valueOf(goodsId)


,goods


,time


,TimeUnit.MINUTES);


}else{


int time = 600 + random.nextInt(600);


// 冷门商品


redisTemplate.opsForValue()


.set(String.valueOf(goodsId)


,goods


,time


,TimeUnit.MINUTES);


}


}else{


// 防止缓存穿透


redisTemplate.opsForValue()


.set(String.val


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ueOf(goodsId)


,null


,60


,TimeUnit.MINUTES);


}


return goods;


}


3.名称解释



缓存穿透

缓存穿透,是指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果 key 不存在或者 key 已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。


public Goods searchArticleById(Long goodsId){


Object object = redisTemplate.opsForValue().get(String.valueOf(goodsId));


if(object != null){// 缓存查询到了结果


return (Goods)object;


}


// 开始查询数据库


Goods goods = goodsMapper.selectByPrimaryKey(goodsId);


if(goods!=null){


// 将结果保存到缓存中


redisTemplate.opsForValue().set(String.valueOf(goodsId),goods,60,TimeUnit.MINUTES);


}else{


redisTemplate.opsForValue().set(String.valueOf(goodsId),null,60,TimeUnit.SECONDS);


}


return goods;


}

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Redis应用之缓存实现,java异步编程实战pdf