中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java高并發系統設計之緩存案例

發布時間:2020-10-12 13:41:06 來源:億速云 閱讀:327 作者:小新 欄目:編程語言

java高并發系統設計之緩存案例?這個問題可能是我們日常學習或工作經常見到的。希望通過這個問題能讓你收獲頗深。下面是小編給大家帶來的參考內容,讓我們一起來看看吧!

常見硬件組件的延時情況如下圖:java高并發系統設計之緩存案例

從這些數據中,你可以看到,做一次內存尋址大概需要 100ns,而做一次磁盤的查找則需要 10ms。可見,我們使用內存作為緩存的存儲介質相比于以磁盤作為主要存儲介質的數據庫來說,性能上會提高多個數量級。所以,內存是最常見的一種緩存數據的介質。

一、緩存案例

1、TLB

Linux 內存管理是通過一個叫做 MMU(Memory Management Unit)的硬件,來實現從虛擬地址到物理地址的轉換的,但是如果每次轉換都要做這么復雜計算的話,無疑會造成性能的損耗,所以我們會借助一個叫做 TLB(Translation Lookaside Buffer)的組件來緩存最近轉換過的虛擬地址,和物理地址的映射。TLB 就是一種緩存組件。

2、抖音

平臺上的短視頻實際上是使用內置的網絡播放器來完成的。網絡播放器接收的是數據流,將數據下載下來之后經過分離音視頻流,解碼等流程后輸出到外設設備上播放。播放器中通常會設計一些緩存的組件,在未打開視頻時緩存一部分視頻數據,比如我們打開抖音,服務端可能一次會返回三個視頻信息,我們在播放第一個視頻的時候,播放器已經幫我們緩存了第二、三個視頻的部分數據,這樣在看第二個視頻的時候就可以給用戶“秒開”的感覺。

3、HTTP協議緩存

當我們第一次請求靜態的資源時,比如一張圖片,服務端除了返回圖片信息,在響應頭里面還有一個“Etag”的字段。瀏覽器會緩存圖片信息以及這個字段的值。當下一次再請求這個圖片的時候,瀏覽器發起的請求頭里面會有一個“If-None-Match”的字段,并且把緩存的“Etag”的值寫進去發給服務端。服務端比對圖片信息是否有變化,如果沒有,則返回瀏覽器一個 304 的狀態碼,瀏覽器會繼續使用緩存的圖片信息。通過這種緩存協商的方式,可以減少網絡傳輸的數據大小,從而提升頁面展示性能。java高并發系統設計之緩存案例

二、緩存分類

1、靜態緩存

靜態緩存在 Web 1.0 時期是非常著名的,它一般通過生成 Velocity 模板或者靜態 HTML 文件來實現靜態緩存,在 Nginx 上部署靜態緩存可以減少對于后臺應用服務器的壓力

2、分布式緩存

分布式緩存的大名可謂是如雷貫耳了,我們平時耳熟能詳的 Memcached、Redis 就是分布式緩存的典型例子。它們性能強勁,通過一些分布式的方案組成集群可以突破單機的限制。所以在整體架構中,分布式緩存承擔著非常重要的角色

3、本地緩存

Guava Cache 或者是 Ehcache 等,它們和應用程序部署在同一個進程中,優勢是不需要跨網絡調度,速度極快,所以可以用來阻擋短時間內的熱點查詢。

三、緩存的讀寫策略

1、Cache Aside策略

在更新數據時不更新緩存,而是刪除緩存中的數據,在讀取數據時,發現緩存中沒了數據之后,再從數據庫中讀取數據,更新到緩存中。java高并發系統設計之緩存案例

這個策略就是我們使用緩存最常見的策略,Cache Aside 策略(也叫旁路緩存策略),這個策略數據以數據庫中的數據為準,緩存中的數據是按需加載的。

Cache Aside 策略是我們日常開發中最經常使用的緩存策略,不過我們在使用時也要學會依情況而變,并不是一成不變的。Cache Aside 存在的最大的問題是當寫入比較頻繁時,緩存中的數據會被頻繁地清理,這樣會對緩存的命中率有一些影響。如果你的業務對緩存命中率有嚴格的要求,那么可以考慮兩種解決方案:

一種做法是在更新數據時也更新緩存,只是在更新緩存前先加一個分布式鎖,因為這樣在同一時間只允許一個線程更新緩存,就不會產生并發問題了。當然這么做對于寫入的性能會有一些影響(推薦);

另一種做法同樣也是在更新數據時更新緩存,只是給緩存加一個較短的過期時間,這樣即使出現緩存不一致的情況,緩存的數據也會很快過期,對業務的影響也是可以接受。

2、Read/Write Through

這個策略的核心原則是用戶只與緩存打交道,由緩存和數據庫通信,寫入或者讀取數據。java高并發系統設計之緩存案例

Write Through

的策略是這樣的:先查詢要寫入的數據在緩存中是否已經存在,如果已經存在,則更新緩存中的數據,并且由緩存組件同步更新到數據庫中,如果緩存中數據不存在,我們把這種情況叫做“Write Miss(寫失效)”。一般來說,我們可以選擇兩種“Write Miss”方式:一個是“Write Allocate(按寫分配)”,做法是寫入緩存相應位置,再由緩存組件同步更新到數據庫中;另一個是“No-write allocate(不按寫分配)”,做法是不寫入緩存中,而是直接更新到數據庫中。 我們看到 Write Through 策略中寫數據庫是同步的,這對于性能來說會有比較大的影響,因為相比于寫緩存,同步寫數據庫的延遲就要高很多了。通過Write Back策略異步的更新數據庫。

Read Through

策略就簡單一些,它的步驟是這樣的:先查詢緩存中數據是否存在,如果存在則直接返回,如果不存在,則由緩存組件負責從數據庫中同步加載數據。

3、Write Back

這個策略的核心思想是在寫入數據時只寫入緩存,并且把緩存塊兒標記為“臟”的。而臟塊兒只有被再次使用時才會將其中的數據寫入到后端存儲中。 在“Write Miss”的情況下,我們采用的是“Write Allocate”的方式,也就是在寫入后端存儲的同時要寫入緩存,這樣我們在之后的寫請求中都只需要更新緩存即可,而無需更新后端存儲了。注意與上面的write through策略作區分。java高并發系統設計之緩存案例

我們在讀取緩存時如果發現緩存命中則直接返回緩存數據。如果緩存不命中則尋找一個可用的緩存塊兒,如果這個緩存塊兒是“臟”的,就把緩存塊兒中之前的數據寫入到后端存儲中,并且從后端存儲加載數據到緩存塊兒,如果不是臟的,則由緩存組件將后端存儲中的數據加載到緩存中,最后我們將緩存設置為不是臟的,返回數據就好了。java高并發系統設計之緩存案例

write back策略多用于向磁盤中寫數據。例如:操作系統層面的 Page Cache、日志的異步刷盤、消息隊列中消息的異步寫入磁盤等。因為這個策略在性能上的優勢毋庸置疑,它避免了直接寫磁盤造成的隨機寫問題,畢竟寫內存和寫磁盤的隨機 I/O 的延遲相差了幾個數量級呢。

四、緩存高可用

緩存的命中率是緩存需要監控的數據指標,緩存的高可用可以一定程度上減少緩存穿透的概率,提升系統的穩定性。緩存的高可用方案主要包括客戶端方案、中間代理層方案和服務端方案三大類:

1、客戶端方案

在客戶端方案中,你需要關注緩存的寫和讀兩個方面: 寫入數據時,需要把被寫入緩存的數據分散到多個節點中,即進行數據分片; 讀數據時,可以利用多組的緩存來做容錯,提升緩存系統的可用性。關于讀數據,這里可以使用主從和多副本兩種策略,兩種策略是為了解決不同的問題而提出的。 具體的實現細節包括:數據分片、主從、多副本

數據分片

一致性Hash算法。在這個算法中,我們將整個 Hash 值空間組織成一個虛擬的圓環,然后將緩存節點的 IP 地址或者主機名做 Hash 取值后,放置在這個圓環上。當我們需要確定某一個 Key 需要存取到哪個節點上的時候,先對這個 Key 做同樣的 Hash 取值,確定在環上的位置,然后按照順時針方向在環上“行走”,遇到的第一個緩存節點就是要訪問的節點。java高并發系統設計之緩存案例

這時如果在 Node 1 和 Node 2 之間增加一個 Node 5,你可以看到原本命中 Node 2 的 Key 3 現在命中到 Node 5,而其它的 Key 都沒有變化;同樣的道理,如果我們把 Node 3 從集群中移除,那么只會影響到 Key 5 。所以你看,在增加和刪除節點時,只有少量的 Key 會“漂移”到其它節點上,而大部分的 Key 命中的節點還是會保持不變,從而可以保證命中率不會大幅下降。 【提示】一致性hash出現的緩存雪崩現象使用虛擬節點解決。一致性hash分片與hash分片的區別在于,緩存命中率的問題,hash分片在存在機器加入或是減少的情況時候,會導致緩存失效,緩存命中率下降。

主從

Redis 本身支持主從的部署方式,但是 Memcached 并不支持,Memcached 的主從機制是如何在客戶端實現的。為每一組 Master 配置一組 Slave,更新數據時主從同步更新。讀取時,優先從 Slave 中讀數據,如果讀取不到數據就穿透到 Master 讀取,并且將數據回種到 Slave 中以保持 Slave 數據的熱度。主從機制最大的優點就是當某一個 Slave 宕機時,還會有 Master 作為兜底,不會有大量請求穿透到數據庫的情況發生,提升了緩存系統的高可用性。

多副本

主從方式已經能夠解決大部分場景的問題,但是對于極端流量的場景下,一組 Slave 通常來說并不能完全承擔所有流量,Slave 網卡帶寬可能成為瓶頸。為了解決這個問題,我們考慮在 Master/Slave 之前增加一層副本層,整體架構是這樣的:java高并發系統設計之緩存案例

這個方案中,當客戶端發起查詢請求時,請求首先會先從多個副本組中選取一個副本組發起查詢,如果查詢失敗,就繼續查詢 Master/Slave,并且將查詢的結果回種到所有副本組中,避免副本組中臟數據的存在。基于成本的考慮,每一個副本組容量比 Master 和 Slave 要小,因此它只存儲了更加熱的數據。在這套架構中,Master 和 Slave 的請求量會大大減少,為了保證它們存儲數據的熱度,在實踐中我們會把 Master 和 Slave 作為一組副本組使用。

2、中間代理層

業界也有很多中間代理層方案,比如 Facebook 的Mcrouter,Twitter 的Twemproxy,豌豆莢的Codis。它們的原理基本上可以由一張圖來概括:java高并發系統設計之緩存案例

3、服務端方案

Redis 在 2.4 版本中提出了 Redis Sentinel 模式來解決主從 Redis 部署時的高可用問題,它可以在主節點掛了以后自動將從節點提升為主節點,保證整體集群的可用性,整體的架構如下圖所示:java高并發系統設計之緩存案例

redis Sentinel 也是集群部署的,這樣可以避免 Sentinel 節點掛掉造成無法自動故障恢復的問題,每一個 Sentinel 節點都是無狀態的。在 Sentinel 中會配置 Master 的地址,Sentinel 會時刻監控 Master 的狀態,當發現 Master 在配置的時間間隔內無響應,就認為 Master 已經掛了,Sentinel 會從從節點中選取一個提升為主節點,并且把所有其他的從節點作為新主的從節點。Sentinel 集群內部在仲裁的時候,會根據配置的值來決定當有幾個 Sentinel 節點認為主掛掉可以做主從切換的操作,也就是集群內部需要對緩存節點的狀態達成一致才行。

【提示】上述客戶端到sentinel集群的連線是虛線,因為對于緩存的寫入和讀取請求不會經過 Sentinel 節點。

五、緩存穿透

1、帕累托

互聯網系統的數據訪問模型一般會遵從“80/20 原則”。“80/20 原則”又稱為帕累托法則,是意大利經濟學家帕累托提出的一個經濟學的理論。簡單來說,它是指在一組事物中,最重要的部分通常只占 20%,而其他的 80% 并沒有那么重要。把它應用到數據訪問的領域,就是我們會經常訪問 20% 的熱點數據,而另外的 80% 的數據則不會被經常訪問。既然緩存的容量有限,并且大部分的訪問只會請求 20% 的熱點數據,那么理論上說,我們只需要在有限的緩存空間里存儲 20% 的熱點數據就可以有效地保護脆弱的后端系統了,也就可以放棄緩存另外 80% 的非熱點數據了。所以這種少量的緩存穿透是不可避免的,但是對系統是沒有損害的。

2、回種空值

當我們從數據庫中查詢到空值或者發生異常時,我們可以向緩存中回種一個空值。但是因為空值并不是準確的業務數據,并且會占用緩存的空間,所以我們會給這個空值加一個比較短的過期時間,讓空值在短時間之內能夠快速過期淘汰。回種空值雖然能夠阻擋大量穿透的請求,但如果有大量的空值緩存,也就會浪費緩存的存儲空間,如果緩存空間被占滿了,還會剔除掉一些已經被緩存的用戶信息反而會造成緩存命中率的下降。所以這個方案,我建議你在使用的時候應該評估一下緩存容量是否能夠支撐。如果需要大量的緩存節點來支持,那么就無法通過通過回種空值的方式來解決,這時你可以考慮使用布隆過濾器。

3、布隆過濾器

1970 年布隆提出了一種布隆過濾器的算法,用來判斷一個元素是否在一個集合中。這種算法由一個二進制數組和一個 Hash 算法組成。它的基本思路如下:我們把集合中的每一個值按照提供的 Hash 算法算出對應的 Hash 值,然后將 Hash 值對數組長度取模后得到需要計入數組的索引值,并且將數組這個位置的值從 0 改成 1。在判斷一個元素是否存在于這個集合中時,你只需要將這個元素按照相同的算法計算出索引值,如果這個位置的值為 1 就認為這個元素在集合中,否則則認為不在集合中。java高并發系統設計之緩存案例

如何使用布隆過濾器解決緩存穿透呢?

以存儲用戶信息的表為例進行講解。首先我們初始化一個很大的數組,比方說長度為 20 億的數組,接下來我們選擇一個 Hash 算法,然后我們將目前現有的所有用戶的 ID 計算出 Hash 值并且映射到這個大數組中,映射位置的值設置為 1,其它值設置為 0。新注冊的用戶除了需要寫入到數據庫中之外,它也需要依照同樣的算法更新布隆過濾器的數組中相應位置的值。那么當我們需要查詢某一個用戶的信息時,先查詢這個 ID 在布隆過濾器中是否存在,如果不存在就直接返回空值,而不需要繼續查詢數據庫和緩存,這樣就可以極大地減少異常查詢帶來的緩存穿透。java高并發系統設計之緩存案例

布隆過濾器優點:

(1)性能高。無論是寫入操作還是讀取操作,時間復雜度都是 O(1) 是常量值

(2)節省空間。比如,20 億的數組需要 2000000000/8/1024/1024 = 238M 的空間,而如果使用數組來存儲,假設每個用戶 ID 占用 4 個字節的空間,那么存儲 20 億用戶需要 2000000000 * 4 / 1024 / 1024 = 7600M 的空間,是布隆過濾器的 32 倍。

布隆過濾器缺點:

(1)它在判斷元素是否在集合中時是有一定錯誤幾率的,比如它會把不是集合中的元素判斷為處在集合中。

原因:Hash算法本身的缺陷。

解決方案:使用多個 Hash 算法為元素計算出多個 Hash 值,只有所有 Hash 值對應的數組中的值都為 1 時,才會認為這個元素在集合中。

(2)不支持刪除元素。布隆過濾器不支持刪除元素的缺陷也和 Hash 碰撞有關。舉一個例子,假如兩個元素 A 和 B 都是集合中的元素,它們有相同的 Hash 值,它們就會映射到數組的同一個位置。這時我們刪除了 A,數組中對應位置的值也從 1 變成 0,那么在判斷 B 的時候發現值是 0,也會判斷 B 是不在集合中的元素,就會得到錯誤的結論。

解決方案:我會讓數組中不再只有 0 和 1 兩個值,而是存儲一個計數。比如如果 A 和 B 同時命中了一個數組的索引,那么這個位置的值就是 2,如果 A 被刪除了就把這個值從 2 改為 1。這個方案中的數組不再存儲 bit 位,而是存儲數值,也就會增加空間的消耗。

4、狗樁效應

比方說當有一個極熱點的緩存項,它一旦失效會有大量請求穿透到數據庫,這會對數據庫造成瞬時極大的壓力,我們把這個場景叫做“dog-pile effect”(狗樁效應)。解決狗樁效應的思路是盡量地減少緩存穿透后的并發,方案也比較簡單:

(1)在代碼中控制在某一個熱點緩存項失效之后啟動一個后臺線程,穿透到數據庫,將數據加載到緩存中,在緩存未加載之前,所有訪問這個緩存的請求都不再穿透而直接返回。

(2)通過在 Memcached 或者 Redis 中設置分布式鎖,只有獲取到鎖的請求才能夠穿透到數據庫

六、CDN

1、靜態資源加速的原因

在我們的系統中存在著大量的靜態資源請求:對于移動 APP 來說,這些靜態資源主要是圖片、視頻和流媒體信息;對于 Web 網站來說,則包括了 JavaScript 文件、CSS 文件、靜態 HTML 文件等等。它們的讀請求量極大并且對訪問速度的要求很高還占據了很高的帶寬,這時會出現訪問速度慢帶寬被占滿影響動態請求的問題,那么你就需要考慮如何針對這些靜態資源進行讀加速了。

2、CDN

靜態資源訪問的關鍵點是就近訪問,即北京用戶訪問北京的數據,杭州用戶訪問杭州的數據,這樣才可以達到性能的最優。我們考慮在業務服務器的上層增加一層特殊的緩存,用來承擔絕大部分對于靜態資源的訪問,這一層特殊緩存的節點需要遍布在全國各地,這樣可以讓用戶選擇最近的節點訪問。緩存的命中率也需要一定的保證,盡量減少訪問資源存儲源站的請求數量(回源請求)。這一層緩存就是CDN。

CDN(Content Delivery Network/Content Distribution Network,內容分發網絡)。簡單來說,CDN 就是將靜態的資源分發到位于多個地理位置機房中的服務器上,因此它能很好地解決數據就近訪問的問題,也就加快了靜態資源的訪問速度。

3、搭建CDN系統

搭建一個 CDN 系統需要考慮哪兩點:

(1)如何將用戶的請求映射到 CDN 節點上

你可能會覺得這很簡單啊,只需要告訴用戶 CDN 節點的 IP 地址,然后請求這個 IP 地址上面部署的 CDN 服務就可以了啊。但是,并不是這樣,需要把ip替換為相應的域名。那么如何做到這一點呢?這就需要依靠 DNS 來幫我們解決域名映射的問題了。DNS(Domain Name System,域名系統)實際上就是一個存儲域名和 IP 地址對應關系的分布式數據庫。而域名解析的結果一般有兩種,一種叫做“A 記錄”,返回的是域名對應的 IP 地址;另一種是“CNAME 記錄”,返回的是另一個域名,也就是說當前域名的解析要跳轉到另一個域名的解析上。

舉個例子:比如你的公司的一級域名叫做 example.com,那么你可以把你的圖片服務的域名定義為“img.example.com”,然后將這個域名的解析結果的 CNAME 配置到 CDN 提供的域名上,比如 uclound 可能會提供一個域名是“80f21f91.cdn.ucloud.com.cn”這個域名。這樣你的電商系統使用的圖片地址可以是“img.example.com/1.jpg”。

用戶在請求這個地址時,DNS 服務器會將域名解析到 80f21f91.cdn.ucloud.com.cn 域名上,然后再將這個域名解析為 CDN 的節點 IP,這樣就可以得到 CDN 上面的資源數據了。

域名層級解析優化

因為域名解析過程是分級的,每一級有專門的域名服務器承擔解析的職責,所以域名的解析過程有可能需要跨越公網做多次 DNS 查詢,在性能上是比較差的。一個解決的思路是:在 APP 啟動時對需要解析的域名做預先解析,然后把解析的結果緩存到本地的一個 LRU 緩存里面。這樣當我們要使用這個域名的時候,只需要從緩存中直接拿到所需要的 IP 地址就好了,如果緩存中不存在才會走整個 DNS 查詢的過程。同時為了避免 DNS 解析結果的變更造成緩存內數據失效,我們可以啟動一個定時器定期地更新緩存中的數據。

(2)如何根據用戶的地理位置信息選擇到比較近的節點。

GSLB(Global Server Load Balance,全局負載均衡)的含義是對于部署在不同地域的服務器之間做負載均衡,下面可能管理了很多的本地負載均衡組件。它有兩方面的作用:一方面,它是一種負載均衡服務器,負載均衡,顧名思義嘛,指的是讓流量平均分配使得下面管理的服務器的負載更平均;另一方面,它還需要保證流量流經的服務器與流量源頭在地緣上是比較接近的。

GSLB 可以通過多種策略來保證返回的 CDN 節點和用戶盡量保證在同一地緣區域,比如說可以將用戶的 IP 地址按照地理位置劃分為若干個區域,然后將 CDN 節點對應到一個區域上,根據用戶所在區域來返回合適的節點;也可以通過發送數據包測量 RTT 的方式來決定返回哪一個節點。

總結:DNS 技術是 CDN 實現中使用的核心技術,可以將用戶的請求映射到 CDN 節點上;DNS 解析結果需要做本地緩存,降低 DNS 解析過程的響應時間;GSLB 可以給用戶返回一個離著他更近的節點,加快靜態資源的訪問速度。

拓展

(1)百度域名的解析過程

一開始,域名解析請求先會檢查本機的 hosts 文件,查看是否有 www.baidu.com 對應的 IP;如果沒有的話,就請求 Local DNS 是否有域名解析結果的緩存,如果有就返回標識是從非權威 DNS 返回的結果;如果沒有就開始 DNS 的迭代查詢。先請求根 DNS,根 DNS 返回頂級 DNS(.com)的地址;再請求.com 頂級 DNS 得到 baidu.com 的域名服務器地址;再從 baidu.com 的域名服務器中查詢到 www.baidu.com 對應的 IP 地址,返回這個 IP 地址的同時標記這個結果是來自于權威 DNS 的結果,同時寫入 Local DNS 的解析結果緩存,這樣下一次的解析同一個域名就不需要做 DNS 的迭代查詢了。

(2)CDN延時

一般我們會通過 CDN 廠商的接口將靜態的資源寫入到某一個 CDN 節點上,再由 CDN 內部的同步機制將資源分散同步到每個 CDN 節點,即使 CDN 內部網絡經過了優化,這個同步的過程是有延時的,一旦我們無法從選定的 CDN 節點上獲取到數據,我們就不得不從源站獲取數據,而用戶網絡到源站的網絡可能會跨越多個主干網,這樣不僅性能上有損耗也會消耗源站的帶寬,帶來更高的研發成本。所以我們在使用 CDN 的時候需要關注 CDN 的命中率和源站的帶寬情況。

感謝各位的閱讀!看完上述內容,你們對java高并發系統設計之緩存案例大概了解了嗎?希望文章內容對大家有所幫助。如果想了解更多相關文章內容,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阜平县| 饶平县| 岚皋县| 兴国县| 开封县| 萍乡市| 临邑县| 保定市| 西贡区| 阆中市| 岳西县| 和田县| 利津县| 黔西| 贵港市| 牡丹江市| 同心县| 中阳县| 东安县| 开远市| 太白县| 柯坪县| 崇文区| 七台河市| 秭归县| 宜都市| 吉安市| 南昌市| 汾西县| 南开区| 广安市| 阳西县| 长春市| 油尖旺区| 仁寿县| 福清市| 沙湾县| 唐河县| 三河市| 定安县| 通山县|