高并发系统设计之缓存
本文已收录至Github,推荐阅读 👉 Java随想录
这篇文章来讲讲缓存。缓存是优化网站性能的第一手段,目的是让访问速度更快。
说起缓存,第一反应可能想到的就是Redis。目前比较好的方案是使用多级缓存,如CPU→Ll/L2/L3→内存→磁盘就是一个典型的例子,CPU需要数据时先从L1读取,如果没有找到,则查找L2/L3读取,如果没有,则到内存中查找,如果还没有,会到磁盘中查找。
文章目录
- 堆缓存
- Nginx代理缓存
- 多级缓存
- 热点Key自动探测
堆缓存
使用Java堆内存来存储缓存对象。使用堆缓存的好处是没有序列化/反序列化,是最快的缓存。缺点也很明显,当缓存的数据量很大时,GC(垃圾回收)暂停时间会变长,存储容量受限于堆空间大小。一般通过软引用/弱引用来存储缓存对象,即当堆内存不足时,可以强制回收这部分内存释放堆内存空间。一般使用堆缓存存储较热的数据。可以使用Caffeine Cache实现。
集成Caffeine Cache详情见我另外一片文章:本地缓存无冕之王Caffeine Cache
Nginx代理缓存
Nginx 的Proxy Cache可以实现代理缓存,特别需要说明的是,Proxy Cache机制依赖于Proxy Buffer机制,只有在Proxy Buffer机制开启的情况下Proxy Cache的配置才发挥作用,Proxy Buffer默认是开启的,缓存内容将存放在tmpfs(内存文件系统)以提升性能。
Proxy Buffer相关参数:
proxy_buffering on;
该参数设置是否开启proxy的buffer功能,参数的值为on或者off,默认是on。proxy_buffer_size 4k;
该参数用来设置一个特殊的buffer大小的。
从被代理服务器上获取到的第一部分响应数据内容到代理服务器上,通常是header,就存到了这个buffer中。
如果该参数设置太小,会出现502错误码,这是因为这部分buffer不够存储header信息。建议设置为4k。proxy_buffers 8 4k;
这个参数设置存储被代理服务器上的数据所占用的buffer的个数和每个buffer的大小。
所有buffer的大小为这两个数字的乘积。proxy_busy_buffer_size 16k;
在所有的buffer里,我们需要规定一部分buffer把自己存的数据传给服务器,这部分buffer就叫做busy_buffer。
proxy_busy_buffer_size参数用来设置处于busy状态的buffer有多大。proxy_temp_path
语法:proxy_temp_path path [level1 level2 level3]
定义proxy的临时文件存在目录以及目录的层级。
proxy_buffering 与 proxy_cache的区别:
缓冲(buffer)
- Nginx 将上游服务器的响应报文保存几秒钟,等整个接收之后,再发送给客户端。
- 可以尽早与上游服务器断开连接,减少其负载。但是会增加客户端等待响应的时间。
- 如果不启用缓冲,则 Nginx 收到上游服务器的一部分响应就会立即发送给客户端,通信延迟低。
缓存(cache)
- Nginx 将上游服务器的响应报文保存几分钟,当客户端再次请求同一个响应报文时就直接回复,不必请求上游服务器。
- 可以避免重复向上游服务器请求一些固定不变的响应报文,减少上游服务器的负载,减少客户端等待响应的时间。
Proxy Cache 相关的参数配置:
- proxy_cache_path:Nginx 使用该参数指定缓存位置,有两个必填参数, 第一个参数为缓存目录。 第二个参数keys_zone指定缓存名称和占用内存空间的大小。
- proxy_cache:该参数为之前指定的缓存名称。
- proxy_cache_key:该指令用来设置web缓存的Key值。
- proxy_cache_min_uses:指定请求至少被发送了多少次以上时才缓存,可以防止低频请求被缓存。
- proxy_cache_methods:指定哪些方法的请求被缓存。
- proxy_cache_valid:该指令用于对于不同返回状态码的URL设置不同的缓存时间。
- proxy_cache_bypass:指定Nginx使用缓存的条件。
如下示例:
http {proxy_cache_path /data/nginx/cache keys_zone=one:10m max_size=10g;proxy_cache_methods GET HEAD POST PUT;proxy_cache_min_uses 1;proxy_cache_valid 200 302 10m;proxy_cache_key "$host:$server_port$uri$is_args$args";upstream www.baidu.com {server 127.0.0.1:8880;server 127.0.0.1:8881;server 127.0.0.1:8882;}server {listen 80 ;proxy_cache one ;server_name www.baidu.com;location / {proxy_pass http://www.baidu.com;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment ;}}}
多级缓存
Nginx缓存→分布式Redis缓存(可以使用Lua脚本直接在Nginx里读取Redis)→堆内存
-
接入 Nginx将请求负载均衡到应用Nginx,此处常用的负载均衡算法是轮询或者一致性哈希。轮询可以使服务器的请求更加均衡,而一致性哈希可以提升应用Nginx的缓存命中率。
-
应用Nginx读取本地缓存(本地缓存可以使用Lua Shared Dict,Nginx Proxy Cache(磁盘/内存)、Local Redis实现)。如果本地缓存命中,则直接返回,使用应用Nginx本地缓存可以提升整体的吞吐量,降低后端压力,尤其应对热点问题非常有效。
-
如果Nginx本地缓存没命中,则会读取相应的分布式缓存(如 Redis缓存,还可以考虑使用主从架构来提升性能和吞吐量),如果分布式缓存命中,则直接返回相应数据(并回写到Nginx本地缓存)。
-
如果分布式缓存也没有命中,则会回源到Tomcat集群,在回源到Tomcat 集群时,也可以使用轮询和一致性哈希作为负载均衡算法。
-
在 Tomcat应用中,首先读取本地堆缓存。如果有,则直接返回(并会写到主Redis集群)。
-
作为可选部分,如果步骤4没有命中,则可以再尝试一次读主Redis集群操作,目的是防止当从集群有问题时的流量冲击。
-
如果所有缓存都没有命中,则只能查询DB或相关服务获取相关数据并返回。
-
步骤7返回的数据异步写到主Redis集群。
整体分了三部分缓存:应用Nginx本地缓存、分布式缓存、Tomcat堆缓存。每一层缓存都用来解决相关问题,如应用Nginx本地缓存用来解决热点缓存问题,分布式缓存用来减少访问回源率,Tomcat堆缓存用于防止相关缓存失效/崩溃之后的冲击。
热点Key自动探测
热点数据其实可以分为:静态热点数据 和 动态热点数据。
所谓“静态热点数据”,就是能够提前预测的热点数据。例如,我们可以通过卖家报名的方式提前筛选出来,通过报名系统对这些热点商品进行打标。另外,我们还可以通过大数据分析来提前发现热点商品,比如我们分析历史成交记录、用户的购物车记录,来发现哪些商品可能更热门、更好卖,这些都是可以提前分析出来的热点,分析出来之后,我们可以提前放入缓存中保护起来,这在技术上是可以实现的。
所谓“动态热点数据”,就是不能被提前预测到的,系统在运行过程中临时产生的热点。例如,卖家在抖音上做了广告,然后商品一下就火了,导致它在短时间内被大量购买。
所以如何动态预测热点数据就成了我们缓存系统的关键。
这里我给出一个动态热点发现系统的具体实现。
-
构建一个异步的系统,它可以收集交易链路上各个环节中的中间件产品的热点Key,如Nginx、缓存、RPC服务框架等这些中间件(一些中间件产品本身已经有热点统计模块)。
-
建立一个热点上报和可以按照需求订阅的热点服务的下发规范,主要目的是通过交易链路上各个系统(包括详情、购物车、交易、优惠、库存、物流等)访问的时间差把上游已经发现的热点透传给下游系统,提前做好保护。比如,对于大促高峰期,详情系统是最早知道的,在统一接入层上 Nginx模块统计的热点URL。
-
将上游系统收集的热点数据发送到热点服务台,然后下游系统(如交易系统)就会知道哪些商品会被频繁调用,然后做热点保护,对于热点的统计可以很简单的对访问的商品进行访问计数,然后排序还有就是用通常的队列的淘汰算法如lru等都可以实现。
实现思路步骤如下:
1.接入Nginx将请求转发给应用Nginx。
2.应用 Nginx首先读取本地缓存。如果命中,则直接返回,不命中会读取分布式缓存、回源到Tomcat进行处理。
3.应用Nginx 会将请求上报给实时热点发现系统,如使用UDP直接上报请求,或者将请求写到本地kafka,或者使用flume订阅本地 Nginx日志。上报给实时热点发现系统后,它将进行热点统计(可以考虑storm实时计算)。
4.根据设置的阈值将热点数据推送到应用Nginx本地缓存。
我们主要是依赖前面的导购页面(包括首页、搜索页面、商品详情、购物车等)提前识别哪些商品的访问量高,通过这些系统中的中间件来收集热点数据,并记录到日志中。
我们通过部署在每台机器上的Agent把日志汇总到聚合和分析集群中,然后把符合一定规则的热点数据,通过订阅分发系统再推送到相应的系统中。你可以是把热点数据填充到Cache中,或者直接推送到应用服务器的内存中,还可以对这些数据进行拦截,总之下游系统可以订阅这些数据,然后根据自己的需求决定如何处理这些数据。
热点发现要做到接近实时(3s内完成热点数据的发现),因为只有做到接近实时,动态发现才有意义,才能实时地对下游系统提供保护。如果10s内才发送热点就没意义了,因为10s内用户可以进行的操作太多了。时间越长,不可控元素越多,热点缓存命中率越低。
京东在网上开源了热点key探测技术的具体实现:https://gitee.com/jd-platform-opensource/hotkey?_from=gitee_search
本篇文章就到这里,感谢阅读,如果本篇博客有任何错误和建议,欢迎给我留言指正。
相关文章:
高并发系统设计之缓存
本文已收录至Github,推荐阅读 👉 Java随想录 这篇文章来讲讲缓存。缓存是优化网站性能的第一手段,目的是让访问速度更快。 说起缓存,第一反应可能想到的就是Redis。目前比较好的方案是使用多级缓存,如CPU→Ll/L2/L3→…...
【N32WB03x SDK使用指南】
【N32WB03x SDK使用指南】1. 简介1.1 产品简介1.2 主要资源1.3 典型应用2. SDK/开发固件文件目录结构2.1 doc2.2 firmware2.3 middleware2.4 utilities2.5 projects Projects3. 项目配置与烧录3.1 编译环境安装3.2 固件支持包安装3.3 编译环境配置3.4 编译与下载3.5 BLE工程目录…...
pytest测试框架——pytest.ini用法
这里写目录标题一、pytest用法总结二、pytest.ini是什么三、改变运行规则pytest.inicheck_demo.py执行测试用例四、添加默认参数五、指定执行目录六、日志配置七、pytest插件分类八、pytest常用插件九、改变测试用例的执行顺序十、pytest并行与分布式执行十一、pytest内置插件h…...
KAFKA安装与配置(带Zookeeper)2023版
KAFKA安装与配置(带Zookeeper) 一、环境准备: Ubuntu 64位 22.04,三台 二、安装JDK1.8 下载JDK1.8,我这边用的版本是jdk1.8.0_2022、解压jdk tar -zxvf jdk1.8.0_202.tar.gz 3、在/usr/local创建java文件夹,并将解压的jdk移动到/usr/local/java sudo mv jdk1.8.0_202…...
深入浅出解析ChatGPT引领的科技浪潮【AI行研商业价值分析】
Rocky Ding写在前面 【AI行研&商业价值分析】栏目专注于分享AI行业中最新热点/风口的思考与判断。也欢迎大家提出宝贵的意见或优化ideas,一起交流学习💪 大家好,我是Rocky。 2022年底,ChatGPT横空出世,火爆全网&a…...
.net 批量导出文件,以ZIP压缩方式导出
1. 首先Nuget ICSharpCode.SharpZipLib <script type"text/javascript">$(function () {$("#OutPutLink").click(function () { // 单击下文件时$.ajax({ // 先判断条件时间内没有文件url: "/Home/ExistsFile?statTime" $(&q…...
数据分析:某电商优惠卷数据分析
数据分析:某电商优惠卷数据分析 作者:AOAIYI 专栏:python数据分析 作者简介:Python领域新星作者、多项比赛获奖者:AOAIYI首页 😊😊😊如果觉得文章不错或能帮助到你学习,可…...
性能测试流程
性能测试实战一.资源指标分析1.判断CPU是否瓶颈的方法2.判断内存是否瓶颈的方法3.判断磁盘I/O是否瓶颈的方法4.判断网络带宽是否是瓶颈的方法二.系统指标分析三.性能调优四.性能测试案例1.项目背景2.实施规划(1)需求分析(2)测试方…...
zookeeper集群的搭建,菜鸟升级大神必看
一、下载安装zookeeperhttp://archive.apache.org/dist/zookeeper/下载最新版本2.8.1http://archive.apache.org/dist/zookeeper/zookeeper-3.8.1/二、上传安装包到服务器上并且解压,重命名tar -zxvf apache-zookeeper-3.8.1-bin.tar.gzmv apache-zookeeper-3.8.1-b…...
C语言之习题练习集
💗 💗 博客:小怡同学 💗 💗 个人简介:编程小萌新 💗 💗 如果博客对大家有用的话,请点赞关注再收藏 🌞 文章目录牛客网题号: JZ17 打印从1到最大的n位数牛客网题号&#x…...
Buuctf [ACTF新生赛2020]Universe_final_answer 题解
1.程序逻辑 程序逻辑并不复杂: 首先输入字符串,然后对字符串进行一个判断是否满足条件的操作 如果满足则对字符串进行处理并输出,输出的就是flag 2.judge_860函数 显然根据这十个条件可以通过矩阵解线性方程组,这里对变量的命名做了一些调整,让Vi对应flag[i]方便读 …...
【Linux】环境变量
目录背景1.概念2.常见环境变量2.1 PATH指令和自定义程序向环境变量PATH中添加路径删除PATH中的路径2.2 env:显示所有环境变量2.3 环境变量相关的命令3.通过代码获取环境变量1.char* envp[]2.第三方变量enciron3.getenv函数获取指定环境变量4.利用获取的环境变量自制…...
单一职责原则
单一职责原则: 就一个类而言,应该只有一个引起它变化的原因,如果一个类承担的职责过多就等于把这些职责耦合在一起,至少会造成以下两方面的问题: 我们要去修改该类中的一个职责可能会影响到该类的其它职责。这种耦合…...
golangの并发编程(GMP模型)
GMP模型 && channel1. 前言2. GMP模型2.1. 基本概念2.2. 调度器策略2.3. go指令的调度流程2.4. go启动周期的M0和G02.5. GMP可视化2.6. GMP的几种调度场景3. channel3.1. channel的基本使用3.2. 同步器1. 前言 Go中的并发是函数相互独立运行的体现,Gorouti…...
MacBook Pro错误zsh: command not found: brew解决方法
问题描述:本地想安装Jenkins,但是brew指令不存在/我的电脑型号是19款的MacBook Pro(Intel芯片)。解决方法MacBook Pro 重新安装homebrew,用以下命令安装,序列号选择阿里巴巴下载源。/bin/zsh -c "$(cu…...
spring中BeanFactory 和ApplicationContext
在学习spring的高阶内容时,我们有必要先回顾一下spring回顾spring1.什么是springspring是轻量级的,指核心jar包时很小的;非侵入式的一站式框架(数据持久层,web层,核心aop),为了简化企业级开发。核心是IOC&a…...
HC32L17x的LL驱动库之dma
#include "hc32l1xx_ll_dma.h"/// //函 数: //功 能: //输入参数: //输出参数: //说 明: // uint8_t LL_DMA_DeInit(DMA_TypeDef* DMAx, uint32_t Channel) {__IO uint32_t* dmac NULL;dmac &(DMAx->CONFA0);Channel << 4;dmac …...
SSM项目 替换为 SpringBoot
一、运行SSM项目 保证项目改为SpringBoot后运行正常,先保证SSM下运行正常。 项目目录结构 创建数据库,导入sql文件 查看项目中连接数据jar版本,修改对应版本,修改数据库配置信息 配置启动tomcat 运行项目,测试正常…...
RL笔记:动态规划(2): 策略迭代
目录 0. 前言 (4.3) 策略迭代 Example 4.2: Jack’s Car Rental Exercise 4.4 Exercise 4.5 Exercise 4.6 Exercise 4.7 0. 前言 Sutton-book第4章(动态规划)学习笔记。本文是关于其中4.2节(策略迭代)。 (4.3) 策略迭代 基…...
2023软件测试金三银四常见的软件测试面试题-【测试理论篇】
三、测试理论 3.1 你们原来项目的测试流程是怎么样的? 我们的测试流程主要有三个阶段:需求了解分析、测试准备、测试执行。 1、需求了解分析阶段 我们的SE会把需求文档给我们自己先去了解一到两天这样,之后我们会有一个需求澄清会议, 我…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
