除了缓存,性能优化还可以这么搞?

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

01、引言:取与舍

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

大部分时候我们需要的是:在业务遇到瓶颈之前,利用常见的技术手段将系统优化到预期水平。那么,「性能优化有哪些技术方向和手段呢」 ?

「性能优化通常是“时间”与“空间”的互换与取舍」 。本篇分两个部分,在上篇,讲解六种通用的“时间”与“空间”互换取舍的手段:

  • 索引术

  • 压缩术

  • 缓存术

  • 预取术

  • 削峰填谷术

  • 批量处理术

在下篇,介绍四种进阶性的内容,大多与「提升并行能力」 有关:

  • 八门遁甲 —— 榨干计算资源

  • 影分身术 —— 水平扩容

  • 奥义 —— 分片术

  • 秘术 —— 无锁术

每种性能优化的技术手段,我都找了一张「应景」 的《火影忍者》中人物或忍术的配图,大家都知道对应的是那个忍者和忍术吗,评论区打出来。

注:所有配图来自动漫《火影忍者》,部分图片添加了文字方便理解,仅作技术交流用途

02、索引术

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

10ms之后。

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

「索引」的原理是「拿额外的存储空间换取查询时间」,增加了「写入数据」的开销,但使「读取数据」 的时间复杂度一般从O(n)降低到O(logn)甚至O(1)。索引不仅在数据库中广泛使用,前后端的开发中也在不知不觉运用。

在数据集比较大时,不用索引就像从一本「没有目录而且内容乱序」的新华字典查一个字,得一页一页全翻一遍才能找到;用索引之后,就像用拼音先在目录中先「找到要查到字在哪一页」 ,直接翻过去就行了。书籍的目录是典型的树状结构,那么软件世界常见的索引有哪些数据结构,分别在什么场景使用呢?

  • 「哈希表(Hash Table)」 :哈希表的原理可以类比银行办业务取号,给每个人一个号(计算出的Hash值),叫某个号直接对应了某个人,索引效率是最高的O(1),消耗的存储空间也相对更大。K-V存储组件以及各种编程语言提供的Map/Dict等数据结构,多数底层实现是用的哈希表。

  • 「二叉搜索树(Binary Search Tree)」:有序存储的二叉树结构,在编程语言中广泛使用的「红黑树」 属于二叉搜索树,确切的说是“不完全平衡的”二叉搜索树。从C++、Java的TreeSet、TreeMap,到Linux的CPU调度,都能看到红黑树的影子。Java的HashMap在发现某个Hash槽的链表长度大于8时也会将链表升级为红黑树,而相比于红黑树“更加平衡”的AVL树反而实际用的更少。

  • 「平衡多路搜索树(B-Tree)」 :这里的B指的是Balance而不是Binary,二叉树在大量数据场景会导致查找深度很深,解决办法就是变成多叉树,MongoDB的索引用的就是B-Tree。

  • 「叶节点相连的平衡多路搜索树(B+ Tree)」 :B+ Tree是B-Tree的变体,只有叶子节点存数据,叶子与相邻叶子相连,MySQL的索引用的就是B+树,Linux的一些文件系统也使用的B+树索引inode。其实B+树还有一种在枝桠上再加链表的变体:B*树,暂时没想到实际应用。

  • 「日志结构合并树(LSM Tree)」 :Log Structured Merge Tree,简单理解就是像日志一样顺序写下去,多层多块的结构,上层写满压缩合并到下层。LSM Tree其实本身是为了优化写性能牺牲读性能的数据结构,并不能算是索引,但在大数据存储和一些NoSQL数据库中用的很广泛,因此这里也列进去了。

  • 「字典树(Trie Tree)」 :又叫前缀树,从树根串到树叶就是数据本身,因此树根到枝桠就是前缀,枝桠下面的所有数据都是匹配该前缀的。这种结构能非常方便的做前缀查找或词频统计,典型的应用有:自动补全、URL路由。其变体基数树(Radix Tree)在Nginx的Geo模块处理子网掩码前缀用了;Redis的Stream、Cluster等功能的实现也用到了基数树(Redis中叫Rax)。

  • 「跳表(Skip List)」 :是一种多层结构的有序链表,插入一个值时有一定概率“晋升”到上层形成间接的索引。跳表更适合大量并发写的场景,不存在红黑树的再平衡问题,Redis强大的ZSet底层数据结构就是哈希加跳表。

  • 「倒排索引(Inverted index)」 :这样翻译不太直观,可以叫“关键词索引”,比如书籍末页列出的术语表就是倒排索引,标识出了每个术语出现在哪些页,这样我们要查某个术语在哪用的,从术语表一查,翻到所在的页数即可。倒排索引在全文索引存储中经常用到,比如ElasticSearch非常核心的机制就是倒排索引;Prometheus的时序数据库按标签查询也是在用倒排索引。

「数据库主键之争」 :自增长 vs UUID。主键是很多数据库非常重要的索引,尤其是MySQL这样的RDBMS会经常面临这个难题:是用自增长的ID还是随机的UUID做主键?

自增长ID的性能最高,但不好做分库分表后的全局唯一ID,自增长的规律可能泄露业务信息;而UUID不具有可读性且太占存储空间。争执的结果就是找一个兼具二者的优点的「折衷方案」:用「雪花算法」 生成分布式环境全局唯一的ID作为业务表主键,性能尚可、不那么占存储、又能保证全局单调递增,但引入了额外的复杂性,再次体现了取舍之道。

再回到「数据库」 中的索引,建索引要注意哪些点呢?

  • 定义好主键并尽量使用主键,多数数据库中,主键是效率最高的「聚簇索引」 ;

  • 「Where」「Group By、Order By、Join On」条件中用到的字段也要「按需」 建索引或联合索引,MySQL中搭配explain命令可以查询DML是否利用了索引;

  • 类似枚举值这样重复度太高的字段「不适合」 建索引(如果有位图索引可以建),频繁更新的列不太适合建索引;

  • 单列索引可以根据「实际查询的字段」升级为「联合索引」,通过部分冗余达到「索引覆盖」,以「避免回表」 的开销;

  • 尽量减少索引冗余,比如建A、B、C三个字段的联合索引,Where条件查询A、A and B、A and B and C 都可以利用该联合索引,就无需再给A单独建索引了;

  • 根据数据库特有的索引特性选择适合的方案,比如像MongoDB,还可以建自动删除数据的「TTL索引」、不索引空值的「稀疏索引」、地理位置信息的「Geo索引」 等等。

「数据库之外」,在代码中也能应用索引的思维,比如对于集合中大量数据的查找,使用「Set、Map、Tree」这样的数据结构,其实也是在用哈希索引或树状索引,比「直接遍历」 列表或数组查找的性能高很多。

03、缓存术

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

「缓存」优化性能的原理和索引一样,是拿额外的「存储空间换取查询时间」 。缓存无处不在,设想一下我们在浏览器打开这篇文章,会有多少层缓存呢?

  • 首先解析DNS时,浏览器一层DNS缓存、操作系统一层DNS缓存、DNS服务器链上层层缓存;

  • 发送一个GET请求这篇文章,服务端很可能早已将其缓存在KV存储组件中了;

  • 即使没有击中缓存,数据库服务器内存中也缓存了最近查询的数据;

  • 即使没有击中数据库服务器的缓存,数据库从索引文件中读取,操作系统已经把热点文件的内容放置在Page Cache中了;

  • 即使没有击中操作系统的文件缓存,直接读取文件,大部分固态硬盘或者磁盘本身也自带缓存;

  • 数据取到之后服务器用模板引擎渲染出HTML,模板引擎早已解析好缓存在服务端内存中了;

  • 历经数十毫秒之后,终于服务器返回了一个渲染后的HTML,浏览器端解析DOM树,发送请求来加载静态资源;

  • 需要加载的静态资源可能因Cache-Control在浏览器本地磁盘和内存中已经缓存了;

  • 即使本地缓存到期,也可能因Etag没变服务器告诉浏览器304 Not Modified继续缓存;

  • 即使Etag变了,静态资源服务器也因其他用户访问过早已将文件缓存在内存中了;

  • 加载的JS文件会丢到JS引擎执行,其中可能涉及的种种缓存就不再展开了;

  • 整个过程中链条上涉及的「所有的计算机和网络设备」 ,执行的热点代码和数据很可能会载入CPU的多级高速缓存。

这里列举的「仅仅是一部分」 常见的缓存,就有多种多样的形式:从廉价的磁盘到昂贵的CPU高速缓存,最终目的都是用来换取宝贵的时间。

「缓存是“银弹”吗?」

不,Phil Karlton 曾说过:

计算机科学中只有两件困难的事情:缓存失效和命名规范。There are only two hard things in Computer Science: cache invalidation and naming things.

缓存的使用除了带来额外的复杂度以外,还面临如何处理「缓存失效」 的问题。

  • 多线程并发编程需要用各种手段(比如Java中的synchronized volatile)防止并发更新数据,一部分原因就是防止线程「本地缓存的不一致」 ;

  • 缓存失效衍生的问题还有:「缓存穿透、缓存击穿、缓存雪崩」 。解决用不存在的Key来穿透攻击,需要用空值缓存或布隆过滤器;解决单个缓存过期后,瞬间被大量恶意查询击穿的问题需要做查询互斥;解决某个时间点大量缓存同时过期的雪崩问题需要添加随机TTL;

  • 热点数据如果是「多级缓存」,在发生修改时需要清除或修改「各级缓存」 ,这些操作往往不是原子操作,又会涉及各种不一致问题。

除了通常意义上的缓存外,「对象重用的池化技术」,也可以看作是一种「缓存的变体」。常见的诸如JVM,V8这类运行时的「常量池、数据库连接池、HTTP连接池、线程池、Golang的sync.Pool对象池」 等等。在需要某个资源时从现有的池子里直接拿一个,稍作修改或直接用于另外的用途,池化重用也是性能优化常见手段。

04、压缩术

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

说完了两个“空间换时间”的,我们再看一个“「时间换空间」”的办法——「压缩」。压缩的原理「消耗计算的时间,换一种更紧凑的编码方式来表示数据」 。

为什么要拿时间换空间?时间不是最宝贵的资源吗?

举一个视频网站的例子,如果不对视频做任何压缩编码,因为带宽有限,巨大的数据量在网络传输的耗时会比编码压缩的耗时多得多。「对数据的压缩虽然消耗了时间来换取更小的空间存储,但更小的存储空间会在另一个维度带来更大的时间收益」 。

这个例子本质上是:“「操作系统内核与网络设备处理负担 vs 压缩解压的CPU/GPU负担」 ”的权衡和取舍。

我们在代码中通常用的是「无损压缩」 ,比如下面这些场景:

  • HTTP协议中Accept-Encoding添加Gzip/deflate,服务端对接受压缩的文本(JS/CSS/HTML)请求做压缩,大部分图片格式本身已经是压缩的无需压缩;

  • HTTP2协议的头部HPACK压缩;

  • JS/CSS文件的混淆和压缩(Uglify/Minify);

  • 一些RPC协议和消息队列传输的消息中,采用二进制编码和压缩(Gzip、Snappy、LZ4等等);

  • 缓存服务存过大的数据,通常也会事先压缩一下再存,取的时候解压;

  • 一些大文件的存储,或者不常用的历史数据存储,采用更高压缩比的算法存储;

  • JVM的对象指针压缩,JVM在32G以下的堆内存情况下默认开启“UseCompressedOops”,用4个byte就可以表示一个对象的指针,这也是JVM尽量不要把堆内存设置到32G以上的原因;

  • MongoDB的二进制存储的BSON相对于纯文本的JSON也是一种压缩,或者说更紧凑的编码。但更紧凑的编码也意味着更差的可读性,这一点也是需要取舍的。纯文本的JSON比二进制编码要更占存储空间但却是REST API的主流,因为数据交换的场景下的可读性是非常重要的。

「信息论」告诉我们,无损压缩的极限是「信息熵」。进一步减小体积只能以损失部分信息为代价,也就是「有损压缩」 。

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

「那么,有损压缩有哪些应用呢?」

  • 预览和缩略图,低速网络下视频降帧、降清晰度,都是对信息的有损压缩;

  • 音视频等多媒体数据的「采样和编码」大多是有损的,比如MP3是利用傅里叶变换,有损地存储音频文件;jpeg等图片编码也是有损的。虽然有像WAV/PCM这类无损的音频编码方式,但多媒体数据的「采样本身就是有损的」 ,相当于只截取了真实世界的极小一部分数据;

  • 「散列化」 ,比如K-V存储时Key过长,先对Key执行一次“傻”系列(SHA-1、SHA-256)哈希算法变成固定长度的短Key。另外,散列化在文件和数据验证(MD5、CRC、HMAC)场景用的也非常多,无需耗费大量算力对比完整的数据。

除了有损/无损压缩,但还有一个办法,就是「压缩的极端」——从根本上「减少数据或彻底删除」 。

「能减少的就减少」 :

  • JS打包过程“摇树”,去掉没有使用的文件、函数、变量;

  • 开启HTTP/2和高版本的TLS,减少了Round Trip,节省了TCP连接,自带大量性能优化;

  • 减少不必要的信息,比如Cookie的数量,去掉不必要的HTTP请求头;

  • 更新采用增量更新,比如HTTP的PATCH,只传输变化的属性而不是整条数据;

  • 缩短单行日志的长度、缩短URL、在具有可读性情况下用短的属性名等等;

  • 使用位图和位操作,用风骚的「位操作最小化存取的数据」 。典型的例子有:用Redis的位图来记录统计海量用户登录状态;布隆过滤器用位图排除不可能存在的数据;大量开关型的设置的存储等等。

「能删除的就删除」 :

  • 删掉不用的数据;

  • 删掉不用的索引;

  • 删掉不该打的日志;

  • 删掉不必要的通信代码,不去发不必要的HTTP、RPC请求或调用,轮询改发布订阅;

  • 「终极方案:砍掉整个功能」 。

No code is the best way to write secure and reliable applications. Write nothing; deploy nowhere. —— Kelsey Hightower

05、预取术

「预取」通常搭配缓存一起用,其原理是「在缓存空间换时间基础上」更进一步,再加上一次“「时间换时间」”,也就是:「用事先预取的耗时,换取第一次加载的时间」 。当可以猜测出以后的某个时间很有可能会用到某种数据时,把数据预先取到需要用的地方,能大幅度提升用户体验或服务端响应速度。

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

是否用预取模式就像自助餐餐厅与厨师现做的区别,在自助餐餐厅可以直接拿做好的菜品,一般餐厅需要坐下来等菜品现做。那么,预取在哪些实际场景会用呢?

  • 视频或直播类网站,在播放前先缓冲一小段时间,就是预取数据。有的在播放时不仅预取这一条数据,甚至还会预测下一个要看的其他内容,提前把数据取到本地;

  • 「HTTP/2 Server Push」 ,在浏览器请求某个资源时,服务器顺带把其他相关的资源一起推回去,HTML/JS/CSS几乎同时到达浏览器端,相当于浏览器被动预取了资源;

  • 一些客户端软件会用常驻进程的形式,提前预取数据或执行一些代码,这样可以极大提高第一次使用的打开速度;

  • 服务端同样也会用一些预热机制,一方面「热点数据预取到内存提前形成多级缓存」;另一方面也是「对运行环境的预热」 ,载入CPU高速缓存、热点函数JIT编译成机器码等等;

  • 「热点资源提前预分配」到各个实例,比如:秒杀、售票的「库存性质的数据」;分布式「唯一ID」 等等。

天上不会掉馅饼,「预取也是有副作用的」。正如烤箱预热需要消耗时间和额外的电费,在软件代码中做预取/预热的副作用通常是启动慢一些、占用一些闲时的计算资源、可能取到的「不一定是后面需要的」 。

06、削峰填谷术

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

「削峰填谷」的原理也是“「时间换时间」”,「谷时换峰时」。削峰填谷与「预取」 是反过来的:预取是事先花时间做,削峰填谷是事后花时间做。就像三峡大坝可以抗住短期巨量洪水,事后雨停再慢慢开闸防水。软件世界的“削峰填谷”是类似的,只是不是用三峡大坝实现,而是用消息队列、异步化等方式。

常见的有这几类问题,我们分别来看每种对应的解决方案:

  • 针对前端、客户端的「启动优化或首屏优化」:代码和数据等资源的「延时加载、分批加载、后台异步加载、或按需懒加载」 等等。

  • 「背压控制」 - 「限流、节流、去抖」等等。一夫当关,万夫莫开,从「入口处削峰」 ,防止一些恶意重复请求以及请求过于频繁的爬虫,甚至是一些DDoS攻击。简单做法有网关层根据单个IP或用户用漏桶控制请求速率和上限;前端做按钮的节流去抖防止重复点击;网络层开启TCP SYN Cookie防止恶意的SYN洪水攻击等等。彻底杜绝爬虫、黑客手段的恶意洪水攻击是很难的,DDoS这类属于网络安全范畴了。

  • 针对正常的业务请求洪峰,「用消息队列暂存再异步化处理」:常见的后端消息队列「Kafka、RocketMQ」 甚至Redis等等都可以做缓冲层,第一层业务处理直接校验后丢到消息队列中,在洪峰过去后慢慢消费消息队列中的消息,执行具体的业务。另外执行过程中的耗时和耗计算资源的操作,也可以丢到消息队列或数据库中,等到谷时处理。

  • 「捋平毛刺」:有时候洪峰不一定来自外界,如果系统内部大量「定时任务」 在同一时间执行,或与业务高峰期重合,很容易在监控中看到“毛刺”——短时间负载极高。一般解决方案就是错峰执行定时任务,或者分配到其他非核心业务系统中,把“毛刺”摊平。比如很多数据分析型任务都放在业务低谷期去执行,大量定时任务在创建时尽量加一些随机性来分散执行时间。

  • 「避免错误风暴带来的次生洪峰」:有时候网络抖动或短暂宕机,业务会出现各种异常或错误。这时处理不好很容易带来「次生灾害」 ,比如:很多代码都会做错误重试,不加控制的大量重试甚至会导致网络抖动恢复后的瞬间,积压的大量请求再次冲垮整个系统;还有一些代码没有做超时、降级等处理,可能导致大量的等待耗尽TCP连接,进而导致整个系统被冲垮。解决之道就是做限定次数、间隔指数级增长的Back-Off重试,设定超时、降级策略。

07、批量处理术

除了缓存,性能优化还可以这么搞?,java,学习,程序人生

「批量处理」同样可以看成“「时间换时间」”,其原理是「减少了重复的事情,是一种对执行流程的压缩」。以「个别批量操作更长的耗时为代价,在整体上换取了更多的时间」 。

批量处理的应用也非常广泛,我们还是从前端开始讲:

  • 打包合并的JS文件、雪碧图等等,将「一批资源」集中到一起,「一次性传输」 ;

  • 前端动画使用requestAnimationFrame在UI渲染时「批量处理积压的变化」 ,而不是有变化立刻更新,在游戏开发中也有类似的应用;

  • 前后端中使用「队列暂存临时产生的数据」 ,积压到一定数量再批量处理;

  • 在不影响可扩展性情况下,「一个接口传输多种需要的数据」,减少大量ajax调用(「GraphQL」 在这一点就做到了极致);

  • 「系统间通信尽量发送整批数据」,比如「消息队列的发布订阅、存取缓存服务的数据、RPC调用、插入或更新数据库」 等等,能批量做尽可能批量做,因为这些系统间通信的I/O时间开销已经很昂贵了;

  • 「数据积压到一定程度再落盘」 ,操作系统本身的写文件就是这么做的,Linux的fwrite只是写入缓冲区暂存,积压到一定程度再fsync刷盘。在应用层,很多高性能的数据库和K-V存储的实现都体现了这一点:一些NoSQL的LSM Tree的第一层就是在内存中先积压到一定大小再往下层合并;Redis的RDB结合AOF的落盘机制;Linux系统调用也提供了批量读写多个缓冲区文件的系统调用:readv/writev;

  • 「延迟地批量回收资源」 ,比如JVM的Survivor Space的S0和S1区互换、Redis的Key过期的清除策略。

批量处理如此好用,那么问题来了,「每一批放多大最合适呢」 ?

这个问题其实没有定论,有一些个人经验可以分享。

  • 前端把所有文件打包成单个JS,大部分时候并不是最优解。Webpack提供了很多分块的机制,CSS和JS分开、JS按业务分更小的Chunk结合懒加载、一些体积大又不用在首屏用的第三方库设置external或单独分块,可能整体性能更高。不一定要一批搞定所有事情,分几个小批次反而用户体验的性能更好。

  • Redis的「MGET、MSET」来批量存取数据时,每批大小「不宜过大」 ,因为Redis主线程只有一个,如果一批太大执行期间会让其他命令无法响应。经验上一批50-100个Key性能是不错的,但最好在真实环境下用真实大小的数据量化度量一下,做Benchmark测试才能确定一批大小的最优值。

  • MySQL、Oracle这类RDBMS,最优的批量Insert的大小也视数据行的特性而定。我之前在2U8G的Oracle上用一些普遍的业务数据做过测试,批量插入时每批5000-10000条数据性能是最高的,每批过大会导致DML的解析耗时过长,甚至单个SQL语句体积超限,单批太多反而得不偿失。

  • 消息队列的发布订阅,每批的消息长度尽量控制在1MB以内,有些云服务商提供的消息队列限制了最大长度,那这个长度可能就是「性能拐点」 ,比如AWS的SQS服务对单条消息的限制是256KB。

总之,多大一批可以确保单批响应时间不太长的同时让整体性能最高,是需要在实际情况下做基准测试的,不能一概而论。而批量处理的「副作用」 在于:处理逻辑会更加复杂,尤其是一些涉及事务、并发的问题;需要用数组或队列用来存放缓冲一批数据,消耗了额外的存储空间。

08、小结

上半部分先聊到这里,大都是“时间”与“空间”的取舍之术,这些思路在很多地方甚至是非软件领域都是「普适」 的。下半部分我们再聊一些不完全普适、稍微进阶一点的性能优化的技术路线。文章来源地址https://www.toymoban.com/news/detail-768160.html

到了这里,关于除了缓存,性能优化还可以这么搞?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 原来可以这么玩?Jmeter接口性能测试工具玩转测试Redis数据库(超详细)

    Jmeter作为当前非常受欢迎的接口测试和性能测试的工具,在企业中得到非常广泛的使用,而redis作为缓存数据库,也在企业中得到普遍使用,那如何使用jmeter来测试redis数据库呢? 今天我们就来讲一讲怎么使用jmeter来调用redis。 Jmeter准备 由于jmeter本身并没有带有redis的测试入

    2024年02月13日
    浏览(60)
  • Java原来可以这么玩!CV实现多张图片生成视频

    比如我像将几张图片变成一个视频的形式发不到短视频平台,虽然短视频平台也有上传图片变成视频的功能,但是我想要具体控制每张图片显示多久后切换到下一个图片,短视频平台目前无法实现,于是乎,我用java代码实现了这个功能。 生成视频展示 多张图片生成视频 Ja

    2024年01月17日
    浏览(49)
  • web 性能优化详解(Lighthouse工具、优化方式、强缓存和协商缓存、代码优化、算法优化)

    优化性能概念宽泛,可以从信号、系统、计算机原理、操作系统、网络通信、DNS解析、负载均衡、页面渲染。只要结合一个实际例子讲述清楚即可。 Web 性能是客观的衡量标准,是用户对加载时间和运行时的直观体验。 Web 性能指页面加载到可交互和可响应所消耗的时间,以

    2024年02月07日
    浏览(45)
  • Java原来可以这么玩!CV视频合成处理,视频前后拼接,画面合并

    本章内容教会你如何用java代码实现 两个视频的画面合并 或者前后拼接。原理是使用了javacv开源jar包,代码经过反复修改,已经实现我能想到的最优最快的实现,如果你有更好更快的实现,欢迎评论区留言!!! 先展示一下效果吧!!! 两个视频前后拼接 两个视频画面合并

    2024年01月21日
    浏览(46)
  • 前端性能优化:缓存

    在快节奏的互联网时代,网站的加载速度直接影响用户体验和业务成功。而缓存作为性能优化的重要手段,可以大幅提升网页加载速度,减少服务器负担。本文将为你详解缓存的使用,帮助你优化前端性能,为用户呈现更快速、流畅的体验。 1. 什么是缓存? 缓存是指将已获

    2024年02月13日
    浏览(37)
  • 性能优化之-缓存篇

    什么是缓存?在哪可以看到?为什么要做? 百度百科可以解释,包含各种方向(缓存) 最常见就是目前看到这篇文章的位置-浏览器,打开F12, 中文汉字很好找;包括硬盘,服务器这些都是它的容器, 缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。 本文简单

    2024年02月13日
    浏览(43)
  • Android性能优化—ViewPagers + Fragment缓存优化

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

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

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

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

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

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

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

    2024年02月08日
    浏览(43)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包