自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

性能優(yōu)化2.0,新增緩存后,程序的秒開率不升反降

數(shù)據(jù)庫(kù) 其他數(shù)據(jù)庫(kù)
緩存就是把訪問量較高的熱點(diǎn)數(shù)據(jù)從傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)中加載到內(nèi)存中,當(dāng)用戶再次訪問熱點(diǎn)數(shù)據(jù)時(shí),是從內(nèi)存中加載,減少了對(duì)數(shù)據(jù)庫(kù)的訪問量,解決了高并發(fā)場(chǎng)景下容易造成數(shù)據(jù)庫(kù)宕機(jī)的問題。

一、前情提要

在上一篇文章中提到,有一個(gè)頁(yè)面加載速度很慢,是通過緩沖流優(yōu)化的。

查詢的時(shí)候,會(huì)訪問后臺(tái)數(shù)據(jù)庫(kù),查詢前20條數(shù)據(jù),按道理來說,這應(yīng)該很快才對(duì)。

追蹤代碼,看看啥問題,最后發(fā)現(xiàn)問題有三:

  • 表中有一個(gè)BLOB大字段,存儲(chǔ)著一個(gè)PDF模板,也就是上圖中的運(yùn)費(fèi)模板。
  • 查詢后會(huì)將這個(gè)PDF模板存儲(chǔ)到本地磁盤。
  • 點(diǎn)擊線上顯示,會(huì)讀取本地的PDF模板,通過socket傳到服務(wù)器。

大字段批量查詢、批量文件落地、讀取大文件并進(jìn)行網(wǎng)絡(luò)傳輸,不慢才怪,這一頓騷操作,5秒能加載完畢,已經(jīng)燒高香了。

經(jīng)過4次優(yōu)化,將頁(yè)面的加載時(shí)間控制在了1秒以內(nèi),實(shí)打?qū)嵉奶嵘顺绦虻拿腴_率。

  • 批量查詢時(shí),不查詢BLOB大字段。
  • 點(diǎn)擊運(yùn)費(fèi)查詢時(shí),單獨(dú)查詢+觸發(fā)索引,實(shí)現(xiàn)“懶加載”。
  • 異步存儲(chǔ)文件。
  • 通過 緩沖流 -> 內(nèi)存映射技術(shù)mmap -> sendFile零拷貝 讀取本地文件。

有一個(gè)小伙伴在評(píng)論中提到,還可以通過緩存繼續(xù)優(yōu)化,確實(shí)是可以的,緩存也是復(fù)用優(yōu)化的一種。

為了提高頁(yè)面的加載速度,使用了單條查詢 + 觸發(fā)索引,提高數(shù)據(jù)庫(kù)查詢速度。

歸根結(jié)底,還是查詢了數(shù)據(jù)庫(kù),如果不查呢,訪問速度肯定會(huì)更快。

這個(gè)時(shí)候,就用到了緩存,將運(yùn)費(fèi)模板存到緩存中。

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

緩存就是把訪問量較高的熱點(diǎn)數(shù)據(jù)從傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)中加載到內(nèi)存中,當(dāng)用戶再次訪問熱點(diǎn)數(shù)據(jù)時(shí),是從內(nèi)存中加載,減少了對(duì)數(shù)據(jù)庫(kù)的訪問量,解決了高并發(fā)場(chǎng)景下容易造成數(shù)據(jù)庫(kù)宕機(jī)的問題。

我理解的緩存的本質(zhì)就是一個(gè)用空間換時(shí)間的一個(gè)思想。

提供“緩存”的目的是為了讓數(shù)據(jù)訪問的速度適應(yīng)CPU的處理速度,其基于的原理是內(nèi)存中“局部性原理”。

CPU 緩存的是內(nèi)存數(shù)據(jù),用于解決 CPU 處理速度和內(nèi)存不匹配的問題,比如處理器和內(nèi)存之間的高速緩存,操作系統(tǒng)在內(nèi)存管理上,針對(duì)虛擬內(nèi)存 為頁(yè)表項(xiàng)使用了一特殊的高速緩存TLB轉(zhuǎn)換檢測(cè)緩沖區(qū),因?yàn)槊總€(gè)虛擬內(nèi)存訪問會(huì)引起兩次物理訪問,一次取相關(guān)的頁(yè)表項(xiàng),一次取數(shù)據(jù),TLB引入來加速虛擬地址到物理地址的轉(zhuǎn)換。

1、緩存有哪些分類

  • 操作系統(tǒng)磁盤緩存,減少磁盤機(jī)械操作
  • 數(shù)據(jù)庫(kù)緩存,減少文件系統(tǒng) I/O
  • 應(yīng)用程序緩存,減少對(duì)數(shù)據(jù)庫(kù)的查詢
  • Web 服務(wù)器緩存,減少應(yīng)用程序服務(wù)器請(qǐng)求
  • 客戶端瀏覽器緩存,減少對(duì)網(wǎng)站的訪問

2、本地緩存與分布式緩存

本地緩存:在客戶端本地的物理內(nèi)存中劃出一部分空間,來緩存客戶端回寫到服務(wù)器的數(shù)據(jù)。當(dāng)本地回寫緩存達(dá)到緩存閾值時(shí),將數(shù)據(jù)寫入到服務(wù)器中。

本地緩存是指程序級(jí)別的緩存組件,它的特點(diǎn)是本地緩存和應(yīng)用程序會(huì)運(yùn)行在同一個(gè)進(jìn)程中,所以本地緩存的操作會(huì)非???,因?yàn)樵谕粋€(gè)進(jìn)程內(nèi)也意味著不會(huì)有網(wǎng)絡(luò)上的延遲和開銷。

本地緩存適用于單節(jié)點(diǎn)非集群的應(yīng)用場(chǎng)景,它的優(yōu)點(diǎn)是快,缺點(diǎn)是多程序無法共享緩存。

無法共享緩存可能會(huì)造成系統(tǒng)資源的浪費(fèi),每個(gè)系統(tǒng)都單獨(dú)維護(hù)了一份屬于自己的緩存,而同一份緩存有可能被多個(gè)系統(tǒng)單獨(dú)進(jìn)行存儲(chǔ),從而浪費(fèi)了系統(tǒng)資源。

分布式緩存是指將應(yīng)用系統(tǒng)和緩存組件進(jìn)行分離的緩存機(jī)制,這樣多個(gè)應(yīng)用系統(tǒng)就可以共享一套緩存數(shù)據(jù)了,它的特點(diǎn)是共享緩存服務(wù)和可集群部署,為緩存系統(tǒng)提供了高可用的運(yùn)行環(huán)境,以及緩存共享的程序運(yùn)行機(jī)制。

下面介紹一個(gè)小編最常用的本地緩存 Guava Cache。

三、Guava Cache本地緩存

1、Google Guava

Google Guava是一個(gè)Java編程庫(kù),其中包含了許多高質(zhì)量的工具類和方法。其中,Guava的緩存工具之一是LoadingCache。LoadingCache是一個(gè)帶有自動(dòng)加載功能的緩存,可以自動(dòng)加載緩存中不存在的數(shù)據(jù)。其實(shí)質(zhì)是一個(gè)鍵值對(duì)(Key-Value Pair)的緩存,可以使用鍵來獲取相應(yīng)的值。

Guava Cache 的架構(gòu)設(shè)計(jì)靈感來源于 ConcurrentHashMap,它使用了多個(gè) segments 方式的細(xì)粒度鎖,在保證線程安全的同時(shí),支持了高并發(fā)的使用場(chǎng)景。Guava Cache 類似于 Map 集合的方式對(duì)鍵值對(duì)進(jìn)行操作,只不過多了過期淘汰等處理邏輯。

Guava Cache對(duì)比ConcurrentHashMap優(yōu)勢(shì)在哪?

  • Guava Cache可以設(shè)置過期時(shí)間,提供數(shù)據(jù)過多時(shí)的淘汰機(jī)制。
  • 線程安全,支持并發(fā)讀寫。
  • 在緩存擊穿時(shí),GuavaCache 可以使用 CacheLoader 的load 方法控制,對(duì)同一個(gè)key,只允許一個(gè)請(qǐng)求去讀源并回填緩存,其他請(qǐng)求阻塞等待。

2、Loadingcache數(shù)據(jù)結(jié)構(gòu)

  • Loadingcache含有多個(gè)Segment,每一個(gè)Segment中有若干個(gè)有效隊(duì)列。
  • 多個(gè)Segment之間互不打擾,可以并發(fā)執(zhí)行。
  • 各個(gè)Segment的擴(kuò)容只需要擴(kuò)自己,與其它的Segment無關(guān)。
  • 設(shè)置合適的初始化容量與并發(fā)水平參數(shù),可以有效避免擴(kuò)容,但是設(shè)置的太大了,耗費(fèi)內(nèi)存,設(shè)置的太小,緩存價(jià)值降低,需要根據(jù)業(yè)務(wù)需求進(jìn)行權(quán)衡。
  • Loadingcache數(shù)據(jù)結(jié)構(gòu)和ConcurrentHashMap很相似,ReferenceEntry用于存放key-value。
  • 每一個(gè)ReferenceEntry都會(huì)存放一個(gè)雙向鏈表,采用的是Entry替換的方式。
  • 每次訪問某個(gè)元素就將元素移動(dòng)到鏈表頭部,這樣鏈表尾部的元素就是最近最少使用的元素,替換的復(fù)雜度為o(1),但是訪問的復(fù)雜度還是O(n)。
  • 隊(duì)列用于實(shí)現(xiàn)LRU緩存回收算法。

3、Loadingcache數(shù)據(jù)結(jié)構(gòu)構(gòu)建流程:

  • 初始化CacheBuilder,指定參數(shù)(并發(fā)級(jí)別、過期時(shí)間、初始容量、緩存最大容量),使用build()方法創(chuàng)建LocalCache實(shí)例。
  • 創(chuàng)建Segment數(shù)組,初始化每一個(gè)Segment。
  • 為Segment屬性賦值。
  • 初始化Segment中的table,即一個(gè)ReferenceEntry數(shù)組(每一個(gè)key-value就是一個(gè)ReferenceEntry)。
  1. 根據(jù)之前類變量的賦值情況,創(chuàng)建相應(yīng)隊(duì)列,用于LRU緩存回收算法。

4、判斷緩存是否過期

  • expireAfterWrite,在put時(shí)更新緩存時(shí)間戳,在get時(shí)如果發(fā)現(xiàn)當(dāng)前時(shí)間與時(shí)間戳的差值大于過期時(shí)間戳,就會(huì)進(jìn)行l(wèi)oad操作。
  • expireAfterAccess,在expireAfterWrite的基礎(chǔ)上,不管是寫還是讀都會(huì)記錄新的時(shí)間戳。
  1. refreshAfterWrite,調(diào)用get進(jìn)行值的獲取的時(shí)候才會(huì)執(zhí)行reload操作,這里的刷新操作可以通過異步調(diào)用load實(shí)現(xiàn)。

5、Loadingcache如何解決緩存穿透

緩存穿透是指在Loadingcache緩存中,由于某些原因,緩存的數(shù)據(jù)無法被正常訪問或處理,導(dǎo)致緩存失去了它的作用。

發(fā)生緩存穿透的原因有很多,比如數(shù)據(jù)量過大、數(shù)據(jù)更新頻繁、數(shù)據(jù)過期、數(shù)據(jù)權(quán)限限制、緩存性能瓶頸等原因,這里不過多糾結(jié)。

(1)expireAfterAcess和expireAfterWrite同步加載

設(shè)置為expireAfterAcess和expireAfterWrite時(shí),在進(jìn)行g(shù)et的過程中,緩存失效的話,會(huì)進(jìn)行l(wèi)oad操作,load是一個(gè)同步加載的操作,如下圖:

如果發(fā)生了緩存穿透,當(dāng)有大量并發(fā)請(qǐng)求訪問緩存時(shí),會(huì)有一個(gè)線程去同步查詢DB,隨即通過reeatrantLock進(jìn)入loading等待狀態(tài),其它請(qǐng)求相同key的線程,一部分在waitforvalue,另一部分在reentantloack的阻塞隊(duì)列中,等待同步查詢完畢,所有請(qǐng)求都會(huì)獲得最新值。

(2)refreshAfterWrite同步加載

如果采用refresh的話,會(huì)通過scheduleRefresh方法進(jìn)行l(wèi)oad,也是一個(gè)線程同步獲取DB。

其它線程不會(huì)阻塞,性能比expireAfterWrite同步加載高,但是,可能返回新值、也可能返回舊值。

(3)refreshAfterWrite異步加載

當(dāng)加載緩存的線程是異步加載的話,對(duì)于請(qǐng)求1,如果在異步結(jié)束前返回,就會(huì)返回舊值,反之是新值。

對(duì)于其他線程來說,不會(huì)被阻塞,直接返回,返回值可能是新值或者是舊值。

Loadingcache沒使用額外的線程去做定時(shí)清理和加載的功能,而是依賴于get()請(qǐng)求。

在查詢的時(shí)候,進(jìn)行時(shí)間對(duì)比,如果使用refreshAfterWrite,在長(zhǎng)時(shí)間沒有查詢時(shí),查詢有可能會(huì)得到一個(gè)舊值,我們可以通過設(shè)置refreshAfterWrite(寫刷新,在get時(shí)可以同步或異步緩存的時(shí)間戳)為5s,將expireAfterWrite(寫過期,在put時(shí)更新緩存的時(shí)間戳)設(shè)為10s,當(dāng)訪問頻繁的時(shí)候,會(huì)在每5秒都進(jìn)行refresh,而當(dāng)超過10s沒有訪問,下一次訪問必須load新值。

四、Redis中如何解決緩存穿透

如果發(fā)生了緩存穿透,可以針對(duì)要查詢的數(shù)據(jù),在Redis中插入一條數(shù)據(jù),添加一個(gè)約定好的默認(rèn)值,比如defaultNull。

比如你想通過某個(gè)id查詢某某訂單,Redis中沒有,MySQL中也沒有,此時(shí),就可以在Redis中插入一條,存為defaultNull,下次再查詢就有了,因?yàn)槭翘崆凹s定好的,前端也明白是啥意思,一切OK,歲月靜好。

五、使用loadingCache優(yōu)化頁(yè)面加載

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()
            // 并發(fā)級(jí)別設(shè)置為 10,是指可以同時(shí)寫緩存的線程數(shù)
            .concurrencyLevel(10)
            // 寫刷新,在get時(shí)可以同步或異步緩存的時(shí)間戳
            .refreshAfterWrite(5, TimeUnit.SECONDS)
            // 寫過期,在put時(shí)更新緩存的時(shí)間戳
            .expireAfterWrite(10, TimeUnit.SECONDS)
            // 設(shè)置緩存容器的初始容量為 10
            .initialCapacity(10)
            // 設(shè)置緩存最大容量為 100,超過之后就會(huì)按照 LRU 算法移除緩存項(xiàng)
            .maximumSize(100)
            // 設(shè)置要統(tǒng)計(jì)緩存的命中率
            .recordStats()
            // 指定 CacheLoader,緩存不存在時(shí),可自動(dòng)加載緩存
            .build(new CacheLoader<String, String>() {
                        @Override
                        public String load(String key) throws Exception {
                            // 自動(dòng)加載緩存的業(yè)務(wù)
                            return "error";
                        }
                    }
            );
}

3、優(yōu)化5:通過LoadingCache緩存模板數(shù)據(jù),在編輯模板后,更新緩存

查詢模板信息后,通過loadCache.put(uuid, pdf);加載到內(nèi)存中,在編輯模板時(shí),更新緩存過期時(shí)間,下次獲取模板PDF時(shí),直接從LoadingCache緩存中取,降低數(shù)據(jù)庫(kù)訪問壓力,perfect?。?!

這種情況是不適合緩存的,因?yàn)槟0錺df本來就是一個(gè)BLOB大數(shù)據(jù),你把它放內(nèi)存里緩存了,你告訴我,能放幾個(gè)??jī)?nèi)存扛得住嗎?

責(zé)任編輯:姜華 來源: 哪吒編程
相關(guān)推薦

2024-10-22 18:44:48

2024-02-01 15:01:26

AI訓(xùn)練

2024-01-03 14:41:30

iOS 17蘋果

2024-01-11 08:36:47

懶加載零拷貝前端

2014-11-24 09:23:22

華為數(shù)據(jù)中心

2024-01-08 09:43:20

2022-12-28 18:32:48

前端性能優(yōu)化

2011-09-08 11:02:39

Web2.0網(wǎng)康

2020-02-06 19:58:03

LinuxWindows 7Windows 10

2025-01-17 13:20:00

2017-08-16 10:57:25

H5HTML開發(fā)

2013-03-12 10:15:08

2018-06-20 15:42:09

2025-02-04 10:58:16

2021-06-10 10:02:19

優(yōu)化緩存性能

2019-07-12 15:28:41

緩存數(shù)據(jù)庫(kù)瀏覽器

2021-08-26 22:26:55

性能優(yōu)化技術(shù)

2019-03-14 15:38:19

ReactJavascript前端

2014-06-05 10:22:06

Tomcat 7

2010-11-15 16:20:33

Oracle系統(tǒng)優(yōu)化
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)