大家好,我是哪吒。

一、前情提要

在上一篇文章中提到,有一个页面加载速度很慢,是通过缓冲流优化的。

在这里插入图片描述

查询的时候,会访问后台数据库,查询前20条数据,按道理来说,这应该很快才对。

追踪代码,看看啥问题,最后发现问题有三:

  1. 表中有一个BLOB大字段,存储着一个PDF模板,也就是上图中的运费模板;
  2. 查询后会将这个PDF模板存储到本地磁盘
  3. 点击线上显示,会读取本地的PDF模板,通过socket传到服务器。

大字段批量查询、批量文件落地、读取大文件并进行网络传输,不慢才怪,这一顿骚操作,5秒能加载完毕,已经烧高香了。

在这里插入图片描述

经过4次优化,将页面的加载时间控制在了1秒以内,实打实的提升了程序的秒开率。

  1. 批量查询时,不查询BLOB大字段;
  2. 点击运费查询时,单独查询+触发索引,实现“懒加载”;
  3. 异步存储文件
  4. 通过 缓冲流 -> 内存映射技术mmap -> sendFile零拷贝 读取本地文件;

在这里插入图片描述

有一个小伙伴在评论中提到,还可以通过缓存继续优化,确实是可以的,缓存也是复用优化的一种。

为了提高页面的加载速度,使用了单条查询 + 触发索引,提高数据库查询速度。

归根结底,还是查询了数据库,如果不查呢,访问速度肯定会更快。

这个时候,就用到了缓存,将运费模板存到缓存中。

二、先了解一下,什么是缓存

缓存就是把访问量较高的热点数据从传统的关系型数据库中加载到内存中,当用户再次访问热点数据时,是从内存中加载,减少了对数据库的访问量,解决了高并发场景下容易造成数据库宕机的问题。

我理解的缓存的本质就是一个用空间换时间的一个思想。

提供“缓存”的目的是为了让数据访问的速度适应CPU的处理速度,其基于的原理是内存中“局部性原理”。

CPU 缓存的是内存数据,用于解决 CPU 处理速度和内存不匹配的问题,比如处理器和内存之间的高速缓存,操作系统在内存管理上,针对虚拟内存 为页表项使用了一特殊的高速缓存TLB转换检测缓冲区,因为每个虚拟内存访问会引起两次物理访问,一次取相关的页表项,一次取数据,TLB引入来加速虚拟地址到物理地址的转换。

1、缓存有哪些分类

  1. 操作系统磁盘缓存,减少磁盘机械操作
  2. 数据库缓存,减少文件系统 I/O
  3. 应用程序缓存,减少对数据库的查询
  4. Web 服务器缓存,减少应用程序服务器请求
  5. 客户端浏览器缓存,减少对网站的访问

2、本地缓存与分布式缓存

本地缓存:在客户端本地的物理内存中划出一部分空间,来缓存客户端回写到服务器的数据。当本地回写缓存达到缓存阈值时,将数据写入到服务器中。

本地缓存是指程序级别的缓存组件,它的特点是本地缓存和应用程序会运行在同一个进程中,所以本地缓存的操作会非常快,因为在同一个进程内也意味着不会有网络上的延迟和开销。

本地缓存适用于单节点非集群的应用场景,它的优点是快,缺点是多程序无法共享缓存。

无法共享缓存可能会造成系统资源的浪费,每个系统都单独维护了一份属于自己的缓存,而同一份缓存有可能被多个系统单独进行存储,从而浪费了系统资源。

分布式缓存是指将应用系统和缓存组件进行分离的缓存机制,这样多个应用系统就可以共享一套缓存数据了,它的特点是共享缓存服务和可集群部署,为缓存系统提供了高可用的运行环境,以及缓存共享的程序运行机制。

下面介绍一个小编最常用的本地缓存 Guava Cache。

三、Guava Cache本地缓存

1、Google Guava

Google Guava是一个Java编程库,其中包含了许多高质量的工具类和方法。其中,Guava的缓存工具之一是LoadingCache。LoadingCache是一个带有自动加载功能的缓存,可以自动加载缓存中不存在的数据。其实质是一个键值对(Key-Value Pair)的缓存,可以使用键来获取相应的值。

Guava Cache 的架构设计灵感来源于 ConcurrentHashMap,它使用了多个 segments 方式的细粒度锁,在保证线程安全的同时,支持了高并发的使用场景。Guava Cache 类似于 Map 集合的方式对键值对进行操作,只不过多了过期淘汰等处理逻辑。

Guava Cache对比ConcurrentHashMap优势在哪?

  1. Guava Cache可以设置过期时间,提供数据过多时的淘汰机制;
  2. 线程安全,支持并发读写;
  3. 在缓存击穿时,GuavaCache 可以使用 CacheLoader 的load 方法控制,对同一个key,只允许一个请求去读源并回填缓存,其他请求阻塞等待;

2、Loadingcache数据结构

在这里插入图片描述

  1. Loadingcache含有多个Segment,每一个Segment中有若干个有效队列;
  2. 多个Segment之间互不打扰,可以并发执行;
  3. 各个Segment的扩容只需要扩自己,与其它的Segment无关;
  4. 设置合适的初始化容量与并发水平参数,可以有效避免扩容,但是设置的太大了,耗费内存,设置的太小,缓存价值降低,需要根据业务需求进行权衡;
  5. Loadingcache数据结构和ConcurrentHashMap很相似,ReferenceEntry用于存放key-value;
  6. 每一个ReferenceEntry都会存放一个双向链表,采用的是Entry替换的方式;
  7. 每次访问某个元素就将元素移动到链表头部,这样链表尾部的元素就是最近最少使用的元素,替换的复杂度为o(1),但是访问的复杂度还是O(n);
  8. 队列用于实现LRU缓存回收算法;

3、Loadingcache数据结构构建流程:

  1. 初始化CacheBuilder,指定参数(并发级别、过期时间、初始容量、缓存最大容量),使用build()方法创建LocalCache实例;
  2. 创建Segment数组,初始化每一个Segment;
  3. 为Segment属性赋值;
  4. 初始化Segment中的table,即一个ReferenceEntry数组(每一个key-value就是一个ReferenceEntry);
  5. 根据之前类变量的赋值情况,创建相应队列,用于LRU缓存回收算法。

4、判断缓存是否过期

  1. expireAfterWrite,在put时更新缓存时间戳,在get时如果发现当前时间与时间戳的差值大于过期时间戳,就会进行load操作;
  2. expireAfterAccess,在expireAfterWrite的基础上,不管是写还是读都会记录新的时间戳;
  3. refreshAfterWrite,调用get进行值的获取的时候才会执行reload操作,这里的刷新操作可以通过异步调用load实现。

5、Loadingcache如何解决缓存穿透

缓存穿透是指在Loadingcache缓存中,由于某些原因,缓存的数据无法被正常访问或处理,导致缓存失去了它的作用。

发生缓存穿透的原因有很多,比如数据量过大、数据更新频繁、数据过期、数据权限限制、缓存性能瓶颈等原因,这里不过多纠结。

(1)expireAfterAcess和expireAfterWrite同步加载

设置为expireAfterAcess和expireAfterWrite时,在进行get的过程中,缓存失效的话,会进行load操作,load是一个同步加载的操作,如下图:

如果发生了缓存穿透,当有大量并发请求访问缓存时,会有一个线程去同步查询DB,随即通过reeatrantLock进入loading等待状态,其它请求相同key的线程,一部分在waitforvalue,另一部分在reentantloack的阻塞队列中,等待同步查询完毕,所有请求都会获得最新值。

在这里插入图片描述

(2)refreshAfterWrite同步加载

如果采用refresh的话,会通过scheduleRefresh方法进行load,也是一个线程同步获取DB。

其它线程不会阻塞,性能比expireAfterWrite同步加载高,但是,可能返回新值、也可能返回旧值。

在这里插入图片描述

(3)refreshAfterWrite异步加载

当加载缓存的线程是异步加载的话,对于请求1,如果在异步结束前返回,就会返回旧值,反之是新值。

对于其他线程来说,不会被阻塞,直接返回,返回值可能是新值或者是旧值。
在这里插入图片描述

Loadingcache没使用额外的线程去做定时清理和加载的功能,而是依赖于get()请求。

在查询的时候,进行时间对比,如果使用refreshAfterWrite,在长时间没有查询时,查询有可能会得到一个旧值,我们可以通过设置refreshAfterWrite(写刷新,在get时可以同步或异步缓存的时间戳)为5s,将expireAfterWrite(写过期,在put时更新缓存的时间戳)设为10s,当访问频繁的时候,会在每5秒都进行refresh,而当超过10s没有访问,下一次访问必须load新值。

四、Redis中如何解决缓存穿透

如果发生了缓存穿透,可以针对要查询的数据,在Redis中插入一条数据,添加一个约定好的默认值,比如defaultNull。

比如你想通过某个id查询某某订单,Redis中没有,MySQL中也没有,此时,就可以在Redis中插入一条,存为defaultNull,下次再查询就有了,因为是提前约定好的,前端也明白是啥意思,一切OK,岁月静好。

在这里插入图片描述

五、使用loadingCache优化页面加载

1、引入pom

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

2、初始化LoadingCache

private static LoadingCache<String, String> loadCache;

public static void initLoadingCache() {
    loadCache = CacheBuilder.newBuilder()
            // 并发级别设置为 10,是指可以同时写缓存的线程数
            .concurrencyLevel(10)
            // 写刷新,在get时可以同步或异步缓存的时间戳
            .refreshAfterWrite(5, TimeUnit.SECONDS)
            // 写过期,在put时更新缓存的时间戳
            .expireAfterWrite(10, TimeUnit.SECONDS)
            // 设置缓存容器的初始容量为 10
            .initialCapacity(10)
            // 设置缓存最大容量为 100,超过之后就会按照 LRU 算法移除缓存项
            .maximumSize(100)
            // 设置要统计缓存的命中率
            .recordStats()
            // 指定 CacheLoader,缓存不存在时,可自动加载缓存
            .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            // 自动加载缓存的业务
                            return "error";
                        }
                    }
            );
}

3、优化5:通过LoadingCache缓存模板数据,在编辑模板后,更新缓存

查询模板信息后,通过loadCache.put(uuid, pdf);加载到内存中,在编辑模板时,更新缓存过期时间,下次获取模板PDF时,直接从LoadingCache缓存中取,降低数据库访问压力,perfect!!!
在这里插入图片描述

然并卵,这种情况是不适合缓存的,因为模板pdf本来就是一个BLOB大数据,你把它放内存里缓存了,你告诉我,能放几个?内存扛得住吗?

相关文章

Redis高并发分布锁实战

Redis分布式锁自己去实现可能会出现几个问题没有在finally显示释放锁,当客户端挂掉了,锁没有被及时删除,这样会导致死锁问题,它这个是需要我们显示的释放锁假如此时我们设置过期时间,但是我们用的是同一个key,就可能出现下一个线程删除上一个线程的锁,但是上一个线程还没有执行完,它这个需要key是不能重复的假如我们既设置了过期时间也指定了不同的key,此时可能因为网络延迟出现上一个线程删除下一个线程的锁,也就是说业务执行的时间超过了锁过期的时间,它这个需要一个锁续命的功能。

Git如何清除缓存?这四个命令得会!

git rm适用于从版本库和工作目录中删除文件,但无法处理未被追踪文件。git reset可以用于回滚提交和从缓存中移除文件,但不涉及工作目录中未被追踪的文件。提供更高级的索引控制,但不直接删除文件。git clean用于删除工作目录中未被追踪的文件。在实际使用中,根据具体的需求和场景选择适当的命令。清除缓存是一项破坏性操作,需要慎重对待,确保在清除之前做好备份,并尽量避免不必要的清除操作。

日常遇到Maven出现依赖版本/缓存问题通用思路。

如果怀疑是本地仓库中缓存的依赖有问题,可以手动删除本地仓库(默认位置在用户的.m2/repository目录下),但这是一个较为极端的做法,因为这会删除所有项目的所有本地依赖,之后Maven将不得不重新下载这些依赖。针对于这样的问题 首先我们的第一思路 就是怀疑到是缓存的问题,那么我在这里去描述一下 我们遇到这类通用类的问题如何解决。检查项目的pom.xml文件,确认依赖声明正确无误,没有冲突的版本号或不正确的依赖范围。版本问题导致的,但是我确认过了一下的一些操作 依然没有解决我的问题。

Linux 目录磁盘满了,怎么查找大文件

如果你不确定某个文件或目录的用途,最好先进行调查或咨询专业人士,而不是直接删除它们。,这是一个基于文本的磁盘使用分析器,非常适合于查找大文件。如果它没有预装,你可以通过你的包管理器安装它(例如,在Ubuntu上使用。会分析根目录的磁盘使用情况,并提供一个交互式界面来浏览最大的文件和目录。请注意,运行这些命令可能需要一些时间,因为它们会检查许多文件。)磁盘满了,你可以使用以下方法来查找占用空间最大的文件和目录。这个命令会搜索根目录下所有的文件,并显示它们的大小。为了找到最大的文件,你可以使用。

IP定位技术助力网络安全保护

当网络遭受DDoS攻击时,通过IP定位技术,可以迅速确定攻击源头的地理位置,进而采取相应的防御措施,如屏蔽攻击源IP地址等。随着技术的不断进步和应用场景的不断拓展,IP定位技术将在网络安全保护领域发挥更加重要的作用,为网络空间的安全稳定提供有力保障。例如,网络管理员可以根据IP定位结果,将恶意IP地址列入黑名单,阻止其访问网络,从而避免潜在的安全威胁。IP定位技术是一种基于IP地址的地理位置定位技术,通过分析IP地址的归属地信息,可以精确地确定网络用户的地理位置,从而实现对网络流量的有效监控和管理。

使用redis-insight连接到服务器上的redis数据库

我们现在虽然安装好了redis数据库,但是外界是连接不到的,我们需要打破这个限制!设置完之后,可以按以下图的命令查看,redis的密码是不是起作用了。的更改,并退出编辑器。在网上下载好redis-insight的客户端,打开。默认情况下,它可能被设置为只监听本地连接,如。这允许在没有进行身份验证的情况下接受外部连接。(3)为了增强安全性,强烈建议设置访问密码。三、使用redis-insight连接数据库。1.查找redis的配置文件。指令,并确保将其设置为。替换为你自己的强密码。

.[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复

这些勒索病毒往往攻击入侵的目标基本是Windows系统的服务器,包括一些市面上常见的业务应用软件,例如:金蝶软件数据库,用友软件数据库,管家婆软件数据库,速达软件数据库,科脉软件数据库,海典软件数据库,思迅软件数据库,OA软件数据库,ERP软件数据库,自建网站的数据库等,均是其攻击加密的常见目标文件,所以有以上这些业务应用软件的服务器更应该注意做好服务器安全加固及数据备份工作。通过采取这些预防措施,您可以最大程度地降低成为[hudsonL@cock.li].mkp勒索病毒等威胁的风险,并保护您的数据安全。

服务器与电脑的区别?

服务器是指一种专门提供计算和存储资源、运行特定软件服务的物理或虚拟计算机。服务器主要用于接受和处理来自客户端(如个人电脑、手机等)的请求,并向客户端提供所需的服务或数据。服务器在网络环境中扮演着中心节点的角色,负责存储和管理数据、提供网络服务、处理计算任务等。

linux docker 部署mysql8以上版本时弹出Access denied for user root @ localhost (using password: YES)的解决方案

mysql8登录第一次遇到MYSQL_ROOT_PASSWORD时会自动把该密码尽兴登录,生成一个秘钥放在mysql的数据文件里面,命令里带的MYSQL_ROOT_PASSWORD密码是个参数,除了第一次运行mysql带上会设置密码生成秘钥,其他次启动而不是设置mysql的密码,而是作为参数去验证这个最初的秘钥是否核对正确,于是我进入挂载的data目录,发现我的猜想是对的。通过docker将服务部署完后,navicat连接报错,密码错误,于是我尝试进入mysql容器登录 发现也报错。

Ubuntu使用Docker部署Nginx并结合内网穿透实现公网远程访问

cpolar安装成功后,在外部浏览器上访问Linux 的9200端口即:【http://服务器的局域网ip:9200】,使用cpolar账号登录,登录后即可看到cpolar web 配置界面,结下来在web 管理界面配置即可。如果有长期远程访问本地Nginx服务的需求,但又不想每天重新配置公网地址,还想地址好看又好记,那我推荐大家选择使用固定的二级子域名方式来远程访问。,点击左侧的预留,选择保留二级子域名,地区选择China VIP,设置一个二级子域名名称,点击保留,保留成功后复制保留的二级子域名名称。

Linux 磁盘空间占用率100%的排查

max-size 最大数值 , max-file 最大日志数,但一两个容器还好,但是如果有很多容器需要管理,这样就很不方便了,最好还是可以统一管理(全局修改)结果显示多条如下数据,这里最关键的指标就是使用百分比,这个值较高一般需要处理,或者明确知道自己项目或工作目录是哪个,就只要找对应的位置去处理即可。/var/lib/docker/overlay2 【文件系统】基于容器文件系统保存的数据会写到本机的此目录下,进行限制,以减少日志文件对存储空间的占用,以下配置分别为日志文件最大容量、最大日志文件数。

内网穿透、远程桌面、VPN的理解

针对不同的场景可能咱们可以选择不同的方法,局域网远程桌面这种其实也不是很常用,为什么呢,因为如果就在局域网的话,那么你本人直接过去操作就可以了,不需要远程桌面,而且远程桌面还需要给你的登录账户和密码,这些都是隐私的东西,一般最好不对外泄露。VPN其实是比较适合在家远程办公的场景的,电脑带回家,然后连接VPN,就可以实现办公了,但是也有一些问题,就是公司必须要一个固定的IP,还必须要配一个VPN服务器,固定IP这个是越来越少了,很多宽带都不是固定外网IP了。

Web 安全之点击劫持(Clickjacking)攻击详解

点击劫持(Clickjacking)攻击,又称为界面伪装攻击,是一种利用视觉欺骗手段进行攻击的方式。攻击者通过技术手段欺骗用户点击本没有打算点击的位置,当用户在被攻击者攻击的页面上进行操作时,实际点击结果被劫持,从而被攻击者利用。这种攻击方式利用了用户对网站的信任,通过覆盖层(通常是透明的iframe)覆盖在另一个网页之上,使受害者无法察觉。

Docker网络配置与自定义IP容器通信

互联网是由许多小型网络构成的,每个网络上都有许多主机,这样便构成了一个有层次的结构。IP地址在设计时就考虑到地址分配的层次特点,将每个IP地址都分割成网络号和主机号两部分,以便于 IP地址的寻址操作。

linux配置DNS主从服务器

主服务器:OpenElur Linux IP地址为192.168.188.129。从服务器:RedHat Linux IP地址为192.168.188.128。2.配置主服务器的`解析配置文件。3.进行从服务器的基础配置。1.进行主服务器的基础配置。

大数据深度学习卷积神经网络CNN:CNN结构、训练与优化一文全解

卷积神经网络是一种前馈神经网络,它的人工神经元可以响应周围单元的局部区域,从而能够识别视觉空间的部分结构特征。卷积层: 通过卷积操作检测图像的局部特征。激活函数: 引入非线性,增加模型的表达能力。池化层: 减少特征维度,增加模型的鲁棒性。全连接层: 在处理空间特征后,全连接层用于进行分类或回归。卷积神经网络的这些组件协同工作,使得CNN能够从原始像素中自动学习有意义的特征层次结构。随着深度增加,这些特征从基本形状和纹理逐渐抽象为复杂的对象和场景表现。

如何配置Pycharm服务器并结合内网穿透工具实现远程开发

本文主要介绍如何使用Pycharm进行远程开发,并实现在家远程与公司服务器资源同步。新版本Jetbrains系列开发IDE(IntelliJ IDEA,PyCharm,GoLand)等都支持远程使用服务器编译,并且可以通过SFTP同步本地与服务器项目代码。这样做的好处是我们只要连接上服务器就能开始干活儿,不用折腾环境,不占用个人笔记本资源,最重要的是不用忍受笔记本的烂风扇噪音。

什么是DDOS高防ip?DDOS高防ip是怎么防护攻击的

同时,高防IP设备还支持防御日志的记录和分析功能,可以对攻击流量进行深入分析,进一步了解攻击来源和特点,为后续的防御策略提供数据支持。为了解决这些问题,高防IP作为一种网络安全服务应运而生。高防IP通过实时监测和分析流量,识别和拦截恶意流量,确保正常流量能够安全地转发给目标服务器,从而保护企业的业务正常运行。高防IP设备会对流量进行实时监测和分析,对流量的来源、目的地、协议、数据包大小、数据包速率等进行全面分析和判断。通过对流量的实时监测和分析,可以及时发现异常流量的存在,为后续的防御策略提供依据。

详解动态网页数据获取以及浏览器数据和网络数据交互流程-Python

动态网页是一种在用户浏览时实时生成或变化的网页。。相比之下,动态网页可以根据用户的互动、请求或其他条件在浏览器端或服务器端生成新的内容。而且现在的网页一般都是采用前后端分离的架构,前端负责展示和用户交互,后端负责数据处理。这种架构使得前端可以更加灵活地实现动态内容的加载和展示。所以说以后想要获取到数据,动态网页数据获取会成为我们主流获取网页数据的技术。所以在动态网页数据获取这方面我们需要下足功夫了解动态网页数据交互形式、数据存储访问模式等方方面面的知识,我们才好更加灵活的获取到数据。

[redis] redis的安装,配置与简单操作

Redis是一个开源、基于内存、使用C语言编写的key-value数据库,并提供了多种语言的API。它的数据结构十分丰富,主要可以用于数据库、缓存、分布式锁、消息队列等...Redis服务器程序是单进程模型,也就是在一台服务器上可以同时启动多个Redis进程,Redis的实际处理速度则是完全依靠于主进程的执行效率。若在服务器上只运行一个Redis进程,当多个客户端同时访问时,服务器的处理能力是会有一定程度的下降;

Samtec卓越应用 | SEARAY:最大限度提高设计灵活性和密度

与标准的BGA/焊球连接相比,Solder Charge互连在连接器与印刷电路板的组装过程中。是专为高带宽应用而设计的坚固触头点。如果您需要SEARAY™尺寸的高速、高密度电缆组件,请查看SEAC系列阵列电缆组件。0.80 毫米间距系统的密度是标准 1.27 毫米栅格的两倍。为设计人员提供了大量的设计灵活性,远远超过业内任何其他阵列产品。这样就能实现更长的循环寿命和更优越的电气性能。如果使用我们推荐的引脚分配,设计人员的单端设计最多可获得。,这在设计高引脚数连接器时是一个重要的考虑因素。

网络知识-以太网技术的发展及网络设备

大家都被互联网上各种各样的内容、技术闪亮了眼睛,没有太多人去了解比较底层的一些网络技术。面试的时候,我也问过很多技术人员,对以太网是否了解,了解多少?但是很多人都知之甚少!但是,在我们实际工作碰到问题、分析问题、定位问题、解决问题的时候,又必须要了解这方面的知识。以太网最初到现在的主要设备包括集线器、中继器、网桥、交换机。以太网目前应用在很多行业,在视频监控、安防、视频会议等领域都有很广泛的应用。

详解静态网页数据获取以及浏览器数据和网络数据交互流程-Python

在网站设计领域,基于纯HTML格式构建的网页通常定义为静态网页,这种类型的网页是早期网站建设的主要形式。对于网络爬虫来说,抓取静态网页中的数据相对较为简单,因为所需的所有信息都直接嵌入在网页的HTML代码里。然而,对于那些利用AJAX技术动态加载数据的网页,其数据并不总是直接出现在HTML代码中,这对爬虫的抓取工作造成了一定的难度。在静态网页的数据抓取过程中,Requests库显示出其卓越的实用性。这个库不仅功能全面,而且操作简洁直观。

如何使用Node.js快速创建本地HTTP服务器并实现公网访问服务端

Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation(原为 Node.js Foundation,已与 JS Foundation 合并)持有和维护,亦为 Linux 基金会的项目。Node.js 采用 Google 开发的 V8 运行代码,使用事件驱动、非阻塞和异步输入输出模型等技术来提高性能,可优化应用程序的传输量和规模。这些技术通常用于资料密集的即时应用程序。Node.js 大部分基本模块都用 JavaScri
返回
顶部