首页 > 数据库 >Redis

Redis

时间:2023-02-02 09:56:21浏览次数:45  
标签:sku 缓存 return Redis skuInfo value key

Redis缓存常见问题

缓存穿透--查询一定不存在数据

缓存穿透是指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不
存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。

有很多种方法可以有效地解决缓存穿透问题,我们选择一种,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五
分钟。

缓存穿透解决--存入这个null

非注解解决

RedisUtils

@Component
public class RedisUtils {
 @Autowired
 RedisTemplate<Object,Object> redisTemplate;
 @Autowired
 StringRedisTemplate stringRedisTemplate;
 /**
 * 向redis中存入string类型数据
 * @param key
 * @param value
 * @return
 */
 public boolean set(String key,Object value) {
 try {
 redisTemplate.opsForValue().set(key,value);
 } catch (Exception e) {
 e.printStackTrace();
 return false;
 }
        return true;
 }
 /**
 * 向redis中存入string类型数据,并指定key的过期时间
 * @param key
 * @param value
 * @return
 */
 public boolean set(String key,Object value,long timeout,TimeUnit
timeUnit) {
 try {
 redisTemplate.opsForValue().set(key,value,timeout,timeUnit);
 } catch (Exception e) {
 e.printStackTrace();
 return false;
 }

 return true;
 }
 /**
 * 根据key取出redis中的string类型数据
 * @param key
 * @return
 */
 public Object get(String key) {
 return StringUtils.isEmpty(key)? null : 
redisTemplate.opsForValue().get(key);
 }
 /**
 * 向redis中存入hash类型数据
 * @param field
 * @param key
 * @param value
 * @return
 */
 public boolean hset(String key,Object field, Object value) {
 try {
 redisTemplate.opsForHash().put(key,field,value);
 } catch (Exception e) {
 e.printStackTrace();
 return false;
 }
 return true;
 }
 /**
 * 根据key和field取出redis中的hash类型的数据
 * @param key
 * @param field
 * @return
 */
 public Object get(String key, Object field) {
 return StringUtils.isEmpty(key)? null : 
redisTemplate.opsForHash().get(key,field);
 }
 /**
 * 通用方法:根据key删除对应的数据
 * @param key
 */
 public void del(String key) {
 redisTemplate.delete(key);
 }
 /**
 * 通用方法:判断key是否存在
 * @param key
 * @return
 */
 public boolean exist(String key) {
 try {
 return redisTemplate.hasKey(key);
 更多精彩好课 
https://bwonline.ke.qq.com/
修改shop-product-provider的SkuServiceImpl
 } catch (Exception e) {
 e.printStackTrace();
 return false;
 }
 }
 /**
 * 为某个key设置过期时间
 * @param key
 * @param timeout
 * @return
 */
 public boolean expire(String key, long timeout) {
 try {
 return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
 } catch (Exception e) {
 e.printStackTrace();
 return false;
 }
 }
 /**
 * 查看key的剩余存活时间
 * @param key
 * @return
 */
 public long ttl(String key) {
 return redisTemplate.getExpire(key, TimeUnit.SECONDS);
 }
}

@Service
public class SKUServiceImpl implements SKUService {
    @Autowired
    SkuInfoMapper skuInfoMapper;
    @Autowired
    SkuAttrValueMapper skuAttrValueMapper;
    @Autowired
    SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    SkuImageMapper skuImageMapper;
    @Autowired
    SkuInfoCustomMapper skuInfoCustomMapper;
    @Autowired
    RedisUtils redisUtils;

    //......................省略其它代码..............................
    
    @Override
    public SkuInfo findBySkuInfoId(Integer skuInfoId) {
        String key = "sku:" + skuInfoId + ":info";
        Object value = redisUtils.get(key);
        SkuInfo skuInfo;
        if (value != null) {
            //说明缓存中缓存这个sku,直接取出缓存数据
             skuInfo = new Gson().fromJson(value.toString(), SkuInfo.class);
       } else {
            //说明缓存中没有缓存这个sku,查数据并缓存
            //1.查询sku_info表
            skuInfo =
skuInfoMapper.selectByPrimaryKey(skuInfoId.longValue());
            if (skuInfo != null) {
                //2.查询sku_image表:sku对应的图片
                SkuImageExample skuImageExample = new SkuImageExample();
               
skuImageExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValue());
                List<SkuImage> skuImages =
skuImageMapper.selectByExample(skuImageExample);
                //3.查询sku_sale_attr_value=>sku对应的销售属性值信息
                SkuSaleAttrValueExample skuSaleAttrValueExample = new
SkuSaleAttrValueExample();
               
skuSaleAttrValueExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValu
e());
                List<SkuSaleAttrValue> skuSaleAttrValueList =
skuSaleAttrValueMapper.selectByExample(skuSaleAttrValueExample);
                //4.将skuImages设置到skuInfo中
                skuInfo.setSkuImageList(skuImages);
                skuInfo.setSkuSaleAttrValueList(skuSaleAttrValueList);
                //5.缓存skuInfo到redis
                redisUtils.set(key, new Gson().toJson(skuInfo));
           } else {
       //如果skuInfo为null,将null缓存到redis,过期时间设置为5min!!!!!!!!!!!!!!!!
         redisUtils.set(key, new Gson().toJson(null), 5, 
TimeUnit.MINUTES);
           }
       }
        return skuInfo;
   }
  
   
}

注解解决缓存穿透

需要配置cacheManager

@SuppressWarnings("ALL")
@Configuration
public class RedisConfig {
 @Bean
 public RedisTemplate<Object, Object>
redisTemplate(RedisConnectionFactory redisConnectionFactory)
 throws UnknownHostException {
 RedisTemplate<Object, Object> template = new RedisTemplate<>();
 template.setConnectionFactory(redisConnectionFactory);
 //设置序列化器
 StringRedisSerializer serializer = new StringRedisSerializer();
 template.setKeySerializer(serializer);
 template.setValueSerializer(serializer);
 template.setHashKeySerializer(serializer);
 template.setHashValueSerializer(serializer);
 return template;
 }
 @Bean
 @Primary
 public CacheManager cacheManager(RedisConnectionFactory
redisConnectionFactory){
 更多精彩好课 
https://bwonline.ke.qq.com/
修改shop-product-provider的SkuServiceImpl
 RedisCacheConfiguration cacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig()
 .disableCachingNullValues() //禁用缓存null,如果设置,那么缓存null
时会抛出异常
 .entryTtl(Duration.ofDays(1))
 
.serializeKeysWith(RedisSerializationContext.SerializationPair
 .fromSerializer(new StringRedisSerializer()))
 
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSeriali
zer(new GenericJackson2JsonRedisSerializer()));
 return
RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfigu
ration).build();
 }
 @Bean
 public CacheManager cacheManagerTTL(RedisConnectionFactory
redisConnectionFactory){
 RedisCacheConfiguration cacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig()
 //.disableCachingNullValues()
 .entryTtl(Duration.ofMinutes(5))
 
.serializeKeysWith(RedisSerializationContext.SerializationPair
 .fromSerializer(new StringRedisSerializer()))
 
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSeriali
zer(new GenericJackson2JsonRedisSerializer()));
 return
RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfigu
ration).build();
 }
}

提供者代码

  @Override
    @Cacheable(value = "skuInfo",key ="#skuInfoId+':info'",cacheManager =
"cacheManagerTTL")//指定缓存管理器
    public SkuInfo findBySkuInfoId(Integer skuInfoId) {
       // String key = "skuInfo:" + skuInfoId + ":info";
        //1.查询sku_info表
        SkuInfo skuInfo =
skuInfoMapper.selectByPrimaryKey(skuInfoId.longValue());
        if (skuInfo!=null) {
            //2.查询sku_image表:sku对应的图片
            SkuImageExample skuImageExample = new SkuImageExample();
           
skuImageExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValue());
            List<SkuImage> skuImages =
skuImageMapper.selectByExample(skuImageExample);
            //3.查询sku_sale_attr_value=>sku对应的销售属性值信息
            SkuSaleAttrValueExample skuSaleAttrValueExample = new
SkuSaleAttrValueExample();
           
skuSaleAttrValueExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValu
e());
            List<SkuSaleAttrValue> skuSaleAttrValueList =
skuSaleAttrValueMapper.selectByExample(skuSaleAttrValueExample);
            //4.将skuImages设置到skuInfo中
            skuInfo.setSkuImageList(skuImages);
            skuInfo.setSkuSaleAttrValueList(skuSaleAttrValueList);
       }
        return skuInfo;
   }
}

缓存雪崩--解决设置随机数

缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发
到DB,DB瞬时压力过重产生雪崩。
一个简单方案就时讲缓存失效时间分散开,比如我们可以在原有的失效时间基础上增加一个随机值,
比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

非注解解决

    @Override
    public SkuInfo findBySkuInfoId(Integer skuInfoId) {
        String key = "sku:" + skuInfoId + ":info";
        Object value = redisUtils.get(key);
        SkuInfo skuInfo;
        if (value != null) {
            //说明缓存中缓存这个sku,直接取出缓存数据
             skuInfo = new Gson().fromJson(value.toString(), SkuInfo.class);
       } else {
            //说明缓存中没有缓存这个sku,查数据并缓存
            //1.查询sku_info表
            skuInfo = skuInfoMapper.selectByPrimaryKey(skuInfoId.longValue());
            if (skuInfo != null) {
                //2.查询sku_image表:sku对应的图片
                SkuImageExample skuImageExample = new SkuImageExample();
               
skuImageExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValue());
                List<SkuImage> skuImages =
skuImageMapper.selectByExample(skuImageExample);
    
                //3.查询sku_sale_attr_value=>sku对应的销售属性值信息
                SkuSaleAttrValueExample skuSaleAttrValueExample = new
SkuSaleAttrValueExample();
 更多精彩好课 
https://bwonline.ke.qq.com/
4.8.2.3 注解式缓存解决方案
 一般可以采用多级缓存,不同级别的缓存设置不同的超时时间,尽量避免集体失效,由于注解式的灵活度
很低(高度封装),建议使用非注解式解决方案
4.8.3 缓存击穿
4.8.3.1 概述
 在高并发的情况下,大量的请求同时查询同一个key时,此时这个key正好失效了,就会导致同一时
间,这些请求都会去查询数据库,这样的现象我们称为缓存击穿
               
skuSaleAttrValueExample.createCriteria().andSkuIdEqualTo(skuInfoId.longValue())
;
                List<SkuSaleAttrValue> skuSaleAttrValueList =
skuSaleAttrValueMapper.selectByExample(skuSaleAttrValueExample);
    
                //4.将skuImages设置到skuInfo中
                skuInfo.setSkuImageList(skuImages);
                skuInfo.setSkuSaleAttrValueList(skuSaleAttrValueList);
    
           //5.缓存skuInfo到redis
                //redisUtils.set(key, new Gson().toJson(skuInfo));
                //随机时间范围 1min<= timeout<6min
                //redisUtils.set(key, new Gson().toJson(skuInfo), 
ThreadLocalRandom.current().nextInt(5) + 1, TimeUnit.MINUTES);
    
                //随机时间范围:60s<=timeout<301s
                redisUtils.set(key, new Gson().toJson(skuInfo), 
ThreadLocalRandom.current().nextInt(241) + 60, TimeUnit.SECONDS);
                
           } else {
                //如果skuInfo为null,将null缓存到redis,过期时间设置为5min
                redisUtils.set(key, new Gson().toJson(null), 5, 
TimeUnit.MINUTES);
           }
       }
    
        return skuInfo;
   }
   
}

缓存穿透

标签:sku,缓存,return,Redis,skuInfo,value,key
From: https://www.cnblogs.com/hellojianzuo/p/17084970.html

相关文章

  • 【Redis】配置文件详解
    目录单位:Redis配置对大小写不敏感!包含:搭建Redis集群时,可以使用includes包含其他配置文件网络:通用GENERAL快照(RDB):持久化,在规定的时间内,执行了多少次操作则会持久化到文件.r......
  • 【Redis】事务和乐观锁如何实现?
    目录前言Redis如何实现事务?正常执行事务放弃事务编译时异常,代码有问题,或者命令有问题,所有的命令都不会被执行运行时异常,除了语法错误不会被执行且抛出异常后,其他的正确命令......
  • 【Redis】三大特殊数据类型
    目录Geospatial:地理位置Geospatial:地理位置城市经纬度查询:经纬度查询注意点1:两极无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!注意点2:有效的......
  • 【Redis】五大数据类型
    目录String(字符串)添加、查询、追加、获取长度,判断是否存在的操作自增、自减操作截取、替换字符串操作设置过期时间、不存在设置操作mset、mget操作添加获取对象、getset操......
  • redis-desktop-manager-0.8.0.3844
    redis-desktop-manager-0.8.0.3844下载地址:https://www.aliyundrive.com/s/Y2fobkVjAdF链接:https://pan.baidu.com/s/1keQMs8Tiaj4liE6tgBe82A提取码:nrt4......
  • RedisTemplate和StringRedisTemplate区别
    RedisTemplate和StringRedisTemplate区别StringRedisTemplate继承RedisTemplate主要区别是使用的序列化类不同StringRedisTemplate的API假定所有的数据类型都是字符......
  • 75、缓存---分布式锁---Redisson信号量测试
    可以用来做分布式限流操作。我们在redis中存储一个数据为3@AutowiredRedissonClientredissonClient;@AutowiredStringRedisTemplateredisTemplate......
  • 74、缓存---分布式锁---Redisson闭锁测试
    @AutowiredRedissonClientredissonClient;@AutowiredStringRedisTemplateredisTemplate;//闭锁@GetMapping("/lockDoor")@Response......
  • 73、缓存---分布式锁---Redisson读写锁测试
    保证一定读到最新数据,修改期间,写锁是一个排他锁/互斥锁。读锁是一个共享锁。(相当于操作系统中的读写者问题)写锁没释放读必须等待测试如下:@AutowiredRedissonClient......
  • 72、缓存---分布式锁---Redisson的Lock锁测试
    @GetMapping("/hello")@ResponseBodypublicStringhello(){//1、获取一把锁,只要是锁的名字一样,就是同一把锁RLocklock=redissonClien......