1 缓存穿透
缓存穿透是指查询一个一定不存在的数据,由于缓存中没有,每次查询都要去数据库中查询,导致频繁地访问数据库,从而影响系统的性能。攻击者可以利用这一点,对系统进行拒绝服务攻击。
1.1 缓存穿透举例
-
攻击者通过在请求中携带不存在的数据,进行大量的请求,导致系统访问数据库频繁,从而影响系统性能。
-
用户在查询不存在的数据时,由于缓存中没有,每次都会去数据库中查询,也会造成缓存穿透。
1.2 解决方法
-
使用布隆过滤器对查询的键进行过滤,如果查询的键不存在于布隆过滤器中,则直接返回不存在,不再查询缓存和数据库。
布隆过滤器是一种空间效率很高的随机数据结构,它利用位数组和哈希函数实现,可以用于检索一个元素是否在一个集合中。布隆过滤器有一定的误判率,但是可以通过调整位数组的大小和哈希函数的数量来控制误判率。 -
如果查询的数据在数据库中不存在,则将空对象放入缓存中,下次再查询时直接从缓存中获取空对象,避免了频繁地去查询数据库。
例如,当用户查询某个不存在的商品时,将一个空的商品对象放入缓存中,下次再查询时直接从缓存中获取该空对象。这样即使用户频繁地查询不存在的商品,也不会对数据库造成压力。 -
在应用启动时,或者定期地将热点数据加载到缓存中,避免缓存中没有数据导致的缓存穿透。
热点数据是指访问频率高、对系统性能影响大的数据。将热点数据预先加载到缓存中可以避免缓存穿透。可以在应用启动时,或者定期地将热点数据加载到缓存中。可以使用缓存预热技术实现。 -
对请求增加校验机制,过滤掉不合理请求
例如:课程Id是长整型,如果发来的不是长整型则直接返回。
1.3 名词解释
1.3.1 布隆过滤器
布隆过滤器是一种数据结构,可以高效地判断一个元素是否存在于一个集合中。它的主要优点是占用内存空间小、判断速度快,适用于需要快速判断元素是否存在于集合中的场景。布隆过滤器基于哈希函数实现,将元素映射到一个位数组中,并使用多个哈希函数进行映射,从而降低误判率。但是,由于布隆过滤器的空间有限,当元素数量过多时,误判率会增加,从而导致布隆过滤器失效。
1.3.1.1 布隆过滤器的优点
-
占用内存小:布隆过滤器只需要一段位数组和多个哈希函数,占用内存空间很小。
-
判断速度快:布隆过滤器只需要进行位运算,判断速度非常快。
-
可以判断元素是否一定不存在于集合中:如果布隆过滤器判断某个元素不存在于集合中,那么该元素一定不存在于集合中。
1.3.1.2 布隆过滤器的缺点
-
误判率高:由于布隆过滤器使用多个哈希函数进行映射,误判率会随着哈希函数数量的增加而降低,但仍然存在误判率。
-
无法删除元素:由于布隆过滤器的位数组是基于哈希函数映射得到的,无法删除某个元素。
-
受限于容量:当元素数量过多时,误判率会增加,从而导致布隆过滤器失效。
1.3.1.3 布隆过滤器容易失效的场景
-
元素数量过多:当布隆过滤器中的元素数量过多时,误判率会增加,从而导致布隆过滤器失效。
-
哈希函数数量不足:布隆过滤器的误判率与哈希函数数量有关,当哈希函数数量不足时,误判率会增加,从而导致布隆过滤器失效。
-
元素分布不均匀:当元素在哈希函数映射后分布不均匀时,误判率会增加,从而导致布隆过滤器失效。
总的来说,布隆过滤器适用于需要快速判断元素是否存在于集合中的场景,但需要注意它的误判率和容量限制,以及容易失效的场景。在使用布隆过滤器时,需要根据具体场景进行调整,以达到合理的使用效果。
2 缓存雪崩
缓存雪崩是指缓存中大量的数据同时失效,导致大量的请求直接打到数据库上,从而使得数据库承受不了巨大的压力,最终导致整个系统崩溃的现象。缓存雪崩通常是由于缓存中的键同时过期或者缓存服务宕机等原因引起的。
2.1 缓存雪崩举例
-
在某个时间点,所有的缓存数据都过期了。由于缓存中不存在有效的数据,所有的请求都会直接打到数据库上,导致数据库承受巨大的压力,最终导致整个系统崩溃。
-
某个缓存服务宕机了,所有的请求都无法得到缓存服务的响应,直接打到数据库上,导致数据库承受巨大的压力,最终导致整个系统崩溃。
2.2 解决方法
- 缓存数据的过期时间设置随机,避免缓存数据同时失效。可以将缓存数据的过期时间加上一个随机值,避免缓存数据同时失效。比如,将缓存数据的过期时间加上一个随机值,可以使得缓存数据的失效时间分散在时间轴上,避免缓存数据同时失效。
redisTemplate.opsForValue().set("key", JSON.toJSONString(valueObject),30 + new Random().nextInt(100),
TimeUnit.SECONDS);
-
使用分布式缓存,避免单点故障。使用分布式缓存可以避免单点故障,提高缓存服务的可用性。可以使用Redis Cluster、Memcached等分布式缓存来替代单机缓存。
-
数据预热,避免缓存冷启动。在系统启动或者低峰期时,将一些热点数据预先加载到缓存中,避免缓存冷启动。可以使用缓存预热技术实现。
-
多级缓存,避免缓存失效时的“一次性打击”。多级缓存可以避免缓存失效时的“一次性打击”,提高缓存服务的稳定性。可以使用本地缓存和分布式缓存相结合的方式来实现多级缓存。
-
数据库压力保护。可以通过限流、降级等手段来保护数据库,避免数据库承受巨大的压力。可以使用限流、降级等技术来控制数据库的访问量。
3 缓存击穿
缓存击穿是指缓存中不存在的数据被大量请求,导致请求直接打到数据库上,从而使得数据库承受巨大的压力,最终导致整个系统崩溃的现象。缓存击穿通常是由于热点数据集中、缓存过期时间设置不合理等原因引起的。
3.1 缓存击穿举例
-
某个商品的详情信息是热点数据,每秒有很多请求访问该商品详情信息。如果缓存中不存在该商品的详情信息,那么所有请求都会直接打到数据库上,导致数据库承受巨大的压力,最终导致整个系统崩溃。
-
某个恶意用户故意请求一个不存在于缓存中的key,每秒有很多请求访问该key。如果缓存中不存在该key,那么所有请求都会直接打到数据库上,导致数据库承受巨大的压力,最终导致整个系统崩溃。
3.2 解决方法
-
使用互斥锁或者分布式锁,避免缓存击穿。在缓存失效时,使用互斥锁或者分布式锁,避免缓存击穿。在使用锁的时候,需要注意锁的粒度,尽量避免锁的粒度过大,影响系统的并发性能。
-
设置热点数据永不过期。对于热点数据,可以设置其永不过期,避免缓存失效。当然,这种方式需要注意缓存数据的大小,不要将所有数据都设置成永不过期,影响系统的内存使用。
-
在缓存失效时,设置短暂的锁定时间。在缓存失效时,可以设置短暂的锁定时间,避免缓存击穿。在锁定时间内,所有请求都会等待锁被释放后再访问数据库,避免了缓存击穿。
-
使用异步加载,避免缓存击穿。可以使用异步加载技术,将缓存数据的加载异步化,降低缓存击穿的风险。在缓存失效时,可以异步地加载数据到缓存中,避免大量请求直接打到数据库上。
-
增加缓存层,避免缓存击穿。可以增加缓存层,将热点数据缓存到更高层次的缓存中,避免缓存击穿。比如,可以将热点数据缓存到CDN中,避免请求直接打到数据库上。
3.3 名词解释
3.3.1 异步加载
异步加载是指在应用程序中,将数据加载操作放在后台线程中执行的一种技术。它的作用是提高系统的响应速度,并避免缓存失效时大量请求直接打到数据库上,导致数据库负载过高的情况,从而保证系统的稳定性。
在使用异步加载时,应用程序会在缓存失效时向后台线程发送请求,请求后台线程异步地从数据库中加载数据,并将数据存储到缓存中。后台线程会根据任务队列中的请求,异步地从数据库中加载数据,并在加载完成后将加载结果返回给主线程,主线程可以使用加载好的数据进行相应的操作。通过这种方式,应用程序可以避免主线程的阻塞,提高系统的响应速度。同时,异步加载还可以避免缓存击穿的风险,使系统更加稳定可靠。
3.3.1.1 异步加载举例
举个例子,假设我们有一个电商应用,需要加载商品列表数据。为了提高系统的响应速度,我们可以使用异步加载技术,将商品列表数据的加载异步化。
具体操作步骤如下:
-
当用户进入商品列表页面时,应用程序会向后台线程发送请求,请求后台线程加载商品列表数据。
-
后台线程会将请求放入任务队列中,等待执行。
-
后台线程会根据任务队列中的请求,异步地从数据库中加载商品列表数据,并将数据存储到缓存中。
-
当商品列表数据加载完成后,后台线程会将加载结果返回给主线程,主线程可以使用加载好的商品列表数据进行相应的操作,例如展示在页面上。
通过异步加载技术,我们可以避免用户在等待商品列表数据加载时出现卡顿的情况,提高了系统的响应速度。同时,如果缓存失效时,异步加载还可以避免大量请求直接打到数据库上,导致数据库负载过高的情况,从而保证了系统的稳定性。
异步加载是在缓存命中失败时才会触发的。 当缓存命中成功时,应用程序可以直接返回缓存中的数据,避免了不必要的数据库查询操作,从而提高了系统的性能。只有当缓存命中失败时,应用程序才会向后台线程发送请求,请求后台线程异步地从数据库中加载数据,并将数据存储到缓存中,这样可以避免缓存击穿的风险。因此,在实际应用中,异步加载通常是在缓存命中失败时才会使用的。
3.3.2 增加缓存层
增加缓存层是一种常见的避免缓存击穿的方法,可以将热点数据缓存到更高层次的缓存中,避免请求直接打到数据库上,从而提高系统的性能和稳定性。
下面是具体的解释:
-
缓存层级划分
将缓存划分为多个层级,按照访问频率和数据更新频率的高低,将热点数据放在更高层次的缓存中。例如,可以将热点数据缓存到CDN中,将相对冷门的数据缓存到本地缓存或者分布式缓存中,将最不常用的数据缓存到数据库中。 -
CDN缓存
使用CDN缓存来缓存热点数据,可以将数据缓存在离用户更近的地方,避免请求直接打到数据库上。CDN缓存可以根据用户的地理位置,选择离用户更近的节点进行缓存,从而提高访问速度和稳定性。 -
缓存更新
对于经常被访问和更新的数据,需要使用缓存更新策略,避免缓存失效后直接打到数据库上,导致缓存击穿。常见的缓存更新策略有定时更新和异步更新。定时更新是指在缓存过期前,定期将缓存中的数据更新为最新状态;异步更新是指在缓存失效后,异步地从数据库中加载数据,并将数据更新到缓存中。 -
缓存预热
缓存预热是指在系统启动时,将热点数据提前加载到缓存中,避免缓存失效后直接打到数据库上,导致缓存击穿。缓存预热可以通过定时任务或者手动触发来实现。
综上所述,增加缓存层是一种有效的避免缓存击穿的方法,可以提高系统的性能和稳定性。但需要注意的是,缓存层级划分和缓存更新策略需要根据实际情况进行调整,避免缓存命中率过低或者缓存更新过于频繁,影响系统的性能。