缓存穿透、缓存并发、热点缓存之最佳招式

本贴最后更新于 3023 天前,其中的信息可能已经沧海桑田

缓存穿透与并发方案

相信不少朋友之前看过很多类似的文章,但是归根结底就是二个问题:

  • 如何解决穿透
  • 如何解决并发

当并发较高的时候,其实我是不建议使用缓存过期这个策略的,我更希望缓存一直存在,通过后台系统来更新缓存系统中的数据达到数据的一致性目的,有的朋友可能会质疑,如果缓存系统挂了怎么办,这样数据库更新了但是缓存没有更新,没有达到一致性的状态。

解决问题的思路是
如果缓存是因为网络问题没有更新成功数据,那么建议重试几次,如果依然没有更新成功则认为缓存系统出错不可用,这时候客户端会将数据的 KEY 插入到消息系统中,消息系统可以过滤相同的 KEY,只需保证消息系统不存在相同的 KEY,当缓存系统恢复可用的时候,依次从 mq 中取出 KEY 值然后从数据库中读取最新的数据更新缓存。
注意:更新缓存之前,缓存中依然有旧数据,所以不会造成缓存穿透。

下图展示了整个思路的过程:

看完上面的方案以后,又会有不少朋友提出疑问,如果我是第一次使用缓存或者缓存中暂时没有我需要的数据,那又该如何处理呢?

解决问题的思路
在这种场景下,客户端从缓存中根据 KEY 读取数据,如果读到了数据则流程结束,如果没有读到数据(可能会有多个并发都没有读到数据),这时候使用缓存系统中的 setNX 方法设置一个值(这种方法类似加个锁),没有设置成功的请求则 sleep 一段时间,设置成功的请求读取数据库获取值,如果获取到则更新缓存,流程结束,之前 sleep 的请求这时候唤醒后直接再从缓存中读取数据,此时流程结束。

在看完这个流程后,我想这里面会有一个漏洞,如果数据库中没有我们需要的数据该怎么处理,如果不处理则请求会造成死循环,不断的在缓存和数据库中查询,这时候我们会沿用我之前文章中的如果没有读到数据则往缓存中插入一个 NULL 字符串的思路,这样其他请求直接就可以根据“NULL”进行处理,直到后台系统在数据库成功插入数据后同步更新清理 NULL 数据和更新缓存。

流程图如下所示:

总结:
在实际工作中,我们往往将上面二个方案组合使用才能达到最佳效果,虽然第二种方案也会造成请求阻塞,但是只是在第一次使用或者缓存暂时没有数据的情况下才会产生,在生产中经过检验在 TPS 没有上万的情况下是不会造成问题的。

热点缓存解决方案

1、缓存使用背景:

我们拿用户中心的一个案例来说明:
每个用户都会首先获取自己的用户信息,然后再进行其他相关的操作,有可能会有如下一些场景情况:

  • 会有大量相同用户重复访问该项目。
  • 会有同一用户频繁访问同一模块。
2、思路解析
  • 因为用户本身是不固定的而且用户数量也有几百万尤其上千万,我们不可能把所有的用户信息全部缓存起来,通过第一个场景情况可以看到一些规律,那就是有大量的相同用户重复访问,但是究竟是哪些用户重复访问我们也并不知道。

  • 如果有一个用户频繁刷新读取项目,那么对数据库本身也会造成较大压力,当然我们也会有相关的保护机制来确实恶意攻击,可以从前端控制,也可以有采黑名单等机制,这里不在赘述。如果用缓存的话,我们又该如何控制同一用户繁重读取用户信息呢。

请看下图:

我们会通过缓存系统做一个排序队列,比如 1000 个用户,系统会根据用户的访问时间更新用户信息的时间,越是最近访问的用户排名越排前,系统会定期过滤掉排名最后的 200 个用户,然后再从数据库中随机取出 200 个用户加入队列,这样请求每次到达的时候,会先从队列中获取用户信息,如果命中则根据 userId,再从另一个缓存数据结构中读取用户信息,如果没有命中则说明该用户请求频率不高。

Java 伪代码如下所示:

for (int i = 0; i < times; i++) { user = new ExternalUser(); user.setId(i+""); user.setUpdateTime(new Date(System.currentTimeMillis())); CacheUtil.zadd(sortKey, user.getUpdateTime().getTime(), user.getId()); CacheUtil.putAndThrowError(userKey+user.getId(), JSON.toJSONString(user)); } Set userSet = CacheUtil.zrange(sortKey, 0, -1); System.out.println("[sortedSet] - " + JSON.toJSONString(userSet) ); if(userSet == null || userSet.size() == 0) return; Set userSetS = CacheUtil.zrangeWithScores(sortKey, 0, -1); StringBuffer sb = new StringBuffer(); for(Tuple t:userSetS){ sb.append("{member: ").append(t.getElement()).append(", score: ").append(t.getScore()).append("}, "); } System.out.println("[sortedcollect] - " + sb.toString().substring(0, sb.length() - 2)); Set members = new HashSet(); for(String uid:userSet){ String key = userKey + uid; members.add(uid); ExternalUser user2 = CacheUtil.getObject(key, ExternalUser.class); System.out.println("[user] - " + JSON.toJSONString(user2) ); } System.out.println("[user] - " + System.currentTimeMillis()); String[] keys = new String[members.size()]; members.toArray(keys); Long rem = CacheUtil.zrem(sortKey, keys); System.out.println("[rem] - " + rem); userSet = CacheUtil.zrange(sortKey, 0, -1); System.out.println("[remove - sortedSet] - " + JSON.toJSONString(userSet));
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 248 回帖 • 1 关注
  • 缓存
    42 引用 • 70 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    86 引用 • 165 回帖 • 2 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    167 引用 • 408 回帖 • 483 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖
  • Maven

    Maven 是基于项目对象模型(POM)、通过一小段描述信息来管理项目的构建、报告和文档的软件项目管理工具。

    188 引用 • 319 回帖 • 237 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 7 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    948 引用 • 1460 回帖 • 1 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3203 引用 • 8217 回帖 • 2 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1340 回帖 • 1 关注
  • 工具

    子曰:“工欲善其事,必先利其器。”

    301 引用 • 768 回帖
  • Angular

    AngularAngularJS 的新版本。

    26 引用 • 66 回帖 • 562 关注
  • 大数据

    大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。

    89 引用 • 113 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    100 引用 • 905 回帖 • 1 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 560 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 118 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 2 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    10 引用 • 15 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    554 引用 • 675 回帖 • 1 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 2 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    21 引用 • 31 回帖 • 6 关注
  • App

    App(应用程序,Application 的缩写)一般指手机软件。

    91 引用 • 384 回帖
  • 分享

    有什么新发现就分享给大家吧!

    248 引用 • 1794 回帖 • 1 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    26470 引用 • 110095 回帖 • 1 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    291 引用 • 4495 回帖 • 665 关注