缓存穿透
缓存穿透是指用户请求的数据在缓存中不存在即没有命中,同时在数据库中也不存在,导致用户每次请求该数据都要去数据库中查询一遍,然后返回空。
常见与恶意攻击时,不断请求系统中不存在的数据,导致短时间内大量请求缓存中找不到落到数据库中,同时因为不存在,也不会刷新到缓存中,导致数据库压力过大,甚至导致数据库崩溃。
常见解决方法
(1)缓存空对象
当缓存未命中,而且查询数据库也为空时,可以将返回的空对象写到缓存中,这样下次请求的时候,就不会落到数据库中,
存在问题:
- 如果有大量的key穿透,会占用不必要的内存空间(可以设置标识,共用同一缓存)。
- 过期时间内,会导致缓存与数据库不一致的问题(当数据库变化时,主动刷新缓存)。
(2)布隆过滤器
布隆过滤器由一个长度为m比特的位数组(bit array)与k个哈希函数(hash function)组成的数据结构。位数组初始化均为0,所有的哈希函数都可以分别把输入数据尽量均匀地散列。
当要向布隆过滤器中插入一个元素时,会经过n个哈希函数计算出n个哈希值,以哈希值为数组下标,将所有n个对应的比特值置为1.
当对应哈希值为数组下标的值,存在任意一个比特为0,则认为这个元素一定不在该集合中,如果所有比特均为1,则认为这个元素有可能在集合中(因为存在哈希碰撞,所以不能完全判断在集合中)。
这种不确定性,可能增加足够多的哈希函数,来减少此不确定性,当然,这也会造成,该数组能够判断的元素量级变少。
优点:
- 节省空间:在只需要判断是否存在的场景下(比如用户名不能重复),不需要存储数据本身,只需要存储数据对应的哈希比特位
- 时间复杂度:插入和查找的时间复杂度都为O(k),k为哈希函数的个数。
缺点:
- 存在误差,该误差取决于哈希函数的个数,需要根据元素量做权衡
- 不能删除元素
缓存击穿
缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
这个现象会导致数据库瞬间压力暴增,造成大量请求阻塞。
常见解决方式
(1)使用互斥锁
就是让一个线程去完成查库写回缓存工作,其他线程等待完成之后再读缓存。
但是在高并发的场景下,会造成大量线程阻塞,降低吞吐量。
(2)热点数据永不过期
- key的有效期直接不设置(对于运维和长期维护来说不友好)
- 通过后台异步线程对缓存进行更新,续期(key的量过多时,会造成更新量巨大,对热点数据的定义需要更好的规划)
(3)双层缓存策略
使用主备两层缓存:
主缓存:有效期按照经验值设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值。
备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。
缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,请求直接落到数据库上,引起数据库压力过大甚至宕机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
常见解决方式
(1)均匀过期
设置不同的过期时间,比如增加随机值或者统一规划。
但是在key过多时,该问题仍存在
(2)加互斥锁
跟缓存击穿解决思路一致,同一时间只让一个线程构建缓存,其他线程阻塞排队。
高并发情况下,会影响吞吐量。
(3)缓存永不过期
跟缓存击穿解决思路一致,缓存在物理上永远不过期,用一个异步的线程更新缓存。
(4)双层缓存策略
使用主备两层缓存:
主缓存:有效期按照经验设置,设置为主读取的缓存,主缓存失效后从数据库加载最新值。
备份缓存:有效期长,获取锁失败时读取的缓存,主缓存更新时需要同步更新备份缓存。
缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候,先查询数据库,然后再将数据回写到缓存。
如果不进行预热, 那么 Redis 初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。
常见解决方式:
- 数据量不大的时候,工程启动的时候进行加载缓存动作;
- 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
- 数据量太大的时候,优先保证热点数据进行提前加载到缓存。
缓存降级
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。
常见解决方式
在项目实战中通常会将部分热点数据缓存到服务的内存中,这样一旦缓存出现异常,可以直接使用服务的内存数据,从而避免数据库遭受巨大压力。
集群脑裂
因为网络原因,master、slave、sentinel在不同的网络分区,当sentinel无法感知master时,会将slave升级为master,此时,服务器存在两个master,而部分客户端还可能连接原有master,当网络恢复,将原master降为slave时,会造成数据丢失
min-replicas-to-write 3 表示连接到master的最少slave数量
min-replicas-max-lag 10 表示slave连接到master的最大延迟时间
如果连接到master的slave数量 < 第一个参数 且 ping的延迟时间 <= 第二个参数那么master就会拒绝写请求,配置了这两个参数后如果发生了集群脑裂则原先的master节点接收到客户端的写入请求会拒绝就可以减少数据同步之后的数据丢失。