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.";
    }
}

总结

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

相关推荐
科技小花1 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸1 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain1 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希2 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神2 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员2 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java2 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴2 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存
YOU OU2 小时前
三大范式和E-R图
数据库