Redis 缓存穿透及解决方法(2021)

阅读时长 7 分钟读完

简介

Redis 是一个高性能的开源内存数据结构存储系统,广泛应用于缓存和消息队列等领域。在web开发中,Redis 通常被用来缓存一些频繁访问的数据,以减少数据库的访问次数,从而提高系统的性能。然而,如果缓存中不存在的数据一直被频繁的访问,就会对缓存系统造成压力,这就是缓存穿透问题。

缓存穿透

缓存穿透是指缓存系统中没有缓存的数据被频繁的访问,从而导致缓存系统无法发挥作用,最终造成系统性能的下降。缓存穿透问题通常由恶意攻击或者系统设计不当导致。

下面是一个示例代码,模拟了一次缓存穿透的情景。

-- -------------------- ---- -------
--------
-------- ----------------------
  -------------------
  --- ----- - -------------------
  -----------
    ------------------------
    ----- - -------------------------
    -------------------------
    -----------
      ------ -----
    -
    -----------------
    ------------------ -------
  -
  ------ ------
-

--------
--- ------- - -----
--- ----- - ----------------------
----------
  -------------------
- -----
  -------------------------------
-

从该示例代码中,可以看出如果商品信息不存在,会先从Redis缓存中获取,如果Redis中不存在,就会从数据库中获取。在缓存穿透的情况下,攻击者会不断的请求不存在的数据,这就会导致大量的空请求访问到数据库中,从而导致数据库宕机。

解决方法

为了解决缓存穿透的问题,可以使用以下三种方法:

1. 缓存空对象

将无法从数据库中得到信息的商品,也写入Redis缓存中,但是缓存过期时间可以设置短一些,例如10秒钟。这样在10秒钟内,就可以从缓存中获取到空对象。这样就可以避免大量的数据库访问。

下面是一个示例代码:

-- -------------------- ---- -------
--------
-------- ----------------------
  -------------------
  --- ----- - -------------------
  --------- -- ----- --- ------
    ------------------------
    ----- - -------------------------
    ---------------------------------
    -----------
      ------------------ ----- ---- --------
    - -----
      -----------------
      ------------------ -------
    -
  -
  ------ ------
-

--------
--- ------- - -----
--- ----- - ----------------------
-------- --- ------
  -------------------
- -----
  -------------------------------
-

从示例代码中,可以发现在数据库中不存在的商品信息,也会被写入缓存中,并且设置了一个短暂的过期时间。这样再次请求该商品信息的时候,就可以从缓存中获取到空对象,避免了请求到数据库中。

2. 布隆过滤器

布隆过滤器是一种非常高效的数据结构,用来判断某个元素是否存在于某个集合中。它可以快速的判断一个元素是否存在于一个集合中,从而可以避免了缓存和数据库的访问,节省了系统的开销。布隆过滤器有一个不可避免的问题,就是存在一定的误判率,但是误判率可以通过设置合理的参数来降低到很低的程度。

下面是一个示例代码:

-- -------------------- ---- -------
----------------------
--- ----------- - --- -------------------- -------

--------
-------- ----------------------
  ---------------------
  -----------------------------------
    ------------------------
    -------------------------------
    ------ -----
  -

  -------------------
  --- ----- - -------------------
  -----------
    ------------------------
    ----- - -------------------------
    -------------------------------------
    -----------
      -------------------------
    - -----
      -----------------
      ------------------ -------
    -
  -
  ------ ------
-

--------
--- ------- - -----
--- ----- - ----------------------
----------
  -------------------
-

从示例代码中可以看出,在获取商品信息之前,先用布隆过滤器查询该商品信息是否存在,不存在则直接返回。这样就可以避免大量的请求进入缓存和数据库。

3. 加锁

如果没有缓存,就需要从数据库中获取数据,并且在获取之后,需要将数据写入缓存中。在写入缓存之前,需要加锁,防止多个线程同时写入缓存,从而避免了缓存穿透。加锁的时候需要注意一些细节问题,例如加锁时间不能过长,否则会影响到系统的性能。

下面是一个示例代码:

-- -------------------- ---- -------
----------
-------- ----------------------
  -------------------
  --- ----- - -------------------
  -----------
    ---------------------
    ---------------------------------
      ----------------------------
      ----- - -------------------
      -----------
        --------------------------
        ----- - -------------------------
        ---------------------------------
        -----------
          ------------------ ----- ---- --------
        - -----
          -----------------
          ------------------ -------
        -
      -
      ----
      -------------------------------
    - -----
      --------------------------
      -----------
      ----- - ----------------------
    -
  -
  ------ ------
-

--------
--- ------- - -----
--- ----- - ----------------------
-------- --- ------
  -------------------
- -----
  -------------------------------
-

从示例代码中可以看出,先尝试从 Redis 中获取缓存,如果不存在就加锁,防止其他线程同时写入缓存,然后重新获取一次缓存的商品信息,如果还是不存在该商品信息,则从数据库中获取。获取完之后,需要解锁,避免其他线程等待过长的时间。如果加锁失败,等待一段时间后再次获取,避免过多的请求数据库。

结论

Redis 缓存穿透问题是 Web 应用中的一个大问题,如果不处理,可能会导致性能问题,迫使你的服务器负载变高,甚至可以让你的服务器宕机。为了解决这个问题,需要使用多种方法,最好的解决方案是根据实际情况选择不同的方法,根据业务情况使用合适方案,有效处理缓存穿透的问题。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67446379c22b09372b15dc09

纠错
反馈