因为csdn只支持这种文档形式:不支持思维导图:
更好友好的阅读:可以看我的飞书--------思维导图(这样食用更加):
缓存穿透问题原因每次从缓存中都查不到数据,而需要查询数据库,同时数据库中也没有查到该数据,也没法放入缓存如何解决布隆过滤器原理:布隆过滤器可以用于检索一个元素是否在一个集合中。结构:概率型数据结构使用方式一:通过自己写算法实现布隆过滤器方式二:使用网上存在的开源包推荐指数:⭐⭐⭐(这个技术有些过时了,但是可以用)优点:算法简单,可以自己配置缺点:存在一定的误判率不支持删除操作查找性能比布谷鸟底空间利用率低布谷鸟过滤器要求条件:适用于对误判率要求较高的场景。如网络路由、存储系统等原理:最简单的布谷鸟哈希结构是一维数组结构,会有两个 hash 算法将新来的元素映射到数组的两个位置。如果两个位置中有一个位置为空,那么就可以将元素直接放进去。但是如果这两个位置都满了,它就不得不「鸠占鹊巢」,随机踢走一个,然后自己霸占了这个位置。近似集合数据结构如何使用方式一:自己写一个算法(我试过,比较复杂)方式二:调用别人写的库情况优点:相较于布隆过滤器具有更低的误判率支持删除操作查找性能高空间利用率高缺点:删除不完美,存在误删的概率。删除的时候只是删除了一份指纹副本,并不能确定此指纹副本是要删除的key的指纹。同时这个问题也导致了假阳性的情况。插入复杂度比较高随着插入元素的增多,复杂度会越来越高,因为存在桶满,踢出的操作,所以需要重新计算,但综合来讲复杂度还是常数级别。存储空间的大小必须为2的指数让空间效率打了折扣同一个元素最多插入kb次(k指哈希函数的个数,b指的桶中能装指纹的个数)如果布谷鸟过滤器支持删除,则必须存储同一项的多个副本。 插入同一项kb+1次将导致插入失败。 这类似于计数布隆过滤器,其中重复插入会导致计数器溢出。推荐指数:⭐⭐⭐⭐⭐(绝对好用)缓存空对象方案一要求条件:适中推荐指数:⭐⭐⭐⭐(很常见的方式)设置缓存的时候,同时设置一个过期时间,这样过期之后,就会重新去数据库查询最新的数据并缓存起来方案二要求条件:实时性要求非常高推荐指数:⭐⭐⭐(要结合实际情况,考虑双写一致性的问题)如果对的话,那就写数据库的时候,同时写缓存。这样可以保障实时性方案三要求条件:实时性要求不是那么高推荐指数:⭐⭐⭐⭐(非常实用的方式)那就写数据库的时候给消息队列发一条数据,让消息队列再通知处理缓存的逻辑去数据库取出最新的数据校验参数原理:我们可以对用户id做检验。(请求合法性校验)比如权限管理推荐指数:⭐⭐⭐⭐⭐(这个必须做)缓存击穿问题原因缓存击穿问题是由于key过期了导致的如何解决加锁原理:在缓存失效的瞬间,通过加锁机制来保证只有一个请求能够访问数据库,其他请求等待获取缓存数据。使用:同一时刻只有一个请求才能访问某个信息并且另一个线程将数据库中查询到的结果,又重新放入缓存中推荐指数:⭐⭐⭐(非必要不加锁)自动续期原理:在key快要过期之前,就自动给它续期推荐指数:⭐⭐⭐(本来要过期,但是续期的指令如何识别)缓存不失效原理:对于很多热门key,其实是可以不用设置过期时间,让其永久有效的。推荐指数:⭐⭐⭐⭐(正对某些业务可以设置)缓存雪崩
原因:有多个热门key同时失效(非常严重):归根结底都是有大量的请求,透过缓存,而直接访问数据库了有大量的热门缓存,同时失效。会导致大量的请求,访问数据库。而数据库很有可能因为扛不住压力,而直接挂掉。缓存服务器宕机了,可能是机器硬件问题,或者机房网络问题。总之,造成了整个缓存的不可用。解决办法过期时间加随机数推荐指数:⭐⭐⭐⭐(较为安全)要求不要设置相同的过期时间即使在高并发的情况下,多个请求同时设置过期时间,由于有随机数的存在,也不会出现太多相同的过期key高可用(分布式部署)推荐指数:⭐⭐⭐⭐(能在源头处理最好)深层原因:针对缓存服务器宕机的情况,原理:通过多个节点来分担缓存的压力,提高系统的容灾性解决办法:在前期做系统设计时,可以做一些高可用架构redis可以使用哨兵模式,或者集群模式,避免出现单节点故障导致整个redis服务不可用的情况服务降级(备份机制)推荐指数:⭐⭐⭐⭐(做一个数据兜底)更深的原因:做了高可用架构,redis服务还是挂了,该怎么办呢?解决办法:配置一些默认的兜底数据。程序中有个全局开关,比如有10个请求在最近一分钟内,从redis中获取数据失败,则全局开关打开。就直接从配置中心中获取默认的数据。还需要有个线程,每隔一定时间去从redis中获取数据数据预热原理:数据预热就是系统上线后,将相关的缓存数据直接加载到缓存系统,这样就可以避免在用户请求的时候先查询数据库方式:在系统低峰期,提前对热点数据进行加载和缓存,避免大量数据同时失效推荐指数:⭐⭐⭐⭐(不要等高峰了做预案)双层缓存策略C1为原始缓存,C2为拷贝缓存,C1失效时可以访问C2,C1缓存失效时间设置为短期,C2缓存失效时间设置为长期推荐指数:⭐⭐⭐(万一都宕机就完蛋了)定时更新缓存策略原理:失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存使用:另起一个线程,去做任务处理推荐指数:⭐⭐⭐⭐⭐(前期预防永远比后期解决来的好)双写一致性(redis与mysql)
原因:分布式常见问题数据库和缓存双写,就必然会存在不一致的问题最终一致性:要保证数据库与缓存可以先后一致强一致性:有强一致性要求的数据,不能放缓存解决方法:更新缓存先写缓存,写数据库问题程度:上上(很严重)问题原因:刚写完缓存,突然网络出现了异常,导致写数据库失败了推荐指数:⭐⭐(用的不多)造成影响:容易诞生假数据先写数据库,在写缓存问题情况:写缓存失败了问题程度:上 (严重)问题原因:写数据库和写缓存,都属于远程操作。(通常建议写数据库和写缓存不要放在同一个事务中)写数据库成功了,但写缓存失败了,数据库中已写入的数据不会回滚推荐指数:⭐⭐ ⭐(接口性能要求不太高的系统)造成影响:数据库是新数据,而缓存是旧数据,两边数据不一致的情况问题情况:高并发先写数据库,在写缓存问题程度:上上 (很严重)问题原因:请求b在缓存中的新数据,被请求a的旧数据覆盖了。(请求互相覆盖)推荐指数:⭐⭐(高并发场景,不适合)造成影响:可能会出现数据库是新值,而缓存中是旧值,两边数据不一致的情况问题情况:浪费系统资源问题程度:上(严重)问题原因:每写一次缓存,都需要经过一次非常复杂的计算推荐指数:⭐⭐(计算要求低的情况)造成影响:浪费cpu和内存资源删除缓存先删缓存,在写数据库高并发下,单删问题程度:中上(适度)问题原因:请求A删除完缓存数据Data后,网络卡顿,写入数据库操作暂停。请求B读数据Data,缓存中没有,去数据库读,读的是旧数据。请求A网络好了,写入新数据到数据库推荐指数:⭐⭐⭐(低并发下)造成影响:请求A的新值并没有被请求B写入缓存,同样会导致缓存和数据库的数据不一致的情况高并发下,缓存双删问题程度:中下 (良好)问题原因:写数据库之前删除缓存一次,写完数据库后,再删除缓存一次推荐指数:⭐⭐⭐⭐(看情况推荐使用)使用要点:第二次删除缓存,并非立马就删,而是要在一定的时间间隔之后。原因:在另一个请求未生效前,删除这就没有意义了造成影响:第二次删除可能失败解决办法:重试机制原理:更新了数据库成功了,但更新缓存失败了,可以立刻重试N次。如果其中有任何一次成功,则直接返回成功。如果N次都失败了,则写入数据库,准备后续再处理。同步重试推荐指数:⭐⭐⭐()原因:接口并发量比较高的时候,可能有点影响接口性能异步重试推荐指数:⭐⭐⭐⭐⭐方式一:每次都单独起一个线程,该线程专门做重试的工作推荐指数:⭐⭐⭐原因:在高并发的场景下,可能会创建太多的线程,导致系统OOM问题方式二:将重试的任务交给线程池处理推荐指数:⭐⭐⭐⭐原因:如果服务器重启,部分数据可能会丢失方式三:将重试数据写表存入数据库(定时任务)推荐指数:⭐⭐⭐⭐(不适合实时性要求特别高的业务场景)使用:定时任务进行重试重试表(至少得有一下字段):重试次数字段是否成功的状态字段原理:设置初始值,每次删除重试次数字段加1,只要任意有一次成功就返回成功,同时修改状态,等待后续进一步处理缺点:实时性没那么高优点:数据是落库的,不会丢数据方式四:将重试的请求写入mq等消息中间件推荐指数:⭐⭐⭐⭐⭐(实时性还是比较高的)使用:mq的consumer中处理原理:操作写完数据库,但删除缓存失败了,产生一条mq消息,发送给mq服务器mq消费者读取mq消息,如果其中有任意一次成功了,则返回成功。重试N此后还是失败,写入死信队列中,后续需要人工处理优点:mq的实时性还是比较高的优化:删除缓存可以完全走异步。即用户的写操作,在写完数据库之后,不用立刻删除一次缓存。而直接发送mq消息,到mq服务器,然后有mq消费者全权负责删除缓存的任务。方式五:订阅mysql的binlog推荐指数:⭐⭐⭐⭐⭐(强烈推荐)原理:订阅者中,如果发现了更新数据请求,则删除相应的缓存(监听binlog)使用:canal等中间件,实现原理:业务接口中写数据库之后,就不管了,直接返回成功,mysql服务器会自动把变更的数据写入binlog中,binlog订阅者获取变更的数据,然后删除缓存问题:也会删除失败解决办法:重试机制推荐使用方式三和方式四(当推荐方式四)先写数据库,再删缓存问题程度:下上 (优秀)问题原因:缓存过期失效,请求A查询数据发现缓存没有,去数据库查询,但是网络原因,没有及时更新缓存。请求B先写数据库,然后删除缓存。请求A恢复,更新缓存。诞生条件(同时满足概率小)缓存刚好自动失效请求A查询数据库旧值以及更新缓存数据的时间,比请求B写入数据库以及删除缓存数据的时间长一般数据库查询比数据库写入时间短(那种复合,聚合查询就不好说了)推荐指数:⭐⭐⭐⭐⭐ (推荐使用)使用指南:请求查询不要太过复杂造成影响:最差的情况就是读取到旧数据(问题不算太大,刷新一下,重新请求,数据就对了),删除可能失效解决办法:重试机制原理:更新了数据库成功了,但更新缓存失败了,可以立刻重试N次。如果其中有任何一次成功,则直接返回成功。如果N次都失败了,则写入数据库,准备后续再处理。同步重试推荐指数:⭐⭐⭐(适合低并发)原因:接口并发量比较高的时候,可能有点影响接口性能异步重试推荐指数:⭐⭐⭐⭐⭐(常见操作)方式一:每次都单独起一个线程,该线程专门做重试的工作推荐指数:⭐⭐⭐原因:在高并发的场景下,可能会创建太多的线程,导致系统OOM问题方式二:将重试的任务交给线程池处理推荐指数:⭐⭐⭐⭐原因:如果服务器重启,部分数据可能会丢失方式三:将重试数据写表存入数据库(定时任务)推荐指数:⭐⭐⭐⭐(不适合实时性要求特别高的业务场景)使用:定时任务进行重试重试表(至少得有一下字段):重试次数字段是否成功的状态字段原理:设置初始值,每次删除重试次数字段加1,只要任意有一次成功就返回成功,同时修改状态,等待后续进一步处理缺点:实时性没那么高优点:数据是落库的,不会丢数据方式四:将重试的请求写入mq等消息中间件推荐指数:⭐⭐⭐⭐⭐(实时性还是比较高的)使用:mq的consumer中处理原理:操作写完数据库,但删除缓存失败了,产生一条mq消息,发送给mq服务器mq消费者读取mq消息,如果其中有任意一次成功了,则返回成功。重试N此后还是失败,写入死信队列中,后续需要人工处理优点:mq的实时性还是比较高的优化:删除缓存可以完全走异步。即用户的写操作,在写完数据库之后,不用立刻删除一次缓存。而直接发送mq消息,到mq服务器,然后有mq消费者全权负责删除缓存的任务。方式五:订阅mysql的binlog推荐指数:⭐⭐⭐⭐⭐(强烈推荐)原理:订阅者中,如果发现了更新数据请求,则删除相应的缓存(监听binlog)使用:canal等中间件,实现原理:业务接口中写数据库之后,就不管了,直接返回成功,mysql服务器会自动把变更的数据写入binlog中,binlog订阅者获取变更的数据,然后删除缓存问题:也会删除失败解决办法:重试机制推荐使用方式三和方式四(当推荐方式四)缓存的并发竞争
原因:多个redis的client同时set key引起的并发问题多客户端同时并发写一个key,一个key的值是1,本来按顺序修改为2,3,4,最后是4,但是顺序变成了4,3,2,最后变成了2。解决方案:乐观锁推荐指数:⭐⭐⭐(乐观锁适用于大家一起抢着改同一个key)使用:watch 命令可以方便的实现乐观锁缺点:如果 redis 使用了数据分片的方式,那么这个方法就不适用了什么是数据分片?原理:Redis的分片机制允许数据拆分存放在不同的Redis实例上,每个Redis实例只包含所有键的子集优点:可以减轻单台Redis的压力,提升Redis扩展能力和计算能力分布式锁+时间戳原理:加锁的目的实际上就是把并行读写改成串行读写的方式,从而来避免资源竞争用一个状态值表示锁,对锁的占用和释放通过状态值来标识加锁使用方法:使用:使用redis中setnx()函数返回1,则客户端获得锁,把锁的键值设置为时间值,表示该键已经被锁定,可以通过DEL lock.foo来释放该锁返回 0 ,则表明该锁已经被其他客户端取得,这时返回重试等待对方完成,或者等待锁超时,在或者返回推荐指数:⭐⭐⭐⭐(适合分布式环境)优点:不用关心 redis 是否为分片集群模式时间戳使用:(适合有序场景)原理:系统B先抢到锁,将key1设置为{ValueB 7:05}。接下来系统A抢到锁,发现自己的key1的时间戳早于缓存中的时间戳(7:00<7:05),那就不做set操作了推荐指数:⭐⭐⭐(适合对于有序情况)优点:业务处理有序消息队列原理:把Redis.set操作放在队列中使其串行化,及一个一个执行(较为通用的解决方案)推荐指数:⭐⭐⭐⭐(高并发场景中)优点:串行化