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

聊聊本地緩存和分布式緩存

存儲 數(shù)據(jù)管理
緩存是把雙刃劍,一方面我們享受緩存帶來的系統(tǒng)性能提升,另一方面引入緩存會提高系統(tǒng)復(fù)雜度,因?yàn)槟阋紤]緩存的失效、更新、一致性等問題。

緩存,消息隊(duì)列,分庫分表是高并發(fā)解決方案三劍客。

緩存之所以能夠讓系統(tǒng)“更快”,本質(zhì)上做到了如下兩點(diǎn):

  • 減小 CPU 消耗
    將原來需要實(shí)時(shí)計(jì)算的內(nèi)容提前算好、把一些公用的數(shù)據(jù)進(jìn)行復(fù)用,這可以減少 CPU 消耗,從而提升響應(yīng)性能。
  • 減小 I/O 消耗
    將原來對網(wǎng)絡(luò)、磁盤等較慢介質(zhì)的讀寫訪問變?yōu)閷?nèi)存等較快介質(zhì)的訪問,從而提升響應(yīng)性能。

對于應(yīng)用系統(tǒng)來講,我們經(jīng)常將緩存劃分為本地緩存和分布式緩存。

本地緩存 :應(yīng)用中的緩存組件,緩存組件和應(yīng)用在同一進(jìn)程中,緩存的讀寫非???,沒有網(wǎng)絡(luò)開銷。但各應(yīng)用或集群的各節(jié)點(diǎn)都需要維護(hù)自己的單獨(dú)緩存,無法共享緩存。

分布式緩存:和應(yīng)用分離的緩存組件或服務(wù),與本地應(yīng)用隔離,多個(gè)應(yīng)用可直接共享緩存。

這篇文章,聊聊本地緩存和分布式緩存,希望大家讀完之后,在面對不同的業(yè)務(wù)場景時(shí),能夠做出合理的緩存選型。

一、本地緩存 JDK Map

JDK Map 經(jīng)常用于緩存實(shí)現(xiàn):

  • HashMap
    HashMap 是一種基于哈希表的集合類,它提供了快速的插入、查找和刪除操作。可以將鍵值對作為緩存項(xiàng)的存儲方式,將鍵作為緩存項(xiàng)的唯一標(biāo)識符,值作為緩存項(xiàng)的內(nèi)容。
  • ConcurrentHashMap
    ConcurrentHashMap 是線程安全的 HashMap,它在多線程環(huán)境下可以保證高效的并發(fā)讀寫操作。
  • LinkedHashMap
    LinkedHashMap 是一種有序的 HashMap ,它保留了元素插入的順序,可以按照插入順序或者訪問順序進(jìn)行遍歷。
  • TreeMap
    TreeMap 是一種基于紅黑樹的有序 Map,它可以按照鍵的順序進(jìn)行遍歷。

筆者曾經(jīng)負(fù)責(zé)藝龍紅包系統(tǒng),紅包活動就是存儲在 ConcurrentHashMap 中 ,通過定時(shí)任務(wù)刷新緩存 。

圖片

核心流程:

1、紅包系統(tǒng)啟動后,初始化一個(gè) ConcurrentHashMap 作為紅包活動緩存 ;

2、數(shù)據(jù)庫查詢所有的紅包活動 , 并將活動信息存儲在 Map 中 ;

3、定時(shí)任務(wù)每隔 30 秒 ,執(zhí)行緩存加載方法,刷新緩存。

為什么紅包系統(tǒng)會將紅包活動信息存儲在本地內(nèi)存 ConcurrentHashMap 呢 ?

  • 紅包系統(tǒng)是高并發(fā)應(yīng)用,快速將請求結(jié)果響應(yīng)給前端,大大提升用戶體驗(yàn);
  • 紅包活動數(shù)量并不多,就算全部放入到 Map 里也不會產(chǎn)生內(nèi)存溢出的問題;
  • 定時(shí)任務(wù)刷新緩存并不會影響紅包系統(tǒng)的業(yè)務(wù)。

筆者見過很多單體應(yīng)用都使用這種方案,該方案的特點(diǎn)是簡潔易用,工程實(shí)現(xiàn)也容易 。

二、本地緩存框架

雖然使用 JDK Map 能快捷構(gòu)建緩存,但緩存的功能還是比較孱弱的。

因?yàn)楝F(xiàn)實(shí)場景里,我們可能需要給緩存添加緩存統(tǒng)計(jì)、過期失效、淘汰策略等功能。

于是,本地緩存框架應(yīng)運(yùn)而生。

流行的 Java 緩存框架包括:Ehcache , Google Guava ,  Caffeine Cache 。

圖片

下圖展示了 Caffeine 框架的使用示例。

圖片

雖然本地緩存框架的功能很強(qiáng)大,但是本地緩存的缺陷依然明顯。

1、高并發(fā)的場景,應(yīng)用重啟之后,本地緩存就失效了,系統(tǒng)的負(fù)載就比較大,需要花較長的時(shí)間才能恢復(fù);

2、每個(gè)應(yīng)用節(jié)點(diǎn)都會維護(hù)自己的單獨(dú)緩存,緩存同步比較頭疼。

三、分布式緩存

分布式緩存是指將緩存數(shù)據(jù)分布在多臺機(jī)器上,以提高緩存容量和并發(fā)讀寫能力的緩存系統(tǒng)。分布式緩存通常由多臺機(jī)器組成一個(gè)集群,每臺機(jī)器上都運(yùn)行著相同的緩存服務(wù)進(jìn)程,緩存數(shù)據(jù)被均勻地分布在集群中的各個(gè)節(jié)點(diǎn)上。

Redis 是分布式緩存的首選,甚至我們一提到緩存,很多后端工程師首先想到的就它。

下圖是神州專車訂單的 Redis 集群架構(gòu) 。將 Redis 集群拆分成四個(gè)分片,每個(gè)分片包含一主一從,主從可以切換。應(yīng)用 A 根據(jù)不同的緩存 key 訪問不同的分片。

圖片

與本地緩存相比,分布式緩存具有以下優(yōu)點(diǎn):

1、容量和性能可擴(kuò)展

通過增加集群中的機(jī)器數(shù)量,可以擴(kuò)展緩存的容量和并發(fā)讀寫能力。同時(shí),緩存數(shù)據(jù)對于應(yīng)用來講都是共享的。

2、高可用性

由于數(shù)據(jù)被分布在多臺機(jī)器上,即使其中一臺機(jī)器故障,緩存服務(wù)也能繼續(xù)提供服務(wù)。

但是分布式緩存的缺點(diǎn)同樣不容忽視。

1、網(wǎng)絡(luò)延遲

分布式緩存通常需要通過網(wǎng)絡(luò)通信來進(jìn)行數(shù)據(jù)讀寫,可能會出現(xiàn)網(wǎng)絡(luò)延遲等問題,相對于本地緩存而言,響應(yīng)時(shí)間更長。

2、復(fù)雜性

分布式緩存需要考慮序列化、數(shù)據(jù)分片、緩存大小等問題,相對于本地緩存而言更加復(fù)雜。

舉一個(gè)真實(shí)的案例,這次案例讓筆者對于分布式緩存的認(rèn)知提上了另一個(gè)臺階。

2014年,同事開發(fā)了比分直播的系統(tǒng),所有的請求都是從分布式緩存 Memcached 中獲取后直接響應(yīng)。常規(guī)情況下,從緩存中查詢數(shù)據(jù)非常快,但在線用戶稍微多一點(diǎn),整個(gè)系統(tǒng)就會特別卡。

通過 jstat 命令發(fā)現(xiàn) GC 頻率極高,幾次請求就將新生代占滿了,而且 CPU 的消耗都在 GC 線程上。初步判斷是緩存值過大導(dǎo)致的,果不其然,緩存大小在 300k 到 500k 左右。

解決過程還比較波折,分為兩個(gè)步驟:

  1. 修改新生代大小,從原來的 2G 修改成 4G,并精簡緩存數(shù)據(jù)大小 (從平均 300k 左右降為 80k 左右);
  2. 把緩存拆成兩個(gè)部分,第一部分是全量數(shù)據(jù),第二部分是增量數(shù)據(jù)(數(shù)據(jù)量很小)。頁面第一次請求拉取全量數(shù)據(jù),當(dāng)比分有變化的時(shí)候,通過 websocket 推送增量數(shù)據(jù)。

經(jīng)過這次優(yōu)化,筆者理解到:緩存雖然可以提升整體速度,但是在高并發(fā)場景下,緩存對象大小依然是需要關(guān)注的點(diǎn),稍不留神就會產(chǎn)生事故。另外我們也需要合理地控制讀取策略,最大程度減少 GC 的頻率 , 從而提升整體性能。

四、多級緩存

開源中國網(wǎng)站最開始完全是用本地緩存框架 Ehcache 。后來隨著訪問量的激增,出現(xiàn)了一個(gè)可怕的問題:“因?yàn)?Java 程序更新很頻繁,每次更新的時(shí)候都要重啟。一旦重啟后,整個(gè) Ehcache 緩存里的數(shù)據(jù)都被清掉。重啟后若大量訪問進(jìn)來的話,開源中國的數(shù)據(jù)庫基本上很快就會崩掉”。

于是,開源中國開發(fā)了多級緩存框架  J2Cache,使用了多級緩存 Ehcache + Redis 。

多級緩存有如下優(yōu)勢:

  1. 離用戶越近,速度越快;
  2. 減少分布式緩存查詢頻率,降低序列化和反序列化的 CPU 消耗;
  3. 大幅度減少網(wǎng)絡(luò) IO 以及帶寬消耗。

本地緩存做為一級緩存,分布式緩存做為二級緩存,首先從一級緩存中查詢,若能查詢到數(shù)據(jù)則直接返回,否則從二級緩存中查詢,若二級緩存中可以查詢到數(shù)據(jù),則回填到一級緩存中,并返回?cái)?shù)據(jù)。若二級緩存也查詢不到,則從數(shù)據(jù)源中查詢,將結(jié)果分別回填到一級緩存,二級緩存中。

圖片

2018年,筆者服務(wù)的一家電商公司需要進(jìn)行 app 首頁接口的性能優(yōu)化。筆者花了大概兩天的時(shí)間完成了整個(gè)方案,采取的是兩級緩存模式,同時(shí)利用了 Guava 的惰性加載機(jī)制,整體架構(gòu)如下圖所示:

圖片

緩存讀取流程如下:

1、業(yè)務(wù)網(wǎng)關(guān)剛啟動時(shí),本地緩存沒有數(shù)據(jù),讀取 Redis 緩存,如果 Redis 緩存也沒數(shù)據(jù),則通過 RPC 調(diào)用導(dǎo)購服務(wù)讀取數(shù)據(jù),然后再將數(shù)據(jù)寫入本地緩存和 Redis 中;若 Redis 緩存不為空,則將緩存數(shù)據(jù)寫入本地緩存中。

2、由于步驟1已經(jīng)對本地緩存預(yù)熱,后續(xù)請求直接讀取本地緩存,返回給用戶端。

3、Guava 配置了 refresh 機(jī)制,每隔一段時(shí)間會調(diào)用自定義 LoadingCache 線程池(5個(gè)最大線程,5個(gè)核心線程)去導(dǎo)購服務(wù)同步數(shù)據(jù)到本地緩存和 Redis 中。

優(yōu)化后,性能表現(xiàn)很好,平均耗時(shí)在 5ms 左右。最開始我以為出現(xiàn)問題的幾率很小,可是有一天晚上,突然發(fā)現(xiàn) app 端首頁顯示的數(shù)據(jù)時(shí)而相同,時(shí)而不同。

也就是說:雖然 LoadingCache 線程一直在調(diào)用接口更新緩存信息,但是各個(gè) 服務(wù)器本地緩存中的數(shù)據(jù)并非完成一致。說明了兩個(gè)很重要的點(diǎn):

1、惰性加載仍然可能造成多臺機(jī)器的數(shù)據(jù)不一致

2、LoadingCache 線程池?cái)?shù)量配置的不太合理,  導(dǎo)致了線程堆積

最終,我們的解決方案是:

1、惰性加載結(jié)合消息機(jī)制來更新緩存數(shù)據(jù),也就是:當(dāng)導(dǎo)購服務(wù)的配置發(fā)生變化時(shí),通知業(yè)務(wù)網(wǎng)關(guān)重新拉取數(shù)據(jù),更新緩存。

2、適當(dāng)調(diào)大 LoadigCache 的線程池參數(shù),并在線程池埋點(diǎn),監(jiān)控線程池的使用情況,當(dāng)線程繁忙時(shí)能發(fā)出告警,然后動態(tài)修改線程池參數(shù)。

五、總結(jié)

Fred Brooks 在 1987 年所發(fā)表的一篇關(guān)于軟件工程的經(jīng)典論文《沒有銀彈:軟件工程的本質(zhì)性與附屬性工作》。

論文強(qiáng)調(diào)真正的銀彈并不存在,而所謂的銀彈則是指沒有任何一項(xiàng)技術(shù)或方法可以能讓軟件工程的生產(chǎn)力在十年內(nèi)提高十倍。

通俗來講:在技術(shù)領(lǐng)域中沒有一種通用的解決方案可以解決所有問題。技術(shù)本質(zhì)上是為了解決問題而存在的,每個(gè)問題都有其獨(dú)特的環(huán)境和限制條件,沒有一種通用的技術(shù)或工具可以完美地解決所有問題。

緩存是把雙刃劍,一方面我們享受緩存帶來的系統(tǒng)性能提升,另一方面引入緩存會提高系統(tǒng)復(fù)雜度,因?yàn)槟阋紤]緩存的失效、更新、一致性等問題。

在面臨緩存選型時(shí),一定要結(jié)合業(yè)務(wù)場景,研發(fā)效率,運(yùn)維成本,人力模型,技術(shù)儲備等因素,做出合理的選擇。

責(zé)任編輯:武曉燕 來源: 勇哥java實(shí)戰(zhàn)分享
相關(guān)推薦

2024-05-20 08:08:00

分布式系統(tǒng)緩存C#

2018-02-07 10:46:20

數(shù)據(jù)存儲

2023-02-28 07:01:11

分布式緩存平臺

2013-06-13 11:29:14

分布式分布式緩存

2019-02-18 11:16:12

Redis分布式緩存

2017-12-12 14:51:15

分布式緩存設(shè)計(jì)

2019-07-04 15:13:16

分布式緩存Redis

2009-11-09 09:25:24

Memcached入門

2015-09-21 09:20:11

C#Couchbase使用

2022-04-07 17:13:09

緩存算法服務(wù)端

2023-01-13 07:39:07

2018-12-14 10:06:22

緩存分布式系統(tǒng)

2009-02-06 09:38:38

memcached分布式緩存系統(tǒng)ASP.NET

2009-02-10 08:57:01

分布式緩存.Net開發(fā)

2013-04-19 11:03:32

memcahce入門教分布式緩存系統(tǒng)

2023-05-05 06:13:51

分布式多級緩存系統(tǒng)

2017-09-27 10:53:53

分布式數(shù)據(jù)集SparkRDD

2011-11-29 09:49:16

數(shù)據(jù)庫其他數(shù)據(jù)庫NoSQL

2015-12-14 17:35:21

GemFire12306分布式

2020-11-26 09:38:19

分布式架構(gòu)系統(tǒng)
點(diǎn)贊
收藏

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