最近遇到了一个奇怪的问题

cpu异常

系统总是一段时间CPU异常飙升,利用jstack检查java线程并没个别突出异常,但是线程数量很大,每个都占用了一点点CPU,检查线程TID发现大都是对应上tomcat的TaskQueue,怀疑是不是有大量请求。

部署监控SKYwalking

于是就部署了skywalking检查到依赖的jar文件总是向一个服务发送大量请求,而且占用时间很长,但是以前也一样并没有出现这样的问题,不过先和这个服务商沟通检查看看是否存在问题。

多方探索

换个方向,仔细研究一下tomcat日志,发现有特别的地方发现有类序列化失败,redis并没有宕机,但是方法调用不到redis也无法序列化类,对比了 测试环境redis参数,发现memory的数值有一些异常,考虑到之前没有对redis参数作过优化,redis使用内存不足导致,同时也去查看redis存储的数据是否存在回收问题。

最终发现

redis

  • noeviction(默认策略):对于写请求不再提供服务,直接返回错误(DEL请求和部分特殊请求除外)

  • allkeys-lru:从所有key中使用LRU算法进行淘汰算法细节可以查看官方文档

  • volatile-lru:从设置了过期时间的key中使用LRU算法进行淘汰

  • allkeys-random:从所有key中随机淘汰数据

  • volatile-random:从设置了过期时间的key中随机淘汰

  • volatile-ttl:在设置了过期时间的key中,根据key的过期时间进行淘汰,越早过期的越优先被淘汰

PS:当使用volatile-lru、volatile-random、volatile-ttl这三种策略时,如果没有key可以被淘汰,则和noeviction一样返回错误

#没有存入库数据的情况可以直接修改redis内存策略为allkeys-lru
./redis-cli
127.0.0.1:6379> config set maxmemory-policy allkeys-lru

1.针对可以设定内存大小的,设置最大内存,同时可以开启lru机制,考虑关闭dump备份,启用aof备份机制针对不可以设定最大内存大小的,

2.往往这类场景数据也是不可以丢失的,那么可以考虑做切片,引入codis、redis-cluster等方案解决单机内存瓶颈问题。

升华redis三大问题,防止丢失、雪崩、穿透

1、  如果redis宕机了,或者链接不上,怎么办?

解决方法:

    ①配置主从复制,配置哨兵模式(相当于古代门派的长老级别可以选择掌门人的权利),一旦发现主机宕机,让下一个从机当做主机。

    ②如果最坏的情况,只能关闭Redis连接,去往数据库连接。但由于数据量大,这样SQL数据库也会宕掉的。

 

2、  如果redis缓存在高峰期到期失效,在这个时刻请求会向雪崩一样,直接访问数据库如何处理?

     设置条件查询判断,判断redis缓存里是否有数据,如果没有,则去往数据库连接。当然要加分布式锁,利用redis的单线程+多路IO复用技术,原子性原理,让其它的线程请求等待,假若第一个线程进去获取到分布式锁在查询数据的途中宕掉了,不能让其它线程一直等待,设置等待一定时间判断是否取回数据,如果没有,递归调用自己的方法让第二个线程继续拿分布式锁查询数据库。当第二个锁从数据库拿到数据时,把数据值设置到redis数据库缓存中,设置失效时间,避免占内存,方便使用提高效率。

3.   如果用户不停地查询一条不存在的数据,缓存没有,数据库也没有,那么会出现什么

    如果数据不存在,缓存中没有,数据库也没有,当然如果不设置判断,会一直调用数据库,使数据库效率降低,访问量大时甚至会宕机。

    解决方案:从数据库查询,如果数据库没有,则返回值为Null,判断数据库返回的值,如果为Null,则自定义把标识的字段存到Redis中,用key,value的方法,jedis.setex(key,"empty"),设置失效时间跟具体情况而定,然后调用String json=jedis.get(key),判断是否获取的值"empty".equal(json),如果相等,则抛出自定义异常,给用户提示,或者直接return null。这样用户再次查询的时候由于先从reids缓存中查询,redis会有对应的Key获取之前设置的value值,这样就不会再次调用数据库,影响效率等问题。

这里还说明一个问题,第一时间仔细检查错误日志才是解决问题的王道。

致谢

Redis内存满了怎么办————LIUSHIJUN

redis官方文档

Redis 高并发问题,及解决方案!————@溪竹