• 缓存的设计


    1. 缓存的更新机制

    1.1 被动更新

    为缓存设定过期时间,失效从数据库读取,再次写入缓存

    调用方 暂存方(缓存) 数据提供方

    被动:有效期到后,再次写入。

    1. 客户端 查数据,缓存中没有,从提供方获取,写入缓存(有一个过期时间t)。

    2. 在t内,所有的查询,都由缓存提供。所有的写,直接写数据库。

    3. 当 缓存数据 t 到点了,缓存 数据 变没有。后面的查询,回到了第1步。

    适合:对数据准确性和实时性要求不高的场景。比如:商品 关注的人数。

    1.2 主动更新

    1.2.1 Cache Aside Pattern (更新数据库,再删除缓存)

    这是最常用的更新机制

    • 失效: 应用程序从cache中获取数据,没获取到,则从数据库中读取数据,成功后,放到缓存中
    • 命中: 应用程序从缓存中获取数据,得到后直接返回
    • 更新: 先把数据库中的数据更新,成功后,在将缓存删除

    在这里插入图片描述

    有可能产生的问题

    比方一个读操作,一个写操作的并发,读操作没有了删除缓存的操作,直接命中拿的是缓存中的数据,写操作更新了数据库中的数据,并删除了缓存,读操作读的就是老的数据

    但是这种情况只是理论上存在,实际上很少出现,因为这种情况的产生是需要读操作慢于写操作,一般情况下,写操作都是比读操作慢,并且要加事务锁表,所以很少出现这种情况

    1.2.2 更新数据库,更新缓存

    一般也不采用。
    请求被阻塞,
    业务要求:修改了数据库,缓存的值需要通过大量时间的计算才能进行更新,影响了响应时间,直接删了缓存,比较节省计算时间,当用户再次去查询的时候,发现缓存的值不存在,用户只要经过一次复杂的计算就能对缓存的值进行更新

    1.2.3 先删除缓存,在更新数据库

    一般不采用,因为大概率 读比写快。

    一个读请求和一个写请求的并发,当写请求进来,把缓存删了,更新数据库这步操作还没完成,读请求进来,发现没缓存,往数据库中读取,这个时候数据库的数据还是老的数据,读请求把老的数据写入了缓存,然后写请求才把数据更新到数据库,这就造成了数据库和缓存的双写一致性问题
    在这里插入图片描述

    解决双写一致性问题:延迟双删

    延迟双删

    就是在更新晚数据库之后,sleep一段时间,再次进行删除缓存的操作,能极大的保证双写一致性

    在这里插入图片描述

    昨天被高德一个面试问:说,你这个延时双删有这么几步操作。如果其中某一步失败了这么办?
    删除缓存
    更新数据库:事务,回滚就OK。
    第二次删除缓存
    重试删除:当你前面的操作,无法回滚时,为了保证后续数据的一致性,
    (最便宜的做法)硬着头皮往前走,重试。
    借用中间件:消息队列,重发消息。
    系统外订阅:canal。binlog。
    二次删除key,和我们的业务代码解耦。

    1.3 Read/Write Through Pattern

    这个是直接更缓存打交道,所有的增删改查都在缓存上进行,不会出现数据一致性的问题,但是缓存挂了的话,数据容易丢失

    在这里插入图片描述

    1.4 Write Behind Caching Pattern

    更新数据的操作直接在缓存中进行,然后再异步对数据库进行更新,带来的好处就是数据的IO操作非常的快。带来的问题就是可能产生数据的丢失

    2. 缓存的清理机制

    2.1 时效性清理

    就是给缓存设置一个过期时间,到期自动清理

    2.2 数目阀值清理机制

    判断缓存中的缓存的数量 达到一定值 ,对缓存进行清理。
    阈值:根据自己的业务来定。1g,1m,1024个, 800 80%。

    2.3 软应用清理

    当空间不足的时候,会被回收

    2.4 redis 的8中内存淘汰策略

    a) 针对设置了过期时间的key做处理:

    1. volatile-ttl:在筛选时,会针对设置了过期时间的键值对,根据过期时间的先后进行删除,越早过期的越先被删除。

    2. volatile-random:就像它的名称一样,在设置了过期时间的键值对中,进行随机删除。

    3. volatile-lru:会使用 LRU 算法筛选设置了过期时间的键值对删除。

    4. volatile-lfu:会使用 LFU 算法筛选设置了过期时间的键值对删除
      b) 针对所有的key做处理 :

    5. allkeys-random:从所有键值对中随机选择并删除数据。

    6. allkeys-lru:使用 LRU 算法在所有数据中进行筛选删除。

    7. allkeys-lfu:使用 LFU 算法在所有数据中进行筛选删除。
      c) 不处理:

    8. noeviction:不会剔除任何数据,拒绝所有写入操作并返回客户端错误信息"(error) OOM command not allowed when used memory",此时Redis只响应读操作。

    LRU 算法(Least Recently Used,最近最少使用)
    淘汰很久没被访问过的数据,以最近一次访问时间作为参考。(淘汰这段时间中最旧的key)

    LFU 算法(Least Frequently Used,最不经常使用)
    淘汰最近一段时间被访问次数最少的数据,以次数作为参考。(淘汰这段时间使用次数最少的key)

    2.5 缓存清理机制的总结

    1. 时效性清理+数目阀值: 防止:短期内,密集查询,导致缓存空间的急剧增大
    2. lru+软引用:保证热数据,最大限度的提高缓存命中率

    3. 缓存问题

    3.1 缓存穿透

    缓存中没有值,数据库中也没有这个值

    产生可能原因

    1、自身业务代码或者数据出现问题;
    2、一些恶意攻击、 爬虫等造成大量空命中。

    问题解决

    1. 缓存空对象
    注意:对于不存在的空对象,一定要设置过期时间!

    String get(String key) {
        // 从缓存中获取数据
        String cacheValue = cache.get(key);
        // 缓存为空
        if (StringUtils.isBlank(cacheValue)) {
            // 从存储中获取
            String storageValue = storage.get(key);
            cache.set(key, storageValue);
            // 如果存储数据为空, 需要设置一个过期时间(300秒)
            if (storageValue == null) {
                cache.expire(key, 60 * 5);
            }
            return storageValue;
        } else {
            // 缓存非空
            return cacheValue;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    2. 布隆过滤器

    布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。

  • 相关阅读:
    安装speechmetrics报错:可能是pip的问题,建议直接下载zip
    Redis - 0、几款可视化工具
    【编译原理】实验一 词法分析器(Java实现)
    《web应用技术》第十次作业
    新移动时代内容制作呈现与开发利器-Zoomla!逐浪CMS v8.6.2发布
    论文解读(GCA)《Graph Contrastive Learning with Adaptive Augmentation》
    Unity 从Inspector界面打开资源管理器选择并记录文件路径
    list【2】模拟实现(含迭代器实现超详解哦)
    WEE指令和ROHS指令的区别
    Generative AI 新世界 | Falcon 40B 开源大模型的部署方式分析
  • 原文地址:https://blog.csdn.net/yaoxie1534/article/details/128116099