一、Redis常见问题以及解决方案
1. 缓存雪崩
- 简介
缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
- 解决方案
- 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
- 对于一定要在固定时间让key失效的场景(例如每日12点准时更新所有最新排名),可以在固定的失效时间时在接口服务端设置随机延时,将请求的时间打散,让一部分查询先将数据缓存起来;
- 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
- 延长热点key的过期时间或者设置永不过期。
2.缓存穿透
- 简介
当查询Redis中没有的数据时,该查询会下沉到数据库层,同时数据库层也没有该数据,当这种情况大量出现或被恶意攻击时,接口的访问全部透过Redis访问数据库,从而造成数据库短时间内承受大量请求而崩掉。
- 解决方案
- 在接口访问层对用户做校验,如接口传参、登陆状态、n秒内访问接口的次数;
- 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
- 利用布隆过滤器,将数据库层有的数据key存储在位数组中,以判断访问的key在底层数据库中是否存在。
- 如果Redis内不存在该数据,则通过布隆过滤器判断数据是否在底层数据库内;
- 如果布隆过滤器告诉我们该key在底层库内不存在,则直接返回null给客户端即可,避免了查询底层数据库的动作;
- 如果布隆过滤器告诉我们该key极有可能在底层数据库内存在,那么将查询下推到底层数据库即可;
布隆过滤器是一个bit数组,每个元素存储数据的状态(由于每个元素只有 1 bit,所以只能存储 0 或 1 这 2 种状态)。
判断是否存在时:给布隆过滤器一个数据,进行hash得到下标,从bit数组里取数据如果是1 则说明数据存在,如果是0 说明不存在
hash算法存在碰撞的可能,所以不同的数据可能hash为一个下标数据,这个误判的失败率跟hash算法的个数以及位数组的长度有关 ,数组越长、hash算法越多、插入元素越少,误判率就会越低。反之则误判率越高。而位数组长度越长或者hash算法越多,也会影响到空间效率和查询效率,因此我们需要根据实际需要合理的去使用布隆过滤器。
布隆过滤器可以判断某个元素一定不存在,但是无法判断它一定存在;
- 优点
- 空间效率高,空间占用小
- 查询效率高
- 缺点
- 不能删除元素(已被更新为1的位数组中的位,不能再被更新为0)
- 有误判率
3. 缓存击穿
- 简介
缓存击穿和缓存穿透从名词上可能很难区分开来,它们的区别是:穿透表示底层数据库没有数据且缓存内也没有数据,击穿表示底层数据库有数据而缓存内没有数据。当热点数据key从缓存内失效时,大量访问同时请求这个数据,就会将查询下沉到数据库层,此时数据库层的负载压力会骤增
- 解决方案
- 延长热点key的过期时间或者设置永不过期,如排行榜,首页等一定会有高并发的接口;
- 利用互斥锁保证同一时刻只有一个客户端可以查询底层数据库的这个数据,一旦查到数据就缓存至Redis内,避免其他大量请求同时穿过Redis访问底层数据库;
4. 为什么Redis是单线程的
- 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
- 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;