1.redis为什么这么快
- 完全基于内存,绝大部分请求是存粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
- 数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,不会因为可能出现死锁而导致的性能消耗;
- 使用多路I/O复用模型,非阻塞IO
- Redis直接自己构建了VM机制;
2.为什么Redis是单线程的
因为Redis是基于内存的操作,CPU不是Redis的瓶颈。Redis的瓶颈最有可能是机器内存的大小或者网络带宽。单线程也容易实现。
虽然CPU不是瓶颈,但是多个CPU放着不用也难受。所以官方从Redis4.0后的版本部分特定功能开始支持多线程。具体查看官网FAQ
3.缓存雪崩
**原因:**原有缓存失效,新缓存未到期间(e.g.,我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期)。
**后果:**原本应该访问缓存的请求都去查询数据库了,对数据库CPU和内存造成巨大压力,严重一点的可能造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。
预防与解决:
- **加锁或队列:**为了防止缓存雪崩后,大量线程对数据库一次性进行读写。可以在对数据库操作的地方加一把锁。从而减轻数据库压力。但是治标不治本。缺点:分布式环境要解决分布式锁问题;线程阻塞,用户体验差。在真正的高并发场景下很少使用。
- **设置过期标志更新缓存:**未每一种key设置过期标志,每次查询判断标志,标志位未过期,直接从缓存获取数据。标志位已过期,通知其他线程把更新此key加入任务在后台更新。并且还是直接从缓存读取老数据。一般标记位过期时间可以比缓存数据的过期时间延长1倍。**优点:**缓存过期前通过判断过期时间更短的标记位,提前加入更新key的任务队列平滑更新。
- 为key设置不同的缓存失效时间
以上针对的缓存一般是用户访问常用数据的缓存。服务端是无法自己得知的。如果是一些服务端提前知道的数据,可以直接开一个定时器,定时查询所有key直接更新缓存即可。
4.缓存穿透
用户查询数据,数据库都没有,自然缓存中也不会有。这就导致缓存未命中-数据库未命中-返回空。这样的请求即使大量重复,也会穿过缓存查询数据库。缓存在这种情况下形同虚设。如果这是一种攻击,那数据库就危险了。这就是缓存穿透。解决方法如下:
- **缓存空结果:**把数据库查询出的结果缓存。即使结果是空。可以设置一个默认值然后缓存起来(为了防止数据过一会有了,这个缓存过期时间要设置很短,最长不超过五分钟)。这样第二次查询缓存就可以命中,不需要再查询数据库了。避免了短暂时间大量查询数据库的行为。简单粗暴。但是呢,如果攻击值不断变换着key发起查询,还是会发生缓存穿透。比如果查询id范围是1-100,对方从1000开始递增查询。这样还是会直接查询到数据库。
- **布隆过滤器:**将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统查询压力。这个就是直接判断了查询的值如果一定不可能存在就直接返回结果了。
5.缓存预热
系统上线后,将相关的缓存数据直接加载到缓存系统。
- 缓存刷新接口,上线时手动操作下
- 数据不大时,项目启动的时候自动进行加载
- 定时刷新缓存
6.缓存更新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),还可以根据业务需求进行自定义的缓存淘汰。
- 定时清理过期的缓存;
- 用户请求过来时,再判断这个请求所用到的缓存是否过期。过期的话就查询新数据更新缓存。
7.缓存降级
缓存时离用户越近越高效,降级是离用户越近越能对系统保护的好。降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。我个人理解是,比如用户写入量特别,db受不了了,就可以把DB降级为Cache,对Cache先进行操作,然后异步到db。