性能优化2.0,新增缓存后,程序的秒开率不升反降

这篇具有很好参考价值的文章主要介绍了性能优化2.0,新增缓存后,程序的秒开率不升反降。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

大家好,我是哪吒。

一、前情提要

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

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

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

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

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

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

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

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

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

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

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

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

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

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

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

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

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

提供“缓存”的目的是为了让数据访问的速度适应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数据结构

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

  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.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

(2)refreshAfterWrite同步加载

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

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

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

(3)refreshAfterWrite异步加载

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

对于其他线程来说,不会被阻塞,直接返回,返回值可能是新值或者是旧值。
性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

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

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

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

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

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

性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

五、使用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!!!
性能优化2.0,新增缓存后,程序的秒开率不升反降,搬砖工逆袭Java架构师,性能优化,缓存,服务器,网络

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

优化炫技一时爽,BUG不断一直爽,一直爽


🏆文章收录于:100天精通Java从入门到就业

全网最细Java零基础手把手入门教程,系列课程包括:Java基础、Java8新特性、Java集合、高并发、性能优化等,适合零基础和进阶提升的同学。

🏆哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师

华为OD机试 2023B卷题库疯狂收录中,刷题点这里

刷的越多,抽中的概率越大,每一题都有详细的答题思路、详细的代码注释、样例测试,发现新题目,随时更新,全天CSDN在线答疑。文章来源地址https://www.toymoban.com/news/detail-793034.html

到了这里,关于性能优化2.0,新增缓存后,程序的秒开率不升反降的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android性能优化—ViewPagers + Fragment缓存优化

    大家看标题,可能会有点儿懵,什么是ViewPagers,因为在很久之前,我们使用的都是ViewPager,但是现在更多的是在用ViewPager2,因此用ViewPagers(ViewPager、ViewPager2)来代替两者,主要介绍两者的区别。 ViewPagers嵌套Fragment架构,在我们常用的App中随处可见,抖音的首页、各大电商

    2024年02月01日
    浏览(59)
  • Java架构师缓存性能优化

    想学习架构师构建流程请跳转:Java架构师系统架构设计

    2024年02月07日
    浏览(43)
  • Redis缓存设计与性能优化

    缓存穿透是指查询一个根本不存在的数据, 缓存层和存储层都不会命中 , 通常出于容错的考虑, 如果从存储层查不到数据则不写入缓存层。 缓存穿透将导致不存在的数据每次请求都要到存储层去查询, 失去了缓存保护后端存储的意义。 造成缓存穿透的基本原因有两个:

    2024年02月07日
    浏览(48)
  • 前端性能优化之HTTP缓存

    前端缓存可分为两大类: HTTP 缓存 和 浏览器缓存 。 我们今天重点是 HTTP 缓存 ,下面这张图是前端缓存的一个大致知识点: 首先解决困扰绕人们的老大难问题: 一、什么是HTTP缓存? HTTP 缓存会存储与请求关联的响应,并将存储的响应复用于后续请求。(MDN) 通俗的讲,HTTP

    2024年02月06日
    浏览(75)
  • 除了缓存,性能优化还可以这么搞?

    软件设计开发某种意义上是“取”与“舍”的艺术。关于性能方面,就像建筑设计成抗震9度需要额外的成本一样,高性能软件系统也意味着更高的实现成本,有时候与其他质量属性甚至会冲突,比如安全性、可扩展性、可观测性等等。 大部分时候我们需要的是:在业务遇到

    2024年02月03日
    浏览(48)
  • 49.Redis缓存设计与性能优化

    缓存与数据库双写不一致 小概率事件 //线程1 写数据库stock = 5 ---------------》更新缓存 //线程2 写数据库stock = 4 -----》更新缓存 //线程1 ------》写数据库stock = 10 -----》删除缓存 //线程2 -----------------------------------------------------------------------------------------------》写数据库stock = 9 -

    2024年02月08日
    浏览(43)
  • 高并发缓存实战RedisSon、性能优化

    对于经常访问的数据保留在redis缓存当中,不用带数据设置超时时间定期删除控制redis的大小 缓存击穿数据库没有被击穿 如果商家是批量导入的数据,呢么就会同时存到redis中,设置固定的时间就会导致缓存在一瞬间失效,用户访问不到就会将流量打到数据库上造成数据库段

    2024年02月13日
    浏览(65)
  • 6. Redis缓存设计与性能优化

    本文是按照自己的理解进行笔记总结,如有不正确的地方,还望大佬多多指点纠正,勿喷。 课程内容: 1、多级缓存架构详解 2、缓存穿透缓存击穿缓存雪崩详解 3、热点缓存key重建优化 4、缓存与数据库双写不一致终极解决 5、Redis开发规范与性能优化 ngnix到Lua到web层,到re

    2024年02月11日
    浏览(53)
  • H5页面秒开优化与实践

    3月份针对线上重点H5项目秒开进行治理,本文将逐步介绍如何通过H5页面的优化手段来提高 1.5 秒开率。 从用户角度看,优化能够让页面加载得更快、对用户操作响应更及时,用户体验更良好,提升用户体验和降低用户流失率非常重要。其中 Global Web Performance Matters for ecommerc

    2024年02月08日
    浏览(61)
  • React性能优化之memo缓存函数

    React是一个非常流行的前端框架,但是在处理大型应用程序时,性能可能会成为一个问题。为了解决这个问题,React提供了一个称为memo的功能,它可以缓存函数并避免不必要的重新渲染。 memo是React中的一个高阶组件(HOC),它接收一个组件并返回一个新的组件。这个新组件具

    2024年02月11日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包