分布式缓存面临的常见问题及其解决方案

本文介绍了缓存穿透、缓存击穿和缓存雪崩问题的解决方案。对于缓存穿透,提出了缓存空数据、验证拦截和使用布隆过滤器的方法。缓存击穿的对策包括设置热点数据永不过期、应用级别锁和分布式锁。针对缓存雪崩,建议采用高可用缓存、缓存降级和快速预热。在数据一致性方面,提出了在双写时的处理策略。
摘要由CSDN通过智能技术生成

一、缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起 id 为-1 的数据或者特别大的不存在的数据。有可能是黑客利用漏洞攻击从而去压垮应用的数据库。

解决方案

对于缓存穿透问题,常见的解决方案有以下三种:

  • 验证拦截:接口层进行校验,如鉴定用户权限,对 ID 之类的字段做基础的校验,如 id<=0 的字段直接拦截;

  • 缓存空数据:当数据库查询到的数据为空时,也将这条数据进行缓存,但缓存的有效性设置得要较短,以免影响正常数据的缓存;

  • 使用布隆过滤器:布隆过滤器是一种比较独特数据结构,有一定的误差。当它指定一个数据存在时,它不一定存在,但是当它指定一个数据不存在时,那么它一定是不存在的。

方案1:缓存空数据

当数据库查询到的数据为空时,也将这条数据进行缓存,但缓存的有效性设置得要较短,以免影响正常数据的缓存;

  • 优点:

    1. 该方案可以保证对于数据库的请求时一次性的,可以拦截后续重复的请求
    2. 实现简单、直接
  • 不足:

    1. 如果攻击提供的查询值不重复,则无法有效缓存
    2. 不断累加的缓存 key 浪费缓存空间
方案2:验证拦截

接口层进行校验,如鉴定用户权限,对 ID 之类的字段做基础的校验,如 id<=0 的字段直接拦截;维护最大 id 缓存信息,超出的数据直接拒绝

  • 优势:
    1. 在发布内容后需要维护最大 id 信息
    2. 实现也相对比较简单
  • 不足:
    1. 仅满足关键值如 id 信息连续或数据不稀疏的场景
    2. 维护最大 id 等需要原子性以保持并发和递增等一致性
    3. 最大 id 缓存存在一定的初次写入成本
    4. 数据查询数据库时,多一次缓存 id 判定操作
方案3:使用布隆过滤器

布隆过滤器是一种比较独特数据结构,有一定的误差。当它指定一个数据存在时,它不一定存在,但是当它指定一个数据不存在时,那么它一定是不存在的。

而 BloomFilter 实现原理也很简单,首先用多个 bit 位去代替 HashMap 中的数组,这样的话储存空间就下来了,之后就是对 Key 进行多次哈希,将 Key 哈希后的值所对应的 bit 位置为 1。

当判断一个元素是否存在时,就去判断这个值哈希出来的比特位是否都为 1,如果都为 1,那么可能存在,也可能不存在(如下图 F)。但是如果有一个 bit 位不为 1,那么这个 Key 就肯定不存在。

注意:BloomFilter 并不支持删除操作,只支持添加操作。这一点很容易理解,因为你如果要删除数据,就得将对应的 bit 位置为 0,但是你这个 Key 对应的 bit 位可能其他的 Key 也对应着。

  • 优势:
    1. 有开源的工具类可以使用且可实现 redis 分布式版本
    2. 空间占用少
  • 不足:
    1. 多次哈希性能开销
    2. 对于存在的数据有一定误判率,且随着数据增加,误判率会增大
    3. 对于存在数据需要启动并热数据
    4. 无法删除数据,不适合数据删除场景较多的情况
    5. 缓存雪崩

二、缓存击穿

缓存击穿是指当前热点数据存储到期时,多个线程同时并发访问热点数据。因为缓存刚过期,所有并发请求都会到数据库中查询数据。主要是由于数据访问用户并发高,由于缓存失效等原因,并发请求同时发起数据库读取操作,引起数据库压力瞬间增大的情况。

解决方案

方案1:设置热点数据永不过期
  • 优势:
    1. 实现最简单
  • 不足:
    1. 需要 task 等其他方式维护数据更新
方案2:应用级别锁控制并发

单机并发时,应用级别锁控制并发,仅保留 1 个请求去查询并写入缓存,其他请求重试取缓存读取

  • 优势:
    1. 可以减少同时发起请求的并发连接
    2. 应用级别支持,实现相对简单
  • 不足:
    1. 未获取到锁的线程会阻塞,不适合缓存写入等逻辑复杂,用时较长的情况
方案3:使用分布式锁

查询数据库并写入缓存的操作请求需要获取到分布式锁

  • 优势:

    1. 到数据库的并发请求最少
  • 不足:

    1. 需要引入分布式锁操作
    2. 未获取到锁的请求需要定时重试或返回降级内容

三、缓存雪崩

缓存雪崩主要针对大量的缓存在同一时间集体失效,导致大量的查询直接透传到数据库层面,导致 CPU 和内存过载从而压垮数据库。

一个简单的雪崩过程:

  1. Redis 集群产生了大面积故障;
  2. 缓存失败,此时仍有大量请求去访问 Redis 缓存服务器;
  3. 在大量 Redis 请求失败后,这些请求将会去访问数据库;
  4. 由于应用的设计依赖于数据库和 Redis 服务,很快就会造成服务器集群的雪崩,最终导致整个系统的瘫痪。

解决方案

方案1:高可用缓存

高可用缓存是防止出现整个缓存故障。即使个别节点,机器甚至机房都关闭,系统仍然可以提供服务,Redis 哨兵(Sentinel) 和 Redis 集群(Cluster) 都可以做到高可用;

方案2:缓存降级(临时支持)

当访问次数急剧增加导致服务出现问题时,我们如何确保服务仍然可用。在国内使用比较多的是 Hystrix,它通过熔断、降级、限流三个手段来降低雪崩发生后的损失。只要确保数据库不死,系统总可以响应请求,每年的春节 12306 我们不都是这么过来的吗?只要还可以响应起码还有抢到票的机会;

方案3:Redis 备份和快速预热

Redis 数据备份和恢复、快速缓存预热。

四、缓存与数据库双写时的数据一致性

你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?

解决方案

一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况

串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值