1.使用缓存可能会产生的问题

Redis通过set、get命令实现缓存功能是非常简单的,缓存能大大缓解数据访问压力,提升数据访问性能,因为缓存数据存储在内存当中,当缓存数据越来越大时所占用的内存空间也越来越大。Redis一般搭配数据库实现数据缓存功能,Redis实现缓存的步骤如下:
(1).访问数据时首先使用get命令从Redis获取数据,如果有数据就缓存数据。
(2).如果Redis中无缓存数据就使用普通的方式访问数据,并将数据结果存储到Redis,下次使用get命令获取数据时将会从Redis中读取。

1.1 缓存穿透

缓存穿透是指大量不断请求访问缓存和数据源中没有的数据,从而大量请求访问到数据源导致数据源崩溃。
解决办法:

  • 解决缓存穿透的方式有很多种,最常见的解决方式是使用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap(位图)中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力(推荐)。
  • 如果数据源返回的数据为空时(不管数据源中是否存在该数据、或因系统故障等原因),我们仍把这个空结果缓存起来,但它的过期时间会很短(最长不超过五分钟,视业务场景而定)。第一次请求由于缓存中没有数据会将请求转发到数据源中,无论数据源返回什么数据都会存储到缓存当中,下次请求到缓存中缓存一定是有数据的,从而降低对数据源的访问压力,之所以为缓存设置一个短的过期时间是因为降低缓存所占的内存空间。

1.2 缓存击穿

缓存击穿是key对应的缓存数据都存在,但在缓存中已过期,此时若有大量并发请求到缓存,因为缓存数据已过期请求会被转发到数据源,大量并发请求会导致数据源崩溃。
解决方案:缓存击穿最常见的解决方案是使用互斥锁(mutex key)

1.3 缓存雪崩

当缓存服务器重启或大量缓存数据集中在某一时间段同时失效,此时会给数据源造成巨大压力。
解决办法:

  • 避免设置缓存数据的过期时间集中在某段时间内,比如说可以在缓存原有数据的过期时间增加一个随机数,这样每个缓存数据的过期时间的重复率就会降低,缓存雪崩的可能性也会大大降低。
  • 给缓存数据的过期时间设为永不过期,这样就不会出现缓存失效了,但会长期占用内存。

    1.4 缓存与数据库一致性问题

    当数据库进行写操作时,由于某种情况导致缓存数据与数据库数据不一致问题,例如以下两种情况:

  • 如果先删除缓存,此时未进行数据库写操作,另一个线程就来读取缓存,发现缓存为空后,则会向数据库读取数据并将结果写入缓存,此时缓存中的数据为脏数据。我们期望缓存中的数据为数据库写操作成功后向数据库的读取数据。

  • 如果先写数据库,后删除缓存,如果在删除缓存前,写数据库的线程挂掉,就会导致缓存数据未被删除。

解决办法:

  • 基于延迟双删策略解决缓存数据库一致性。写数据库之前或之后都进行删除缓存操作,并设置合理的超时时间。具体步骤如下:
    • (1).删除缓存
    • (2).写数据库
    • (3).休眠xxx毫秒(视业务而定,一般是几百ms)
    • (4).再次删除缓存。

      4.解决缓存与数据库数据一致性问题

      4.1 基于延迟双删策略解决缓存数据库一致性