第17天-整合Redis缓存改造三级分类,并解决缓存击穿、穿透、雪崩、一致性问题

1.缓存



1.1.缓存使用

为了系统性能的提升,一般都会将部分数据放入缓存中,达到快速响应的目的。而数据库承担数据落盘工作。

哪些数据适合放入缓存?

  • 即时性、数据一致性要求不高的
  • 访问量大且更新频率不高的数据(读多,写少)

举例:电商类应用,商品分类,商品列表等适合缓存并加一个失效时间(根据数据更新频率来定),后台如果发布一个商品,买家需要5分钟才能看到新的商品,一般还是可以接受的。



1.2.Cache-Aside模式

边缘缓存模式(Cache-Aside Pattern),即按需将数据从数据存储加载到缓存中。此模式最大的作用就是提高性能减少不必要的查询。

  1. 先从缓存查询数据
  2. 如果没有命中缓存则从数据存储查询
  3. 将数据写入缓存

业务中最常用的缓存层设计模式,基本实现逻辑和相关概念如下:

第17天-整合Redis缓存改造三级分类,并解决缓存击穿、穿透、雪崩、一致性问题_第1张图片

专业术语:

  • 缓存命中:直接查询缓存且命中,返回数据;
  • 缓存加载:查询缓存未命中,从数据库中查询数据,获取数据后并加载到缓存;
  • 缓存失效:数据更新写到数据库,操作成功后,让缓存失效,查询时候再重新加载;
  • 缓存穿透:查询数据库不存在的对象,也就不存在缓存层的命中;
  • 缓存击穿:热点key在失效的瞬间,高并发查询这个key,击穿缓存,直接请求数据库;
  • 缓存雪崩:缓存Key大批量到过期时间,导致数据库压力过大;
  • 命中率:缓存设计的是否合理要看命中率,命中率高说明缓存有效抗住了大部分请求,命中率可以
  • 通过Redis监控信息计算,一般来说命中率在(70-80)%都算合理。

并发问题,执行读操作未命中缓存,然后查询数据库中取数据,数据已经查询到还没放入缓存,同时一个更新写操作让缓存失效,然后读操作再把查询到数据加载缓存,导致缓存的脏数据。

在遵守缓存使用原则下出现该情况概率非常低,可以通过复杂的Paxos协议保证一致性,一般情况是不考量该场景的处理,如果缓存管理过于复杂,会和缓存层核心理念相悖。

基本描述代码

@Service
public class KeyValueServiceImpl extends ServiceImpl<KeyValueMapper,KeyValueEntity> implements KeyValueService {
   
	@Resource
	private RedisService redisService;
	@Override
	public KeyValueEntity select(Integer id) {
   
		//查询缓存
		String redisKey = RedisKeyUtil.getObectKey(id) ;
		String value = redisService.get(redisKey) ;
		if (!StringUtils.isEmpty(value)){
   
			return JSON.parseObject(value, KeyValueEntity.class);
		}
		//查询库
		KeyValueEntity keyValueEntity = this.getById(id);
		if (keyValueEntity != null){
   
			//缓存写入
			redisService.set(redisKey, JSON.toJSONString(keyValueEntity));
		}
		//返回值
		return keyValueEntity ;
	}

	@Override
	public boolean update(KeyValueEntity keyValueEntity) {
   
		//更新数据
		boolean updateFlag = this.updateById(keyValueEntity);
		
		//清除缓存
		if (updateFlag){
   
			redisService.delete(RedisKeyUtil.getObectKey(keyValueEntity.getId()));
		}
		return updateFlag ;
	}
}


1.3.整合Redis作为缓存

gmall-product 模块中 引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置redis

spring:
  redis:
    host: 192.168.139.10

进行压测时产生堆外内存溢出, OutOfDirectMemoryError

  • Spring Boot 2.0以后默认使用lettuce作为操作redis的客户端,它使用netty进行网络通信

  • lettuce的bug导致netty堆外内存溢出

    1、netty若没有指定堆外内存,默认使用-Xmx设置的值
    2、可以通过 -Dio.netty.maxDirectMemory 进行设置

解决方案

  • 不能使用 -Dio.netty.maxDirectMemory 只去调大堆外内存

    这样做只是延缓了出现 OutOfDirectMemoryError 的时间,系统长时间运行后,还是有可
    能出现!

  • 升级Lettuce客户端

  • 切换使用Jedis

    1、从starter中排除lettuce
    2、引入jedis依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <!-- 排除 lettuce -->
        <exclusions>
            <exclusion>
                <groupId>io.lettuce</groupId>
                <artifactId>lettuce-core</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
    </dependency>

Spring 支持 Redis 客户端
  • Lettuce
  • Jedis

RedisAutoConfiguration

@Import({
    LettuceConnectionConfiguration.class,
JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
   }


2.三级分类数据获取优化



2.1.加入缓存

CategoryServiceImpl

@Override
public Map<String, List<Catalog2VO>> getCatalogJson() {
   
	//优先从缓存中获取数据
	String catalogJson = redisTemplate.opsForValue().get("catalogJson"

你可能感兴趣的:(谷粒商城,缓存,redis,数据库)