10-高并发-应用级缓存

news2024/12/23 9:30:56

缓存简介

缓存,是让数据更接近于使用者,目的是让访问速度更快。

工作机制是先从缓存中读取数据,如果没有,再从慢速设备上读取实际数据并同步到缓存。

那些经常读取的数据、频繁访问的数据、热点数据、I/O瓶颈数据、计算昂贵的数据、符合5分钟法则和局部性原理的数据都可以进行缓存。

如CPU→L1/L2/L3→内存→磁盘就是一个典型的例子,CPU需要数据时先从L1读取,如果没有找到,则查找L2/L3读取,如果没有,则到内存中查找,如果还没有,会到磁盘中查找。

还有比如用过Maven的读者都应该知道,加载依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器找。

另外还有京东的物流为什么那么快?他们在各地都有分仓库,如果该仓库有货物,那么送货的速度是非常快的。

缓存命中率

缓存命中率是从缓存中读取数据的次数与总读取次数的比率,命中率越高越好。
缓存命中率=从缓存中读取次数/〔总读取次数(从缓存中读取次数+从慢速设备上读取次数)〕。这是一个非常重要的监控指标,如果做缓存,则应通过监控这个指标来看缓存是否工作良好。

缓存回收策略

基于空间

基于空间指缓存设置了存储空间,如设置为10MB,当达到存储空间上限时,按照一定的策略移除数据。

基于容量

基于容量指缓存设置了最大大小,当缓存的条目超过最大大小时,按照一定的策略移除旧数据。

基于时间

TTL(Time To Live):存活期,即缓存数据从创建开始直到到期的一个时间段(不管在这个时间段内有没有被访问,缓存数据都将过期)。
TTI(Time To Idle):空闲期,即缓存数据多久没被访问后移除缓存的时间。

基于Java对象引用

  • 软引用:如果一个对象是软引用,那么当JVM堆内存不足时,垃圾回收器可以回收这些对象。
    软引用适合用来做缓存,从而当JVM堆内存不足时,可以回收这些对象腾出一些空间供强引用对象使用,从而避免OOM。
  • 弱引用:当垃圾回收器回收内存时,如果发现弱引用,则将它立即回收。相对于软引用,弱引用有更短的生命周期。

注意:只有在没有其他强引用对象引用弱引用/软引用对象时,垃圾回收时才回收该引用。
即如果有一个对象(不是弱引用/软引用对象)引用了弱引用/软引用对象,那么垃圾回收时不会回收该弱引用/软引用对象。

回收算法

使用基于空间和基于容量的缓存会使用一定的策略移除旧数据,常见的如下。FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除。

LRU(Least Recently Used):最近最少使用算法,使用时间距离现在最久的那个被移除。
LFU(Least Frequently Used):最不常用算法,一定时间段内使用次数(频率)最少的那个被移除。

实际应用中基于LRU的缓存居多,如Guava Cache、Ehcache支持LRU。

Java缓存类型

堆缓存

使用Java堆内存来存储缓存对象。使用堆缓存的好处是没有序列化/反序列化,是最快的缓存。缺点也很明显,当缓存的数据量很大时,GC(垃圾回收)暂停时间会变长,存储容量受限于堆空间大小。一般通过软引用/弱引用来存储缓存对象,即当堆内存不足时,可以强制回收这部分内存释放堆内存空间。一般使用堆缓存存储较热的数据。可以使用Guava Cache、Ehcache 3.x、MapDB实现。

堆外缓存

即缓存数据存储在堆外内存,可以减少GC暂停时间(堆对象转移到堆外,GC扫描和移动的对象变少了),可以支持更大的缓存空间(只受机器内存大小限制,不受堆空间的影响)。但是,读取数据时需要序列化/反序列化,因此会比堆缓存慢很多。

磁盘缓存

即缓存数据存储在磁盘上,在JVM重启时数据还是存在的,而堆缓存/堆外缓存数据会丢失,需要重新加载。可以使用Ehcache 3.x、MapDB实现。

分布式缓存

上文提到的缓存是进程内缓存和磁盘缓存,在多JVM实例的情况下。会存在两个问题:

  • 单机容量问题;
  • 数据一致性问题(多台JVM实例的缓存数据不一致怎么办?),不过,这个问题不用太纠结,既然数据允许缓存,则表示允许一定时间内的不一致,因此可以设置缓存数据的过期时间来定期更新数据;
  • 缓存不命中时,需要回源到DB/服务请求多变问题:每个实例在缓存不命中的情况下都会回源到DB加载数据,因此,多实例后DB整体的访问量就变多了,解决办法是可以使用如一致性哈希分片算法。

这些情况可以考虑使用分布式缓存来解决。可以使用ehcache-clustered(配合Terracotta server)实现Java进程间分布式缓存。

当然也可以使用如Redis实现分布式缓存。

  • 单机时:存储最热的数据到堆缓存,相对热的数据到堆外缓存,不热的数据到磁盘缓存。
  • 集群时:存储最热的数据到堆缓存,相对热的数据到堆外缓存,全量数据到分布式缓存。
    在这里插入图片描述

Guava Cache只提供堆缓存,小巧灵活,性能最好,如果只使用堆缓存,那么使用它就够了。

Ehcache 3.x提供了堆缓存、堆外缓存、磁盘缓存、分布式缓存。但是,其代码注释比较少,API还不完善(比如,2.x支持LRU、LFU、FIFO,而3.x目前还没有API设置),功能还不完善(比如,集群情况下,个人测试结果是暂时不可以在生产环境使用),如果需要较稳定的API和功能,则请考虑使用Ehcache 2.x(不支持堆外缓存)。

MapDB是一款嵌入式Java数据库引擎和集合框架。提供了Maps、Sets、Lists、Queues、Bitmaps的支持,还支持ACID事务、增量备份。支持堆缓存、堆外缓存、磁盘缓存。

缓存使用模式

有人已总结出一些缓存使用模式/模板,我们在使用时直接照搬模式即可。确实已经有总结好的模式,主要分两大类:Cache-Aside和Cache-As-SoR(Read-through、Write-through、Write-behind)。

介绍三个名词:

  • SoR(system-of-record):记录系统,或者可以叫做数据源,即实际存储原始数据的系统。
  • Cache:缓存,是SoR的快照数据,Cache的访问速度比SoR要快,放入Cache的目的是提升访问速度,减少回源到SoR的次数。
  • 回源:即回到数据源头获取数据,Cache没有命中时,需要从SoR读取数据,这叫做回源。

Cache-Aside

Cache-Aside即业务代码围绕着Cache写,是由业务代码直接维护缓存。

对于Cache-Aside,可能存在并发更新情况,即如果多个应用实例同时更新,那么缓存怎么办?

  • 如果是用户维度的数据(如订单数据、用户数据),这种几率非常小,因为并发的情况很少,可以不考虑这个问题,加上过期时间来解决即可。
  • 对于如商品这种基础数据,可以考虑使用canal订阅binlog,来进行增量更新分布式缓存,这样不会存在缓存数据不一致的情况。但是,缓存更新会存在延迟。而本地缓存可根据不一致容忍度设置合理的过期时间。
  • 读服务场景,可以考虑使用一致性哈希,将相同的操作负载均衡到同一个实例,从而减少并发几率,或者设置比较短的过期时间。

Cache-As-SoR

Cache-As-SoR即把Cache看作为SoR,所有操作都是对Cache进行,然后Cache再委托给SoR进行真实的读/写。

即业务代码中只看到Cache的操作,看不到关于SoR相关的代码。有三种实现:read-through、write-through、write-behind。

Read-Through

Read-Through,业务代码首先调用Cache,如果Cache不命中由Cache回源到SoR,而不是业务代码(即由Cache读SoR)。使用Read-Through模式,需要配置一个CacheLoader组件用来回源到SoR加载源数据。Guava Cache和Ehcache 3.x都支持该模式。

Write-Through

Write-Through,被称为穿透写模式/直写模式——业务代码首先调用Cache写(新增/修改)数据,然后由Cache负责写缓存和写SoR,而不是由业务代码。

使用Write-Through模式需要配置一个CacheWriter组件用来回写SoR。Guava Cache没有提供支持。Ehcache
3.x支持该模式。Ehcache需要配置一个CacheLoaderWriter,CacheLoaderWriter知道如何去写SoR。当Cache需要写(新增/修改)数据时,首先调用CacheLoaderWriter来(立即)同步到SoR,成功后会更新缓存。

Write-Behind

Write-Behind,也叫Write-Back,我们称之为回写模式。
不同于Write-Through是同步写SoR和Cache,Write-Behind是异步写。异步之后可以实现批量写、合并写、延时和限流。

Copy Pattern

有两种Copy Pattern,Copy-On-Read(在读时复制)和Copy-On-Write(在写时复制),在Guava Cache和Ehcache中堆缓存都是基于引用的,这样如果有人拿到缓存数据并修改了它,则可能发生不可预测的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1326714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法基础之约数个数

约数个数 核心思想&#xff1a; 用哈希表存每个质因数的指数 然后套公式 #include <iostream>#include <algorithm>#include <unordered_map>#include <vector>using namespace std;const int N 110 , mod 1e9 7;typedef long long LL; //long l…

移动安全APP--Frida+模拟器,模拟器+burp联动

最近测APP被通报了&#xff0c;问题点测得比较深&#xff0c;涉及到frida和burp抓包&#xff0c;一般在公司可能会有网络的限制&#xff0c;手机没办法抓包&#xff0c;我就直接在模拟器上试了&#xff0c;就在这记录一下安装过程。 目录 一、Frida安装 二、burp与逍遥模拟器…

MySQL是如何保证数据不丢失的?

文章目录 前言Buffer Pool 和 DML 的关系DML操作流程加载数据页更新记录 数据持久化方案合适的时机刷盘双写机制日志先行机制日志刷盘机制Redo Log 恢复数据 总结 前言 上篇文章《InnoDB在SQL查询中的关键功能和优化策略》对InnoDB的查询操作和优化事项进行了说明。但是&#…

STM32F4的DHT11初始化与实例分析

STM32—— DHT11 本文主要涉及STM32F4 的DHT11的使用以及相关时序的介绍&#xff0c;最后有工程下载地址。 文章目录 STM32—— DHT11一、 DHT11的介绍1.1 DHT11的经典电路 二、DHT11的通信2.1 DHT11的传输数据格式2.2 DHT11 通信分步解析 三、 DHT11 代码3.1 引脚图3.2 电路图…

污水处理厂可视化:让环保与科技共舞

随着科技的飞速发展&#xff0c;我们的生活环境变得越来越美好。然而&#xff0c;随着城市化进程的加快&#xff0c;污水处理问题也日益凸显。如何有效、高效地处理污水&#xff0c;成为了一个亟待解决的问题。而“污水处理厂可视化”技术的出现&#xff0c;为这个问题提供了一…

隐藏通信隧道技术——防御DNS隧道攻击

隐藏通信隧道技术——防御DNS隧道攻击 DNS协议 ​ DNS协议是一种请求/应答协议&#xff0c;也是一种可用于应用层的隧道技术。虽然激增的DNS流量可能会被发现&#xff0c;但是基于传统Socket隧道已经濒临淘汰及TCP、UDP通信大量被防御系统拦截的状况&#xff0c;DNS、ICMP、H…

第二十二章 : Spring Boot 集成定时任务(一)

第二十二章 &#xff1a; Spring Boot 集成定时任务&#xff08;一&#xff09; 前言 本章知识点&#xff1a; 介绍使用Spring Boot内置的Scheduled注解来实现定时任务-单线程和多线程&#xff1b;以及介绍Quartz定时任务调度框架&#xff1a;简单定时调度器&#xff08;Simp…

QT中网络编程之发送Http协议的Get和Post请求

文章目录 HTTP协议GET请求POST请求QT中对HTTP协议的处理1.QNetworkAccessManager2.QNetworkRequest3.QNetworkReply QT实现GET请求和POST请求Get请求步骤Post请求步骤 测试结果 使用QT的开发产品最终作为一个客户端来使用&#xff0c;很大的一个功能就是要和后端服务器进行交互…

使用代理服务器和Beautiful Soup爬取亚马逊

概述 Beautiful Soup 是一个用于解析 HTML 和 XML 文档的 Python 库&#xff0c;它能够从网页中提取数据&#xff0c;并提供了一些简单的方法来浏览文档树、搜索特定元素以及修改文档的内容。在本文中&#xff0c;我们将介绍如何使用代理服务器和Beautiful Soup库来爬取亚马逊…

nginx 离线安装 https反向代理

这里写自定义目录标题 安装步骤1.安装nginx所需依赖1.1 安装gcc和gcc-c1.1.1下载依赖包1.1.2 上传依赖包1.1.3安装依赖 1.2 安装pcre1.2.1 下载pcre1.2.2 上传解压安装包1.2.3 编译安装 1.3 下载安装zlib1.3.1 下载zlib1.3.2 上传解压安装包1.3.3 编译安装 1.4 下载安装openssl…

UG扫掠体与部件导航器的使用

扫掠体命令的本质在我看来&#xff0c;就是用一个道具沿着轨迹线在选中的实体中进行加工&#xff0c;切除相应部分&#xff1b; 有如下原则 扫掠体&#xff1a; 引导线必须光顺相切&#xff0c;不能有尖角 工具体&#xff1a; 1、必须为单个的实体&#xff0c;不能有孔或内…

[NISACTF 2022]babyserialize

[NISACTF 2022]babyserialize 题目做法及思路解析&#xff08;个人分享&#xff09; 题目平台地址&#xff1a;NSSCTF | 在线CTF平台 一、题目代码 查看分析代码&#xff0c;寻找漏洞点&#xff08;题目中注释为个人思路标注&#xff0c;实际代码中没有&#xff09; …

每日一题,二维平面

给你 二维 平面上两个 由直线构成且边与坐标轴平行/垂直 的矩形&#xff0c;请你计算并返回两个矩形覆盖的总面积。 每个矩形由其 左下 顶点和 右上 顶点坐标表示&#xff1a; 第一个矩形由其左下顶点 (ax1, ay1) 和右上顶点 (ax2, ay2) 定义。 第二个矩形由其左下顶点 (bx1, …

牛客小白月赛78(C: 第K小表示数)

C-第K小表示数_牛客小白月赛78 (nowcoder.com) 问题&#xff1a; 分析: k的极限是1e6,因此要几乎O(n)的时间复杂度给求出来&#xff0c;还需要每插入一个元素我都要去排序&#xff0c;这个时候set就派上用场了&#xff0c;自带排序和去重,集合里面最小和第二小的一定是min(a…

LeetCode 647回文子串 517最长回文子序列 | 代码随想录25期训练营day57

动态规划算法14 LeetCode 647 回文子串 2023.12.20 题目链接代码随想录讲解[链接] int countSubstrings(string s) {//暴力搜索&#xff0c;前两层遍历确定子字符串的起始和末尾位置//第三层循环判断当前子字符串是否为回文串/*int result 0;for (int i 0; i < s.size…

【hadoop】解决浏览器不能访问Hadoop的50070、8088等端口?!

【hadoop】解决浏览器不能访问Hadoop的50070、8088等端口&#xff1f;&#xff01;&#x1f60e; 前言&#x1f64c;【hadoop】解决浏览器不能访问Hadoop的50070、8088等端口&#xff1f;&#xff01;查看自己的配置文件&#xff1a;最终成功访问如图所示&#xff1a; 总结撒花…

Best script for images porter 【容器镜像搬运最佳脚本】

文章目录 1. 简介2. 功能3. 代码4. 示例4.1 拉取 kube-prometheus-stack 55.4.1 版本的镜像 1. 简介 很多情况下&#xff0c;针对一个项目会有很多镜像需要搬运&#xff0c;打包&#xff0c;解压&#xff0c;打标签&#xff0c;推送入库。该项目将针对多个镜像进行管理操作。方…

vscode配置node.js调试环境

node.js基于VSCode的开发环境的搭建非常简单。 说明&#xff1a;本文的前置条件是已安装好node.js(具体安装不再赘述&#xff0c;如有需要可评论区留言)。 阅读本文可掌握&#xff1a; 方便地进行js单步调试&#xff1b;方便地查看内置的对象或属性&#xff1b; 安装插件 C…

GEE:如何解决随机森林分类器的确定性伪随机性?使得每次运行结果(OA、Kappa和混淆矩阵等)不一样

作者:CSDN @ _养乐多_ 在使用 Google Earth Engine(GEE)平台进行土地利用分类时,我们采用了随机森林分类器。理论上,由于该算法的随机性,每次运行后的分类结果应该是不同的。然而,我们在实际应用中却观察到每次运行后总体精度OA值和Kappa值都呈现出完全相同的结果。 这…

云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践

导语 由 InfoQ 主办的 Qcon 全球软件开发者大会北京站上周已精彩落幕&#xff0c;腾讯云中间件团队的冉小龙参与了《云原生机构设计与音视频技术应用》专题&#xff0c;带来了以《云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践》为主题的精彩演讲&#xff0c;在本…