Redis异常测试盘点分析

目录
  • Redis测试中的异常
    • 一、更新 Key 异常
    • 二、Key的删除和丢失
    • 三、KEY 过期策略不当造成内存泄漏
    • 四、查询Redis异常时处理
    • 五、redis 穿透、击穿、雪崩
    • 六、Redis死锁
    • SET Key UniqId Seconds
    • 分布式Redis锁:Redlock
    • 七、Redis持久化
    • 八、缓存与数据库双写时的数据一致性

Redis测试中的异常

在测试工作中,涉及到与 redis 交互的场景变的越来越多了。关于redis本身就不作赘述了,网上随便搜,本人也做过一些整理。

今天只来复盘一下,在测试过程中与 redis 的二三事儿。其中提到的案例是经过抽象化的,用作辅助说明作用,仅供参考。

一、更新 Key 异常

注意点:先删除原 key 再存,还是直接覆盖原 key?

比如:之前 A 服务每8小时去查询一次数据库,更新到缓存里去。后来需求调整,变成当数据库里有变动的时候就会发送MQ消息给服务 A,然后A就去全量拉取库里数据,再更新到缓存。

开发小哥实现的是先删除key再更新,那么可能会导致这个时间如果有大量的请求进来,就不能命中缓存。于是乎建议,当从数据库拉来数据之后,可以先和redis中原来的key值进行对比,删除多余的缓存,其他的覆盖更新。

二、Key的删除和丢失

注意点:考虑key被删除,或者key丢失后对上游的影响。

比如:服务A 会同步一类数据到 redis,然后发消息告诉 服务B。B 收到消息后,拿到 redis 数据去找自己那边 MongoDB里的对应 key,做更新操作,若查不到key,就会删除数据。

此时如果 redis 里产生了数据丢失,key就不存在了,那么同步过后,会导致 MongoDB 里的数据被勿删。

于是乎这里建议方案是:redis 那边涉及要删除key的话,就更新key的值为空[],这时候 MongoDB 查询到值为空的key,就去删除对应数据。
另外,如果redis那边key 丢失了,MongoDB这边也别就删数据了,去调用一个实时接口去查询数据然后更新。

三、KEY 过期策略不当造成内存泄漏

首先回顾一下 redis 中 ttl key指令:

  • 当 key 不存在时,返回 -2
  • 当 key 存在但没有设置剩余生存时间时,返回 -1
  • 否则,返回 key 的剩余生存时间,单位是 s

通常,大多数业务用到redis 都会设置过期时间。接下来,了解一下 key 过期是如何清理的。

定期清理

Redis会定期主动淘汰一批已过期的key(随机抽取一批key检查)。

缺点:可能存在很多KEY已过期,仍未清理。

惰性清理

在获取某个 key 的时候,redis 会检查一下这个 key 如果设置了过期时间并且已经过期,就会删除这个 key,不会返回任何东西。

缺点:如果存在很多未去查询的过期key,就没法走到惰性删除,于是可能会有大量过期的key堆积在内存里,导致内存耗尽。

一般来说,业务会惰性和定期清理配合使用。

内存淘汰机制

但是,如果定期清理漏掉了很多过期的key,然后你也没及时去查,也就没走惰性删除。此时依旧有可能大量过期的key堆积在内存里,导致内存耗尽。

这时候需要内存淘汰机制,有如下几个:

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。这个一般很少用。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key,这个是最常用的。
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

以上可以作个了解。

四、查询Redis异常时处理

很多时候,redis 只是做一个缓存机制,如果redis异常或者未取到数据,是否有实时获取数据的兜底方案(查接口 or 查库?),需要考虑。

五、redis 穿透、击穿、雪崩

穿透

用户想要查询一个数据,发现redis内存数据库中没有,也就是说没有命中缓存,也是会向持久层数据库查询,发现也没有,那么本次查询失败。
如果此时,用户很多,高并发场景下都去查这个数据,由于缓存都没有命中,于是压力直接打到持久层数据库那里,这就是缓存穿透。

解决方案可以用布隆过滤器、返回空对象(设置过期时间)。

击穿

缓存击穿,是指一个key非常热点,在不停的扛着高并发,如果这个key失效了,在失效的瞬间,持续的并发量就会穿破缓存,直接打到持久层数据库,就像一个防御墙被凿开一个洞。

解决方案可以设置热点数据永不过期、加互斥锁等。

雪崩

是指在某一个时间段,缓存集中过期失效,或者redis宕机了。

解决方案:

  • 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
  • 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
  • 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

关于这3个,之前有过一篇整理:https://www.jb51.net/article/230997.htm

六、Redis死锁

Redis锁,小心使用不当造成锁不能释放,陷入死锁。

目前常用的2种锁:

SET Key UniqId Seconds

仅在单实例的场景下是安全的。如果不使用setnx+expire+del中间环节断了仍可能造成死锁;
如果不用SET Key UnixTimestamp Seconds NX,高并发下可能存在相同时间戳。

分布式Redis锁:Redlock

此种方式比原先的单节点的方法更安全。

  • 安全性:在同一时间不允许多个Client同时持有锁。
  • 活性死锁:锁最终应该能够被释放,即使Client端crash或者出现网络分区(通常基于超时机制)。
  • 容错性:只要超过半数Redis节点可用,锁都能被正确获取和释放。

七、Redis持久化

当Redis数据需要长久有效时,需要考虑是否做RDB和AOF持久化,一般RDB和AOF配合使用,但做持久化,会影响性能。

目前接触到的业务做持久化的很少见。比如有个推荐系统Redis数据是长久有效的,但却为了响应快不影响性能,未做持久化,而采用了其他的降级方案Hbase,以及业务的兜底等。

八、缓存与数据库双写时的数据一致性

一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,允许缓存跟数据库偶尔不一致的情况,那么最后好不要做这个一致性方案。

如果实现这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况。

但是串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

还有一种适中的方式就是,就是先更新数据库,然后再删除缓存。可能会暂时产生不一致的情况,但是发生的几率特别小。这时候通常并行写数据库和缓存,可以加个事务,都写成功才成功,有一个环节失败了就回滚事务,全失败。

关于双写一致性的问题,其实可以另起一个篇幅来说了,有兴趣的可以网上搜索一下,后续可能会再进行整理。

以上就是Redis异常测试盘点分析的详细内容,更多关于Redis异常测试的资料请关注我们其它相关文章!

(0)

相关推荐

  • dubbo服务使用redis注册中心的系列异常解决

    目录 前言 1.不支持带密码,设置indexdb的reids 解决方法: 二,集群容错模式异常 三,jedis连接池连接的坑 四,服务超过8个应用启动卡死 文末结语 前言 dubbo支持zookeeper,reids,multicast等注册中心注册服务信息,使用redis作为注册中心时,因为reids作为注册中心使用并不广泛,早期reids由于定位内网访问,使用密码验证也不怎么重视,导致框架本身设计缺陷,会有很多坑,如1.没有考虑到带密码验证的redis,2.集群容错模式判断错误 3.不可以设

  • springboot集成测试里的redis

    测试不应该访问外部资源 对于单元测试,集成测试里,如果被测试的方法中使用到了redis,你需要去模拟一个单机环境的redis server,因为只有这样,你的测试才是客观的,即不会因为网络和其它因素影响你测试的准确性! redis的内嵌版本embedded-redis 它的源码在github上,大家有兴趣可以去看看,非常精简,而且还提供了单机,集群,哨兵多种redis环境,完全可以满足我们的测试需要. 添加依赖 //implementation 'org.springframework.boot

  • 在SpringBoot中注入RedisTemplate实例异常的解决方案

    目录 注入RedisTemplate实例异常 贴出详细的错误日志 最后想再验证一个小的问题 注入RedisTemplate实例异常 最近,在项目开发过程中使用了RedisTemplate,进行单元测试时提示 Field redisTemplate in com.example.demo1.dao.RedisDao required a bean of type ‘org.springframework.data.redis.core.RedisTemplate’ that could not b

  • Redis 执行性能测试

    Redis 性能测试是通过同时执行多个命令实现的. 语法 redis 性能测试的基本命令如下: redis-benchmark [option] [option value] 注意:该命令是在 redis 的目录下执行的,而不是 redis 客户端的内部指令. 实例 以下实例同时执行 10000 个请求来检测性能: $ redis-benchmark -n 10000 -q PING_INLINE: 141043.72 requests per second PING_BULK: 142857.

  • Redis异常测试盘点分析

    目录 Redis测试中的异常 一.更新 Key 异常 二.Key的删除和丢失 三.KEY 过期策略不当造成内存泄漏 四.查询Redis异常时处理 五.redis 穿透.击穿.雪崩 六.Redis死锁 SET Key UniqId Seconds 分布式Redis锁:Redlock 七.Redis持久化 八.缓存与数据库双写时的数据一致性 Redis测试中的异常 在测试工作中,涉及到与 redis 交互的场景变的越来越多了.关于redis本身就不作赘述了,网上随便搜,本人也做过一些整理. 今天只来

  • 盘点MQ中的异常测试

    目录 前言 一.RocketMQ 消息模式 集群消费模式 广播消费模式 二.push 和 pull 优缺点 Pull方式 Push方式 三.刷盘策略 同步刷盘 异步刷盘 四.MQ 异常测试 MQ消息体 消息重复发送 消息到达顺序不一致 消息发送失败重试 接线上生产者 消息丢失 消息争用 MQ比落库快 前言 上一篇小结了一下关于redis的异常测试,今天再来盘一盘 MQ 相关的. MQ 跟 redis 一样,也是现在系统服务中不可或缺的重要中间件,通常用来流量削峰.应用解耦.异步处理等. 之前有过

  • Spring @Cacheable redis异常不影响正常业务方案

    背景 项目中,使用@Cacheable进行数据缓存.发现:当redis宕机之后,@Cacheable注解的方法并未进行缓存冲突,而是直接抛出异常.而这样的异常会导致服务不可用. 原因分析 我们是通过@EnableCaching进行缓存启用的,因此可以先看@EnableCaching的相关注释 通过@EnableCaching的类注释可发现,spring cache的核心配置接口为:org.springframework.cache.annotation.CachingConfigurer /**

  • java中继承测试代码分析

    继承:可以基于已经存在的类构造一个新类.继承已经存在的类就可以复用这些类的方法和域.在此基础上,可以添加新的方法和域,从而扩充了类的功能. public class ExtendsStu { /*动物类:动物都可以动 * 1.Dog 2.Cat * 在java中,子类可以继承父类的属性和功能; * 继承关系的指定: 子类 extends 父类 * 不能被继承的资源: * 1.子类不能继承父类的构造方法,而且必须调用一个父类的构造器(因为生成子类对象的时候会初始化父类属性) * 2.私有的资源不能

  • Java 异步线程监听与结果回调及异常捕获总结分析

    前言 工作中是否遇到这样的场景? 1.需要异步线程执行,而且需要获取到线程执行返回的结果. 2.如果执行过程异常,可以按照自定义方式消费异常信息. 如果只是单纯的使用Callable可以实现,本文提供更加优雅的工具类. Maven依赖 <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.15</ver

  • Redis exists命令bug分析(案例详解)

    目录 1.复现条件版本: 2.源码分析 3.问题解决 本文基于社区版Redis 4.0.8 1.复现条件版本: 社区版Redis 4.0.10以下版本 使用场景:开启读写分离的主从架构或者集群架构(master只负责写流量,slave负责读流量) 案例: # 写入一条带过期时间10s的key 10.90.73.147:12345> set luxiu1 1 ex 10 OK 10.90.73.147:12345> get luxiu1 "1" 10.90.73.147:12

  • 盘点分析C语言中少见却强大的字符串函数

    目录 正片开始 字符串函数 strcpy strcat strcmp strstr strtok strerror 正片开始 字符串函数 首先神魔是字符串函数? 指的是编程语言中用来进行字符串处理的函数,如C,pascal,Visual以及LotusScript中进行字符串拷贝,计算长度,字符查找等的函数. 功能:把src所指由NUL结束的字符串复制到dest所指的 数组 中. 返回指向dest结尾处字符 (NUL)的 指针 . 像之前我写到过的 strcpy,strcat,strcmp 这些函

  • Java Exception异常全方面分析

    目录 一.什么是异常? Java的异常体系 如何排查异常 二. 处理异常 try…catch基本语法 这个时候就要处理异常 如果我们用Exception来捕获异常呢? finally 在方法中出现异常 异常处理流程 手动抛出异常 三.自定义异常 源码剖析 自定义 注意 如下实例: public class Demo { public static void main(String[] args) { int num = 2/0; } } 这段代码中“除0”的逻辑在C语言中就只是报个警告,但是Ja

  • Redis 异常 read error on connection 的解决方案

    目录 一.造成原因与场景 二.出现原因 1)原因1 使用了已经断开的连接 2)原因2:执行超时 三.解决方案 1)如果是原因1 2)如果是原因2:修改超时时间 一.造成原因与场景 在接口连接 redis 时,使用了 pconnect 进行 redis 长连接,在 Redis->auth() 中抛出异常: PHP Fatal error: Uncaught exception 'RedisException' with message 'read error on connection' 注:由于

  • Redis分布式锁实例分析讲解

    目录 1 一人一单并发安全问题 2 分布式锁的原理和实现 2.1 什么是分布式锁 2.2 分布式锁的实现 1 一人一单并发安全问题 之前一人一单的业务使用的悲观锁,在分布式系统下,是无法生效的. 理想的情况下是这样的:一个线程成功获取互斥锁,并对查询订单并创建订单,其他线程无法干预.它的原理是会有一个锁监视器,来监听是谁获得了锁. 但是问题就出现在: 分布式系统下,有多个不同的JVM,不同的JVM的环境下,锁监听器是有多个的,就会出现有的线程在别的线程已经拿到锁的情况下,仍然可以获取的到锁. 这

随机推荐