聊聊缓存穿透

在我们的项目中多少都会使用缓存,因为有些数据我们没有必要每次都去查询数据库,特别是高QPS的系统,每次都去查询数据库会影响数据库性能。

业务系统一般调用流程:

我们一般的做法是先从缓存中查询,如果缓存中查询到了则直接返回,如果缓存中没有,则查询数据库,如果从数据库中查询到了,则先将数据写入缓存,然后返回数据给调用方。

public Result<Post> queryPostFromCache(Long id) {
    // POST:id
    final String key = Constant.Cache.POST + Constant.COLON + id;

    // 从缓存中查询
    Object post = cacheService.get(key);
    if(post != null) {
        return Result.success((Post) post);
    }

    // 从数据库中查询
    post = postMapper.queryPostById(id);
    if(post != null) {
        cacheService.set(key, post, Duration.ofHours(1));
    }
    return Result.success((Post) post);
}

正常情况下,我们查询的数据都是存在的,比如从文章列表页面跳转到文章详情页查询文章详细信息,此时根据文章ID查询,文章的ID是真实存在的。不正常的情况是如果请求查询的文章ID是一条根本不存在的数据,也就是说缓存和数据库中都不会有值,这会导致请求每次都会到数据库中查询。这种查询不存在数据的现象我们称之为缓存穿透

缓存穿透是指用户查询的数据在数据库中没有,那么在缓存中也不会有,也就是说查询的是一条根本不存在的记录。这样就会导致用户首先在缓存中找不到 ,则每次都要去数据库再查询一遍,然后返回空。这样就相当于进行了两次无用的查询。要是有人利用这种不存在的key频繁攻击我们的系统,很可能导致数据库压力增大,甚至导致数据库挂掉。

解决方案

1.将未在数据库中查询到值的key也写入缓存,缓存的值为空对象,同时设置一个较短的过期时间,以防止后面真的有数据。

2.采用布隆过滤器(Bloom Filter),使用一个足够大的bitmap,用于存储数据库中可能访问的key。布隆过滤器可以理解为一个不太精确的set集合,当你使用它的contains方法判断某个对象是否存在时,它可能会误判。它的特点是当布隆过滤器判断某个值存在时,这个值可能不存在(误判),当它判断某个值不存在时,那就肯定不存在。这个特点可以用在判断要查询的key是否存在,如果判断不存在时,那就肯定不存在,这个时候就不用再查询数据库了,直接返回给调用方值不存在。

使用空对象解决缓存穿透问题的示例代码


public Result<Post> queryPostFromCache(Long id) {
    // POST:id
    final String key = Constant.Cache.POST + Constant.COLON + id;

    // 从缓存中查询
    Object post = cacheService.get(key);
    if(post != null) {
        // 判断缓存中的值是否为默认对象
        if(post instanceof NullObjectValue) {
            return Result.fail(ResultCode.RESOURCES_NOT_FOUND);
        }
        return Result.success((Post) post);
    }

    // 从数据库中查询
    post = postMapper.queryPostById(id);
    if(post != null) {
        cacheService.set(key, post, Duration.ofHours(1));
    }else {
        // 数据库中没有对应记录,给这个key缓存设置一个默认值
        cacheService.set(key, new NullObjectValue(), Duration.ofHours(1));
    }

    return Result.success((Post) post);
}

有关使用布隆过滤器来解决缓存穿透问题的具体实现,我将会在下篇文章中介绍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章