redis-缓存三剑客

缓存穿透

定义

缓存穿透是指用户请求的数据既不在缓存中,也不在数据库中。每次这样的请求都会穿透缓存,直接访问数据库,增加数据库的负载。

原因
  • 恶意攻击:攻击者不断请求不存在的数据。
  • 无效请求:用户请求的数据在数据库中不存在。
解决办法

1.布隆过滤器预检查

布隆过滤器是一种高效的概率型数据结构,可以快速判断某个元素是否存在。它可以在缓存之前进行预检查,过滤掉那些不可能存在的数据请求。

java 复制代码
public class BloomFilter {
    private BitSet bitSet;
    private int size;
    private int hashCount;

    public BloomFilter(int size, int hashCount) {
        this.size = size;
        this.hashCount = hashCount;
        this.bitSet = new BitSet(size);
    }

    private int hash(String item, int i) {
        int hash1 = item.hashCode();
        int hash2 = (hash1 >>> 16) ^ (hash1 << 1);
        return Math.abs((hash1 + i * hash2) % size);
    }

    public void add(String item) {
        for (int i = 0; i < hashCount; i++) {
            bitSet.set(hash(item, i));
        }
    }

    public boolean mightContain(String item) {
        for (int i = 0; i < hashCount; i++) {
            if (!bitSet.get(hash(item, i))) {
                return false;
            }
        }
        return true;
    }
}

2.缓存空结果

当查询数据库未命中时,可以将空结果缓存一段时间,避免短时间内重复查询数据库。

java 复制代码
public class CacheService {
    private static final Object NULL_OBJECT = new Object();
    private Map<String, Object> cache = new HashMap<>();

    public Object getData(String key) {
        Object data = cache.get(key);
        if (data == null) {
            data = queryDatabase(key);
            cache.put(key, data == null ? NULL_OBJECT : data);
        }
        return data == NULL_OBJECT ? null : data;
    }

    private Object queryDatabase(String key) {
        // 查询数据库逻辑
        return null;
    }
}

缓存击穿

定义

缓存击穿是指一个非常热门的key在缓存失效的时刻,同时有大量的并发请求到达,这些请求发现缓存失效后同时访问数据库,导致数据库压力骤增。

解决办法

1.互斥锁(Mutex)

在缓存失效时,通过互斥锁确保只有一个请求可以查询数据库并更新缓存,其他请求需要等待缓存更新完成。

java 复制代码
public class ProductService {
    private Map<String, Product> cache = new HashMap<>();
    private Object lock = new Object();

    public Product getProduct(String productId) {
        Product product = cache.get(productId);
        if (product == null) {
            synchronized (lock) {
                product = cache.get(productId);
                if (product == null) {
                    product = queryDatabase(productId);
                    cache.put(productId, product);
                }
            }
        }
        return product;
    }

    private Product queryDatabase(String productId) {
        // 查询数据库逻辑
        return new Product(productId, "Sample Product");
    }
}

2.设置热点数据永不过期

对热点数据不设置过期时间,保持缓存中的数据始终有效,避免缓存失效引发的并发访问数据库问题。

java 复制代码
public class NewsService {
    private Map<String, Integer> cache = new HashMap<>();

    public int getClickCount(String newsId) {
        Integer clickCount = cache.get(newsId);
        if (clickCount == null) {
            clickCount = queryDatabase(newsId);
            cache.put(newsId, clickCount);
        }
        return clickCount;
    }

    private int queryDatabase(String newsId) {
        // 查询数据库逻辑
        return 100; // 示例数据
    }
}

缓存雪崩

定义

缓存雪崩是指在某一时间段内,大量缓存同时失效,导致大量请求直接访问数据库,引发数据库压力骤增,甚至导致系统崩溃。

解决办法

1.缓存过期时间分散

设置缓存时,给不同的缓存键设置随机的过期时间,避免大量缓存同时失效。

java 复制代码
public void cacheData(String key, Object data) {
    int expiryTime = 3600 + new Random().nextInt(600); // 随机过期时间
    cache.put(key, data, expiryTime);
}

2.双层缓存

使用本地缓存和分布式缓存双层架构,本地缓存作为第一层,分布式缓存作为第二层,减少数据库直接访问的压力。

java 复制代码
public class DoubleLayerCache {
    private Map<String, Object> localCache = new HashMap<>();
    private Map<String, Object> distributedCache = new HashMap<>();

    public Object getData(String key) {
        Object data = localCache.get(key);
        if (data == null) {
            data = distributedCache.get(key);
            if (data == null) {
                data = queryDatabase(key);
                distributedCache.put(key, data);
            }
            localCache.put(key, data);
        }
        return data;
    }

    private Object queryDatabase(String key) {
        // 查询数据库逻辑
        return "Sample Data"; // 示例数据
    }
}

3.请求限流

当检测到数据库压力过大时,对请求进行限流,或者降级处理,返回默认值或错误信息,保护数据库。

java 复制代码
public Object getDataWithRateLimit(String key) {
    if (rateLimiter.allowRequest()) {
        return getData(key);
    } else {
        return "Service is busy, please try again later.";
    }
}

总结

缓存穿透、缓存击穿和缓存雪崩是缓存系统中常见的问题。通过使用布隆过滤器、互斥锁、双层缓存等技术,可以有效防止这些问题的发生,确保系统的稳定性和高可用性。选择合适的解决方案,可以显著提高系统的性能和用户体验。希望这些详细的解释和代码示例能帮助你更好地理解和解决这些缓存问题。

相关推荐
TYW082312 分钟前
系统运维——mysql 企业级配置
运维·数据库·mysql
我还能再卷一点15 分钟前
mysql:2059 -Authentication plugin ‘caching-sha2-password‘解决办法
数据库·mysql
一左coding17 分钟前
openGuass——管理用户安全
数据库
天桥下的卖艺者41 分钟前
我写的全部R包和函数,持续更新中
数据库·python·r语言
天天(学习版)1 小时前
我的sql我做主!Mysql 的集群架构详解之组从复制、半同步模式、MGR、Mysql路由和MHA管理集群组
运维·服务器·数据库·mysql·架构·集群·高可用
问道飞鱼1 小时前
Mysql三个日志的作用及区别
数据库·mysql
无名之逆1 小时前
后端微服务与分布式系统
运维·网络·数据库·redis·微服务·postgresql·apache
鱟鲥鳚1 小时前
MySql【SQL顺序】
数据库·sql·mysql
落雨便归尘1 小时前
redis实战——go-redis的使用与redis基础数据类型的使用场景(二)
数据库·redis·golang
ahauedu2 小时前
SpringBoot依赖之Spring Data Redis的功能抽离公共服务
spring boot·redis·spring