www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]什么是靜態(tài)數(shù)據(jù)為什么需要緩存通用緩存機(jī)制總結(jié)后記在分布式系統(tǒng)中,特別是最近很火的微服務(wù)架構(gòu)下,有沒有或者能不能總結(jié)出一個業(yè)務(wù)靜態(tài)數(shù)據(jù)的通用緩存處理機(jī)制或方案,這篇文章將結(jié)合一些實際的研發(fā)經(jīng)驗,嘗試?yán)砬迤渲写嬖诘年P(guān)鍵問題以及探尋通用的解決之道。什么是靜態(tài)數(shù)據(jù)這里靜態(tài)數(shù)據(jù)是指不經(jīng)常發(fā)...

微服務(wù)架構(gòu)下的靜態(tài)數(shù)據(jù)通用緩存機(jī)制!
    • 什么是靜態(tài)數(shù)據(jù)

    • 為什么需要緩存

    • 通用緩存機(jī)制

    • 總結(jié)

    • 后記

在分布式系統(tǒng)中,特別是最近很火的微服務(wù)架構(gòu)下,有沒有或者能不能總結(jié)出一個業(yè)務(wù)靜態(tài)數(shù)據(jù)的通用緩存處理機(jī)制或方案,這篇文章將結(jié)合一些實際的研發(fā)經(jīng)驗,嘗試?yán)砬迤渲写嬖诘年P(guān)鍵問題以及探尋通用的解決之道。


什么是靜態(tài)數(shù)據(jù)




這里靜態(tài)數(shù)據(jù)是指不經(jīng)常發(fā)生變化或者變化頻率比較低的數(shù)據(jù),比如車型庫、用戶基本信息、車輛基本信息等,車型庫這種可能每個月會更新一次,用戶和車輛基本信息的變化來源于用戶注冊、修改,這個操作的頻率相對也是比較低的。

另外這類數(shù)據(jù)的另一個特點是要求準(zhǔn)確率和實時性都比較高,不能出現(xiàn)丟失、錯誤,以及過長時間的陳舊讀。

具體是不是應(yīng)該歸類為靜態(tài)數(shù)據(jù)要看具體的業(yè)務(wù),以及對變化頻率高低的劃分標(biāo)準(zhǔn)。在這里的業(yè)務(wù)定義中,上邊這幾類數(shù)據(jù)都?xì)w為靜態(tài)數(shù)據(jù)。


為什么需要緩存



在面向用戶或車聯(lián)網(wǎng)的業(yè)務(wù)場景中,車型信息、用戶基本信息和車輛基本信息有著廣泛而高頻的業(yè)務(wù)需求,很多數(shù)據(jù)都需要對其進(jìn)行關(guān)聯(lián)處理。在這里緩存的目的就是為了提高數(shù)據(jù)查詢效率。

靜態(tài)數(shù)據(jù)通常都保存在關(guān)系型數(shù)據(jù)庫中,這類數(shù)據(jù)庫的 IO 效率普遍不高,應(yīng)對高并發(fā)的查詢往往捉襟見肘。使用緩存可以極大的提升讀操作的吞吐量,特別是 KV 類的緩存,沒有復(fù)雜的關(guān)系操作,時間復(fù)雜度一般都在 O(1)。注意這里說的緩存指內(nèi)存緩存。

當(dāng)然除了使用緩存,還可以通過其它手段來提高 IO 吞吐量,比如讀寫分離,分庫分表,但是這類面向關(guān)系型數(shù)據(jù)庫的方案更傾向于同時提高讀寫效率,對于單純提升讀吞吐量的需求,這類方案不夠徹底,不能在有限的資源情況下發(fā)揮更好的作用。


通用緩存機(jī)制



下面將直接給出一個我認(rèn)為的通用處理機(jī)制,然后會對其進(jìn)行分析。

微服務(wù)架構(gòu)下的靜態(tài)數(shù)據(jù)通用緩存機(jī)制!
對于某個具體的業(yè)務(wù),其涉及到六個核心程序:

業(yè)務(wù)服務(wù):?提供對某種業(yè)務(wù)數(shù)據(jù)的操作接口,比如車輛服務(wù),提供對車輛基本信息的增刪改查服務(wù)。

關(guān)系數(shù)據(jù)庫:?使用若干表持久化業(yè)務(wù)數(shù)據(jù),比如 SQLServer、MySQL、Oracle 等。

持久化隊列:?可獨立部署的隊列程序,支持?jǐn)?shù)據(jù)持久化,比如 RabbitMQ、RocketMQ、Kafka 等。

緩存處理程序:?從隊列接收數(shù)據(jù),然后寫入緩存。

數(shù)據(jù)一致處理程序:?負(fù)責(zé)檢查緩存數(shù)據(jù)庫和關(guān)系型數(shù)據(jù)庫中數(shù)據(jù)是否一致,如果不一致則使用關(guān)系數(shù)據(jù)庫進(jìn)行更新。

緩存數(shù)據(jù)庫(Redis):?支持持久化的緩存數(shù)據(jù)庫,這里直接選了 Redis,這個基本是業(yè)界標(biāo)準(zhǔn)了。

以及兩個外部定義:

數(shù)據(jù)生產(chǎn)者:業(yè)務(wù)靜態(tài)數(shù)據(jù)的來源,可以理解為前端 APP、Web 系統(tǒng)的某個功能或者模塊。

數(shù)據(jù)消費者:需要使用這些業(yè)務(wù)靜態(tài)數(shù)據(jù)的服務(wù)或者系統(tǒng),比如報警系統(tǒng)需要獲取車輛對應(yīng)的用戶信息以便發(fā)送報警。

下面以問答的形式來說明為什么是這樣一種機(jī)制。


為什么需要業(yè)務(wù)服務(wù)?



既然是微服務(wù)架構(gòu),當(dāng)然離不開服務(wù)了,因為這里探討的是業(yè)務(wù)靜態(tài)數(shù)據(jù),所以是業(yè)務(wù)服務(wù)。不過為了更好的理解,這里還是簡單說下服務(wù)出現(xiàn)的原因。

當(dāng)今業(yè)務(wù)往往需要在多個終端進(jìn)行使用,比如 PC、手機(jī)、平板等,既有網(wǎng)頁的形式,又有 APP 的形式,另外某個數(shù)據(jù)可能在多種不同的業(yè)務(wù)被需要

如果將數(shù)據(jù)操作分布在多個程序中很可能產(chǎn)生數(shù)據(jù)不一致的情況,另外代碼不可避免的冗余,讀寫性能更很難控制,變更也基本上是不敢變的。

通過一個業(yè)務(wù)服務(wù)可以將對業(yè)務(wù)數(shù)據(jù)的操作有序的管理起來,并通過接口的形式對外提供操作能力,代碼不用冗余了,性能也好優(yōu)化了,數(shù)據(jù)不一致也得到了一定的控制,編寫上層應(yīng)用的人也舒服了。


為什么不是進(jìn)程內(nèi)緩存?



很多開發(fā)語言都提供了進(jìn)程內(nèi)緩存的支持,即使沒有提供直接操作緩存的包或庫,也可以通過靜態(tài)變量的方式來實現(xiàn)。對數(shù)據(jù)的查詢請求直接在進(jìn)程內(nèi)存完成,效率可以說是杠杠滴了。但是進(jìn)程內(nèi)緩存存在兩個問題:

緩存數(shù)據(jù)的大小:進(jìn)程可以緩存數(shù)據(jù)的大小受限于系統(tǒng)可用內(nèi)存,同時如果機(jī)器上部署了多個服務(wù),某個服務(wù)使用了太多的內(nèi)存,則可能會影響其它服務(wù)的正常訪問,因此不適合大量數(shù)據(jù)的緩存。

緩存雪崩:緩存同時大量過期或者進(jìn)程重啟的情況下,可能產(chǎn)生大量的緩存穿透,過多的請求打到關(guān)系數(shù)據(jù)庫上,可能導(dǎo)致關(guān)系數(shù)據(jù)庫的崩潰,引發(fā)更大的不可用問題。


為什么是 Redis?



Redis 這類數(shù)據(jù)庫可以解決進(jìn)程內(nèi)緩存的兩個問題:

1、?獨立部署,不影響其它業(yè)務(wù),還可以做集群,內(nèi)存擴(kuò)容比較方便。

2、?支持?jǐn)?shù)據(jù)持久化,即使 Redis 重啟了,緩存的數(shù)據(jù)自身就可以很快恢復(fù)。

另外 Redis 提供了很好的讀寫性能,以及方便的水平擴(kuò)容能力,還支持多種常用數(shù)據(jù)結(jié)構(gòu),使用起來比較方便,可以說是通用緩存首選。


為什么需要隊列?



隊列在這里的目的是為了解耦,坦白的說這個方案中可以沒有隊列,業(yè)務(wù)服務(wù)在關(guān)系數(shù)據(jù)庫操作完成后,直接更新到緩存也是可以的。

之所以加上這個隊列是由于當(dāng)前的業(yè)務(wù)開發(fā)有很明顯的系統(tǒng)拆分的需求,特別是在微服務(wù)架構(gòu)下,為了降低服務(wù)之間的耦合,使用隊列是個常用選擇,在某些開發(fā)模型中也是很推崇的,比如 Actor 模型。

舉個例子,比如新注冊一個用戶,需要贈送其 300 積分,同時還要給其發(fā)個注冊成功的郵件,如果將注冊用戶、贈送積分、發(fā)成功郵件都寫到一起執(zhí)行,會產(chǎn)生兩個問題:

一是注冊操作耗時增加,二是其中某個處理引發(fā)整體不可用的幾率增大,三是程序的擴(kuò)展性不好;

通過引入隊列,將注冊信息分別發(fā)到積分隊列和通知隊列,然后由積分模塊和通知模塊分別處理,用戶、積分、通知三個模塊的耦合降低了,相互影響變小了,以后再增加注冊后的其它處理也就是增加個隊列的事,整體的擴(kuò)展性得到了增強(qiáng)。

隊列作為一種常用的解耦方案,在緩存這里雖然產(chǎn)生的影響不大,但是除了緩存難免同時還會有其它業(yè)務(wù)處理,所以為了統(tǒng)一處理機(jī)制,這里保留了下來。(既然用了,就把它發(fā)揚(yáng)光大)


為什么隊列需要持久化?



持久化是為了解決網(wǎng)絡(luò)抖動或者崩潰導(dǎo)致數(shù)據(jù)丟失的問題,在數(shù)據(jù)從業(yè)務(wù)服務(wù)到隊列,隊列自身處理,再從隊列到緩存處理程序,中間都可能丟失數(shù)據(jù)。

為了解決丟失數(shù)據(jù)的問題,需要發(fā)送時確認(rèn)、隊列自身持久化、接收時確認(rèn)

但是需要注意確認(rèn)機(jī)制可能會導(dǎo)致重復(fù)數(shù)據(jù)的產(chǎn)生,因為在未收到確認(rèn)時就需要重新發(fā)送或接收,而數(shù)據(jù)實際上可能被正常處理,只是確認(rèn)丟失了

確認(rèn)機(jī)制還會降低隊列的吞吐量,但是根據(jù)我們的定義業(yè)務(wù)靜態(tài)數(shù)據(jù)的變更頻率應(yīng)該不高,如果同時還需要較高的并發(fā)分片是個不錯的選擇。

這里持久化隊列推薦選擇 RabbitMQ,雖然吞吐量支持的不是很大,但是各方面綜合不錯,并發(fā)夠用就好。


為什么需要數(shù)據(jù)一致檢查程序?



在業(yè)務(wù)服務(wù)操作完關(guān)系數(shù)據(jù)庫后,數(shù)據(jù)發(fā)送到隊列之前(或者不用隊列就是直接寫入緩存之前),業(yè)務(wù)服務(wù)崩潰了,這時候數(shù)據(jù)就不能更新到緩存了。

還有一種情況是 Redis 發(fā)生了故障轉(zhuǎn)移,master 中的更新沒有同步到 slaver。通過引入這么一個檢查程序,定時的檢查關(guān)系數(shù)據(jù)庫數(shù)據(jù)和緩存數(shù)據(jù)的差別,如果緩存數(shù)據(jù)比較陳舊,則更新之。這樣提供了一種極端情況下的挽救措施。

這個檢查程序的運(yùn)行頻率需要綜合考慮數(shù)據(jù)庫壓力和能夠承受的數(shù)據(jù)陳舊時間,不能把數(shù)據(jù)庫查死了,也不能陳舊太久導(dǎo)致大量數(shù)據(jù)不一致??梢酝ㄟ^設(shè)置上次檢查時間點的方式,每次只檢查從上次檢查時間點(或者最近幾次,防止 Redis 故障轉(zhuǎn)移數(shù)據(jù)未同步的問題)到本次檢查時間點發(fā)生變更的數(shù)據(jù),這樣每次檢查只對增量變更,效率更高。

同時需要理解在分布式系統(tǒng)中,微服務(wù)架構(gòu)下,數(shù)據(jù)不一致是經(jīng)常出現(xiàn)的,必須在一致性和可用性之間做出權(quán)衡,盡力去降低影響,比如使用準(zhǔn)實時或最終一致性。


只要數(shù)據(jù)一致檢查程序是不是就夠了?



假設(shè)沒有緩存處理程序,通過定時同步關(guān)系數(shù)據(jù)庫和緩存數(shù)據(jù)庫是不是就夠了呢?

這還是取決于業(yè)務(wù),如果是車型庫這種數(shù)據(jù),增加一個新的車型,本來之前就沒有,時間上并不是很敏感,這個是可以的。但是對于新增了用戶或者車輛,數(shù)據(jù)消費者還是希望能夠馬上使用最新的數(shù)據(jù)進(jìn)行處理,越快越好,這時使用同步或者準(zhǔn)同步更新就能更加貼近需求。


為什么不用緩存過期機(jī)制?



使用緩存過期機(jī)制可以不需要緩存處理程序和數(shù)據(jù)一致檢查程序,業(yè)務(wù)服務(wù)首先從 Redis 查詢數(shù)據(jù),如果數(shù)據(jù)存在就直接返回,如果不存在則從關(guān)系數(shù)據(jù)庫查詢,然后寫入 Redis,然后再返回,這也是一種常用的緩存處理機(jī)制,網(wǎng)上可以查詢到很多,很多人用的也很好。

但是緩存的過期時間是個問題:緩存多長時間過期,設(shè)置的短可以降低數(shù)據(jù)的陳舊,但是會增加緩存穿透的概率,即使采用隨機(jī)的緩存過期時間,在 Redis 重啟或者故障轉(zhuǎn)移的情況下還是會可能導(dǎo)致緩存雪崩,雪崩的情況下采用數(shù)據(jù)預(yù)熱機(jī)制,也可能會導(dǎo)致服務(wù)更長時間的不可用;

設(shè)置的長可以提升緩存的使用率,但是增加了數(shù)據(jù)陳舊,在上邊對靜態(tài)數(shù)據(jù)的定義中對其準(zhǔn)確率和實時性都有較高的要求,業(yè)務(wù)上能不能接受需要考慮。而且如果操作數(shù)據(jù)和查詢存在波動的峰谷,是不是要引入動態(tài) TTL 的機(jī)制,以達(dá)到緩存使用和直接訪問數(shù)據(jù)庫的一種平衡,這就需要權(quán)衡業(yè)務(wù)需求和技術(shù)方案。


總結(jié)



通過上邊的這些問題問答,再來看看上面提出的微服務(wù)架構(gòu)下靜態(tài)數(shù)據(jù)通用緩存處理機(jī)制。

1、?通過業(yè)務(wù)服務(wù)來包裝對數(shù)據(jù)的操作,不管是操作關(guān)系數(shù)據(jù)庫還是緩存數(shù)據(jù)庫,數(shù)據(jù)消費者其實不需要關(guān)心,它只關(guān)心業(yè)務(wù)服務(wù)能不能提供高并發(fā)實時數(shù)據(jù)的查詢能力

2、?利用分布式系統(tǒng)中經(jīng)常使用隊列進(jìn)行解耦的方式,業(yè)務(wù)服務(wù)不干寫入緩存的事,增加一個隊列訂閱數(shù)據(jù)變更,然后從隊列取數(shù)據(jù)寫入緩存數(shù)據(jù)庫。

3、?對于絕大部分正常的情況,通過隊列更新緩存數(shù)據(jù)和業(yè)務(wù)服務(wù)中更新緩存數(shù)據(jù),其實時性是差不多的,同時實現(xiàn)了業(yè)務(wù)操作和寫緩存的解耦。

4、?在極端崩潰導(dǎo)致數(shù)據(jù)不一致的情況下,通過數(shù)據(jù)一致檢查程序進(jìn)行補(bǔ)救,盡快更新緩存數(shù)據(jù)。

5、?現(xiàn)在業(yè)務(wù)服務(wù)可以通過訪問 Redis 緩存來提供對靜態(tài)數(shù)據(jù)的高并發(fā)準(zhǔn)實時查詢能力,緩存中不存在的數(shù)據(jù)就是不存在,沒有緩存穿透。

對于微服務(wù)架構(gòu)而言,這個機(jī)制借助隊列這種通用的解耦方式,獨立了緩存更新處理,通過準(zhǔn)實時更新和定時檢查,保證了緩存的實時性和極端情況下較短時間內(nèi)達(dá)到最終一致,通過緩存的持久化機(jī)制消除了緩存穿透和雪崩,在緩存的數(shù)據(jù)較大或讀取并發(fā)較高時支持水平擴(kuò)容,可以認(rèn)為對業(yè)務(wù)靜態(tài)數(shù)據(jù)提供了一種廣泛適用的緩存處理機(jī)制。

這個方案在某些情況下可能是沒有必要的,比如你要緩存一個全國限行的城市列表,使用一個進(jìn)程內(nèi)緩存就夠了。

最后剩下的就是工作量的問題了,這個會給開發(fā)和維護(hù)帶來復(fù)雜性,隊列有沒有用的順手的,人手是不是夠,業(yè)務(wù)需求是什么樣的,需要考慮清楚。


后記



Redis 耦合問題:?圖中業(yè)務(wù)服務(wù)直接訪問了 Redis,如果要實現(xiàn)業(yè)務(wù)服務(wù)對 Redis 的完全透明,這個還比較復(fù)雜,可以考慮采用 AOP 的方式,對關(guān)系數(shù)據(jù)庫和 Redis 保持相同的類型定義,分別采用 ORM 和反序列化的方式標(biāo)準(zhǔn)化輸出,這是個想法我也沒有實現(xiàn);同時緩存數(shù)據(jù)是準(zhǔn)實時的,如果要求完全一致,還是應(yīng)該提供從關(guān)系數(shù)據(jù)庫查詢的版本。另外如果要擺脫對 Redis 的直接依賴,還可以通過 OpenResty 來實現(xiàn)對資源的透明訪問,這個不是本文的重點。

服務(wù)可用性問題:?這篇文章沒有關(guān)注服務(wù)可用性問題,為了保證服務(wù)的高可用,每個服務(wù)或者程序都應(yīng)該有多份部署的,無論是負(fù)載均衡的方案,或者傳統(tǒng)的主備方案,在部分部署不可用時仍能夠繼續(xù)提供服務(wù)。

寫的比較快,有些理解不免偏頗,歡迎指正。

作者:波斯碼

cnblogs.com/bossma/p/9858847.html

本文版權(quán)歸作者所有??

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉