简介
Redis 是一个高性能的开源内存数据结构存储系统,广泛应用于缓存和消息队列等领域。在web开发中,Redis 通常被用来缓存一些频繁访问的数据,以减少数据库的访问次数,从而提高系统的性能。然而,如果缓存中不存在的数据一直被频繁的访问,就会对缓存系统造成压力,这就是缓存穿透问题。
缓存穿透
缓存穿透是指缓存系统中没有缓存的数据被频繁的访问,从而导致缓存系统无法发挥作用,最终造成系统性能的下降。缓存穿透问题通常由恶意攻击或者系统设计不当导致。
下面是一个示例代码,模拟了一次缓存穿透的情景。
-- -------------------- ---- ------- -------- -------- ---------------------- ------------------- --- ----- - ------------------- ----------- ------------------------ ----- - ------------------------- ------------------------- ----------- ------ ----- - ----------------- ------------------ ------- - ------ ------ - -------- --- ------- - ----- --- ----- - ---------------------- ---------- ------------------- - ----- ------------------------------- -
从该示例代码中,可以看出如果商品信息不存在,会先从Redis缓存中获取,如果Redis中不存在,就会从数据库中获取。在缓存穿透的情况下,攻击者会不断的请求不存在的数据,这就会导致大量的空请求访问到数据库中,从而导致数据库宕机。
解决方法
为了解决缓存穿透的问题,可以使用以下三种方法:
1. 缓存空对象
将无法从数据库中得到信息的商品,也写入Redis缓存中,但是缓存过期时间可以设置短一些,例如10秒钟。这样在10秒钟内,就可以从缓存中获取到空对象。这样就可以避免大量的数据库访问。
下面是一个示例代码:
-- -------------------- ---- ------- -------- -------- ---------------------- ------------------- --- ----- - ------------------- --------- -- ----- --- ------ ------------------------ ----- - ------------------------- --------------------------------- ----------- ------------------ ----- ---- -------- - ----- ----------------- ------------------ ------- - - ------ ------ - -------- --- ------- - ----- --- ----- - ---------------------- -------- --- ------ ------------------- - ----- ------------------------------- -
从示例代码中,可以发现在数据库中不存在的商品信息,也会被写入缓存中,并且设置了一个短暂的过期时间。这样再次请求该商品信息的时候,就可以从缓存中获取到空对象,避免了请求到数据库中。
2. 布隆过滤器
布隆过滤器是一种非常高效的数据结构,用来判断某个元素是否存在于某个集合中。它可以快速的判断一个元素是否存在于一个集合中,从而可以避免了缓存和数据库的访问,节省了系统的开销。布隆过滤器有一个不可避免的问题,就是存在一定的误判率,但是误判率可以通过设置合理的参数来降低到很低的程度。
下面是一个示例代码:
-- -------------------- ---- ------- ---------------------- --- ----------- - --- -------------------- ------- -------- -------- ---------------------- --------------------- ----------------------------------- ------------------------ ------------------------------- ------ ----- - ------------------- --- ----- - ------------------- ----------- ------------------------ ----- - ------------------------- ------------------------------------- ----------- ------------------------- - ----- ----------------- ------------------ ------- - - ------ ------ - -------- --- ------- - ----- --- ----- - ---------------------- ---------- ------------------- -
从示例代码中可以看出,在获取商品信息之前,先用布隆过滤器查询该商品信息是否存在,不存在则直接返回。这样就可以避免大量的请求进入缓存和数据库。
3. 加锁
如果没有缓存,就需要从数据库中获取数据,并且在获取之后,需要将数据写入缓存中。在写入缓存之前,需要加锁,防止多个线程同时写入缓存,从而避免了缓存穿透。加锁的时候需要注意一些细节问题,例如加锁时间不能过长,否则会影响到系统的性能。
下面是一个示例代码:
-- -------------------- ---- ------- ---------- -------- ---------------------- ------------------- --- ----- - ------------------- ----------- --------------------- --------------------------------- ---------------------------- ----- - ------------------- ----------- -------------------------- ----- - ------------------------- --------------------------------- ----------- ------------------ ----- ---- -------- - ----- ----------------- ------------------ ------- - - ---- ------------------------------- - ----- -------------------------- ----------- ----- - ---------------------- - - ------ ------ - -------- --- ------- - ----- --- ----- - ---------------------- -------- --- ------ ------------------- - ----- ------------------------------- -
从示例代码中可以看出,先尝试从 Redis 中获取缓存,如果不存在就加锁,防止其他线程同时写入缓存,然后重新获取一次缓存的商品信息,如果还是不存在该商品信息,则从数据库中获取。获取完之后,需要解锁,避免其他线程等待过长的时间。如果加锁失败,等待一段时间后再次获取,避免过多的请求数据库。
结论
Redis 缓存穿透问题是 Web 应用中的一个大问题,如果不处理,可能会导致性能问题,迫使你的服务器负载变高,甚至可以让你的服务器宕机。为了解决这个问题,需要使用多种方法,最好的解决方案是根据实际情况选择不同的方法,根据业务情况使用合适方案,有效处理缓存穿透的问题。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67446379c22b09372b15dc09