引入redis来做缓存后,测试时要关注的点

开发为什么要引入缓存

为了性能,主要是高并发下的性能

一些基础的概念介绍

常见的缓存中间件有Redis、Memcache、Ehcache ,一些详细的介绍可以看这里https://blog.csdn.net/riipgah/article/details/112626440
https://blog.csdn.net/weixin_42886699/article/details/121668172
就只引用些示例图帮助大家理解
Redis:image.png

Encached在这里插入图片描述

Memcached
在这里插入图片描述

使用方式

redis在我们公司的应用主要还是分布式锁比较多,缓存用的比较少,直接使用的话可以这样

	@Test
	public void testString() {
 
		// 单个
		for (int i = 1; i <= 10; i++) {
 
			// 获得资源
			Jedis jedis = RedisUtil.getJedis();
			// 设置字符串值
			jedis.set("mike" + i, i + "");
 
			System.out.println(jedis.get("mike" + i));
 
			// 关闭资源
			RedisUtil.backResouce(jedis);
		}

更简单的方式是使用redis的注解

    //@Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),
    //并将执行的结果存入缓存中,否则返回缓存中的对象。这里的缓存中的 key 就是参数 userName,value 就是 Account 对象。“accountCache”缓存是在 spring*.xml 中定义的名称。
    @Cacheable(value="accountCache")
    public Account getAccountByName(String userName) {
        // 方法内部实现不考虑缓存逻辑,直接实现业务
        System.out.println("real query account."+userName);
        return getFromDB(userName);
    }

开发将缓存视为实现细节,而不是需要测试的功能,但引入缓存后测试是必要的

1、缓存的作用和隐患不是及时能体现出来的
2、缓存的使用姿势也有很大讲究
3、缓存出现问题的后果很严重,错误的使用缓存,可能导致有雪崩的风险,导致数据库承担过多的压力

测试要关注的点

使用姿势对不对

失效策略
主动失效
主动失效是指系统有一个主动检查缓存是否失效的机制,比如通过定时任务或者单独的线程不断的去检查缓存队列中的对象是否失效,如果失效就把他们清除掉,避免浪费。主动失效的好处是能够避免内存的浪费,但是会占用额外的CPU时间。
被动失效
被动失效是通过访问缓存对象的时候才去检查缓存对象是否失效,这样的好处是系统占用的CPU时间更少,但是风险是长期不被访问的缓存对象不会被系统清除。

缓存淘汰
FIFO
先进先出(First In First Out)是一种简单的淘汰策略,缓存对象以队列的形式存在,如果空间不足,就释放队列头部的(先缓存)对象。一般用链表实现。
LRU
最近最久未使用(Least Recently Used),这种策略是根据访问的时间先后来进行淘汰的,如果空间不足,会释放最久没有访问的对象(上次访问时间最早的对象)。比较常见的是通过优先队列来实现。
LFU
最近最少使用(Least Frequently Used),这种策略根据最近访问的频率来进行淘汰,如果空间不足,会释放最近访问频率最低的对象。这个算法也是用优先队列实现的比较常见。

缓存对象
需要有足够的内存用于缓存。
缓存中每个对象的大小是多少?
缓存中的最大对象数是多少?
当缓存已满并且需要向其添加一个对象时会发生什么?

缓存更新的策略
https://blog.csdn.net/hukaijun/article/details/81010475

持续的监控

中间件的表现
-CPU,内存指标,持续观测
-是否有频繁的更新缓存(缓存更适用于读多写少的业务)
-是否有某个异常过大的key
-大数据量情况下,缓存的更新和命中率
数据初始化
-如果在应用程序启动时进行了初始化,那么在缓存最大数量的对 象时需要花费多长时间?
-应用程序可以在初始化期间处理请求吗?
-如果在发出请求时缓存对象,那么缓存未命中或直写事务的响应时间是否可以接受?

异常的情况

1、缓存数据和数据库数据的短暂不一致性是否能接受
2、是否配置了缓存数据的持久化(redis异常重启)
3、缓存失效的常见问题

缓存雪崩
雪崩就是指缓存中大批量热点数据过期后系统涌入大量查询请求,因为大部分数据在Redis层已经失效,请求渗透到数据库层,大批量请求犹如洪水一般涌入,引起数据库压力造成查询堵塞甚至宕机。
解法:过期时间设置时错开时间,使用随机数等方式削峰

缓存穿透
举个栗子。数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
解法:无效数据也建立缓存,避免被重复攻击

缓存击穿
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解法:访问数据库时添加分布式锁,更新了缓存后再释放,后续请求即可使用缓存;或者提供未 获得锁时的降级方案

缓存效果的验证
e.g. 有一个商品详情页库存余额数量查询的接口,涉及到了缓存查询,那可能会有哪些测试点:

1、首次请求接口,余额数据正确,且缓存中/数据库中数据保持一致
2、多次请求接口,余额数据正确,且查看链路日志或其他手段确认数据直接从缓存中返回
3、正常请求接口,缓存中存在数据后,更新库存数据(通过正常业务链路),再次查询,余额数据更新正确(review 更新缓存相关代码
4、正常请求接口,缓存中存在数据后,删除对应商品(通过正常业务链路),再次查询,提示商品已被删除,且缓存数据已清除
5、正常请求接口,缓存中存在数据后,更新库存数据(直接修改数据库),再次查询,余额数据未更新
6、正常请求接口,缓存中存在数据后,删除对应商品(直接修改数据库),再次查询,余额数据未更新
7、正常请求接口,缓存中存在数据后,等待较长时间(缓存过期)后,再次查询,余额数据正确且缓存过期时间被更新(校验过期时间配置)
8、正常请求接口,缓存中存在数据后,请求其他数据(增加缓存至内存达到汰换标准)后,再次查询,余额数据正确且缓存淘汰正确(校验缓存淘汰参数配置)
9、正常请求接口,缓存中存在数据后,重启缓存服务,再次查询,余额数据正确,且缓存中/数据库中数据保持一致
10、并发请求接口,余额数据正确,且缓存中/数据库中数据保持一致
11、并发请求修改库存和查询库存接口,余额数据正确

更广泛的含义

前端缓存
	浏览器缓存
	APP的本地缓存
数据库缓存
	索引(思路类似)
CDN缓存
  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 引入依赖 在 pom.xml 文件中添加以下依赖: ``` <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>org.apache.ibatis</groupId> <artifactId>mybatis-redis-cache</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> ``` 2. 配置 Redis 在 application.yml 配置文件中添加 Redis 的连接信息: ``` spring: redis: host: 127.0.0.1 port: 6379 password: 123456 database: 0 pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1ms ``` 3. 配置 MyBatis Plus 在 MyBatis Plus 配置文件中开启二级缓存并配置 Redis 缓存: ``` mybatis-plus: configuration: cache-enabled: true # 开启二级缓存 local-cache-scope: session # 二级缓存作用域 lazy-loading-enabled: true # 开启懒加载 multiple-datasource-enabled: true # 开启多数据源 global-config: db-config: id-type: auto # 主键ID类型 field-strategy: not_empty # 字段非空验证 swagger2: true # 是否开启Swagger2 cache: enabled: true # 开启缓存 type: redis # 缓存类型 # 设置缓存前缀,默认是 mybatis:cache: # prefix: mybatisplus: spring: redis: cache: # 过期时间,默认1天 ttl: 86400 # 二级缓存前缀,默认是 mybatisplus:cache: key-prefix: mybatis-plus:cache: # 条目数量,默认256个 max-number-of-elements-in-cache: 256 ``` 4. 实体类开启缓存 在需要开启缓存的实体类上添加 `@CacheNamespace` 注解: ``` @Data @NoArgsConstructor @TableName("student") @CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class, flushInterval = 60000) public class Student implements Serializable { @TableId(type = IdType.AUTO) private Long id; @TableField("name") private String name; @TableField("age") private Integer age; } ``` 其中,`implementation` 和 `eviction` 属性的值均为 `MybatisRedisCache.class`,表示该实体类使用 Redis 缓存,并且缓存失效时间为 1 分钟(60 秒)。 5. 注册 Redis 缓存插件 在 Spring Boot 应用启动类中注册 Redis 缓存插件: ``` @Configuration public class MyBatisPlusConfig { @Bean public RedisCachePlugin redisCachePlugin(RedisTemplate<Object, Object> redisTemplate) { return new RedisCachePlugin(redisTemplate); } } ``` 6. 测试缓存 使用以下代码进行测试: ``` @Test public void testRedisCache() { Student student1 = studentService.getById(1L); Student student2 = studentService.getById(1L); System.out.println("student1:" + student1); System.out.println("student2:" + student2); Assert.assertEquals(student1, student2); } ``` 第一次查询会从数据库中获取数据并保Redis 缓存,第二次查询会直接从 Redis 缓存中获取数据,输出结果如下: ``` DEBUG [MybatisRedisCache] [Session XX] [Namespace com.example.demo.entity.Student] [Cache INSERT] Student(id=1, name=Tom, age=20) student1:Student(id=1, name=Tom, age=20) student2:Student(id=1, name=Tom, age=20) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值