68、Redis:缓存雪崩、缓存穿透、缓存击穿
**缓存雪崩、缓存穿透、缓存击穿**
在分布式系统中,缓存是为了减少数据库的负载而设计的。然而,在某些情况下,缓存可能会出现问题,使得系统变得不稳定甚至崩溃。这就是我们要讨论的三个问题:缓存雪崩、缓存穿透和缓存击穿。
### 一、缓存雪崩**什么是缓存雪崩?**
缓存雪崩是指在缓存失效或被清除时,系统突然收到大量请求,导致数据库压力过大,从而引起系统崩溃。这种情况通常发生在缓存的有效期较短或者缓存的容量有限的情况下。
**如何避免缓存雪崩?**
1. **设置合理的缓存失效时间**:确保缓存的有效期足够长,以便系统有足够的时间来处理请求。
2. **使用分布式锁机制**:在多个节点上使用分布式锁机制,可以防止同一时间内只有一个节点能够访问数据库,从而避免雪崩。
3. **增加缓存容量**:如果系统需要处理大量请求,增加缓存的容量可以减少雪崩的可能性。
### 二、缓存穿透**什么是缓存穿透?**
缓存穿透是指在缓存中存在一个不存在的键值对,这个键值对被多次访问,从而导致系统持续地向数据库请求数据,造成压力。这种情况通常发生在恶意攻击或程序bug的情况下。
**如何避免缓存穿透?**
1. **设置合理的缓存失效时间**:确保缓存的有效期足够长,以便系统有足够的时间来处理请求。
2. **使用布隆过滤器**:布隆过滤器是一种数据结构,可以快速判断一个元素是否存在于集合中。如果元素不存在,直接返回false,不再向数据库请求,从而避免穿透。
3. **增加缓存容量**:如果系统需要处理大量请求,增加缓存的容量可以减少穿透的可能性。
### 三、缓存击穿**什么是缓存击穿?**
缓存击穿是指在缓存中存在一个热点数据(即被频繁访问的数据),当多个线程同时访问这个数据时,可能会导致缓存失效,从而向数据库请求数据,造成压力。这种情况通常发生在高并发系统中。
**如何避免缓存击穿?**
1. **设置合理的缓存失效时间**:确保缓存的有效期足够长,以便系统有足够的时间来处理请求。
2. **使用分布式锁机制**:在多个节点上使用分布式锁机制,可以防止同一时间内只有一个节点能够访问数据库,从而避免击穿。
3. **增加缓存容量**:如果系统需要处理大量请求,增加缓存的容量可以减少击穿的可能性。
### 总结在分布式系统中,缓存是为了减少数据库的负载而设计的。然而,在某些情况下,缓存可能会出现问题,使得系统变得不稳定甚至崩溃。这就是我们要讨论的三个问题:缓存雪崩、缓存穿透和缓存击穿。通过设置合理的缓存失效时间、使用分布式锁机制和增加缓存容量,可以避免这些问题。
###代码示例
java// 使用布隆过滤器public class BloomFilter { private int m; private int[] bits; public BloomFilter(int size) { this.m = size; this.bits = new int[m]; } public void add(String key) { for (int i =0; i < key.length(); i++) { int hash = hash(key, i); bits[hash] =1; } } public boolean contains(String key) { for (int i =0; i < key.length(); i++) { int hash = hash(key, i); if (bits[hash] ==0) return false; } return true; } private int hash(String key, int index) { // 使用FNV-1a哈希函数 int hash =2166136261; for (int i =0; i < key.length(); i++) { hash ^= key.charAt(i); hash *=16777219; } return hash % m; } public static void main(String[] args) { BloomFilter bloomFilter = new BloomFilter(1000000); bloomFilter.add("key1"); System.out.println(bloomFilter.contains("key1")); // true System.out.println(bloomFilter.contains("key2")); // false } }
java// 使用分布式锁机制public class DistributedLock { private final ReentrantLock lock; public DistributedLock() { this.lock = new ReentrantLock(); } public void acquire() { lock.lock(); } public void release() { lock.unlock(); } }
java// 使用缓存雪崩避免策略public class CacheSnowballAvoidStrategy { private final int cacheSize; private final int cacheTime; public CacheSnowballAvoidStrategy(int cacheSize, int cacheTime) { this.cacheSize = cacheSize; this.cacheTime = cacheTime; } public void add(String key, String value) { // 使用缓存雪崩避免策略 if (cache.get(key) != null && System.currentTimeMillis() - cache.get(key).getTime() < cacheTime) { return; } cache.put(key, new CacheItem(value, System.currentTimeMillis())); if (cache.size() > cacheSize) { // 使用缓存雪崩避免策略 cache.remove(cache.firstKey()); } } public String get(String key) { // 使用缓存雪崩避免策略 if (cache.get(key) != null && System.currentTimeMillis() - cache.get(key).getTime() < cacheTime) { return cache.get(key).getValue(); } return null; } }
java// 使用缓存穿透避免策略public class CachePenetrationAvoidStrategy { private final BloomFilter bloomFilter; public CachePenetrationAvoidStrategy(int size) { this.bloomFilter = new BloomFilter(size); } public void add(String key, String value) { // 使用缓存穿透避免策略 if (bloomFilter.contains(key)) { return; } bloomFilter.add(key); cache.put(key, value); } public String get(String key) { // 使用缓存穿透避免策略 if (!bloomFilter.contains(key)) { return null; } return cache.get(key); } }
java// 使用缓存击穿避免策略public class CacheCrashAvoidStrategy { private final DistributedLock lock; public CacheCrashAvoidStrategy() { this.lock = new DistributedLock(); } public void add(String key, String value) { // 使用缓存击穿避免策略 lock.acquire(); try { cache.put(key, value); } finally { lock.release(); } } public String get(String key) { // 使用缓存击穿避免策略 lock.acquire(); try { return cache.get(key); } finally { lock.release(); } } }