1. 程式人生 > >微服務化之快取的設計

微服務化之快取的設計

前言

在高併發場景下,需要通過快取來減少資料庫的壓力,使得大量的訪問進來能夠命中快取,只有少量的需要到資料庫層。由於快取基於記憶體,可支援的併發量遠遠大於基於硬碟的資料庫。所以對於高併發設計,快取的設計時必不可少的一環。

一、為什麼要使用快取

為什麼要使用快取呢?源於人類的一個夢想,就是多快好省的建設社會主義。

多快好省?很多客戶都這麼要求,但是作為具體做技術的你,當然知道,好就不能快,多就沒法省。

可是沒辦法,客戶都這樣要求:

這個能不能便宜一點,你咋這麼貴呀,你看人家都很便宜的。(您好,這種打折的房間比較靠裡,是不能面向大海的)

你們的效能怎麼這麼差啊,用你這個系統跑的這麼慢,你看人家廣告中說速度能達到多少多少。(您好,你如果買一個頂配的,我們也是有這種效能的)

你們服務不行啊,你就不能彬彬有禮,穿著整齊,送點水果瓜子啥的?(您好,我們蘭州拉麵館沒有這項服務,可以去對面的俏江南看一下)

這麼貴的菜,一盤就這麼一點點,都吃不飽,就不能上一大盤麼。(您好,對面的蘭州拉麵10塊錢一大碗)

怎麼辦呢?勞動人民還是很有智慧的,就是聚焦核心需求,讓最最核心的部分享用好和快,而非核心的部門就多和省就可以了。

你可以大部分時間住在公司旁邊的出租屋裡面,但是出去度假的一個星期,選一個面朝大海,春暖花開的五星級酒店。

你可以大部分時間都擠地鐵,擠公交,跋涉2個小時從北五環到南五環,但是有急事的時候,你可以打車,想旅遊的時候,可以租車。

你可以大部分時間都吃普通的餐館,而朋友來了,就去高階飯店裡面搓一頓。

在計算機世界也是這樣樣子的,如圖所示。

 

 

越是快的裝置,儲存量越小,越貴,而越是慢的裝置,儲存量越大,越便宜。

對於一家電商來講,我們既希望儲存越來越多的資料,因為資料將來就是資產,就是財富,只有有了資料,我們才知道使用者需要什麼,同時又希望當我想訪問這些資料的時候,能夠快速的得到,雙十一拼的就是速度和使用者體驗,要讓使用者有流暢的感覺。

所以我們要講大量的資料都儲存下來,放在便宜的儲存裡面,同時將經常訪問的,放在貴的,小的儲存裡面,當然貴的快的往往比較資源有限,因而不能長時間被某些資料長期霸佔,所以要大家輪著用,所以叫快取,也就是暫時存著。

二、都有哪些型別的快取

當一個應用剛開始的時候,架構比較簡單,往往就是一個Tomcat,後面跟著一個數據庫。

 

 

簡單的應用,併發量不大的時候,當然沒有問題。

然而資料庫相當於我們應用的中軍大帳,是我們整個架構中最最關鍵的一部分,也是最不能掛,也最不能會被攻破的一部分,因而所有對資料庫的訪問都需要一道屏障來進行保護,常用的就是快取。

我們以Tomcat為分界線,之外我們稱為接入層,接入層當然應該有快取,還有CDN,

Tomcat之後,我們稱為應用層,應用層也應該有快取,這是我們這一節討論的重點。

最簡單的方式就是Tomcat裡面有一層快取,常稱為本地快取LocalCache。

這類的快取常見的有Ehcache和Guava Cache,由於這類快取在Tomcat本地,因而訪問速度是非常快的。

但是本地快取有個比較大的缺點,就是快取是放在JVM裡面的,會面臨Full GC的問題,一旦出現了FullGC,就會對應用的效能和相應時間產生影響,當然也可以嘗試jemalloc的分配方式。

還有一種方式,就是在Tomcat和Mysql中間加了一層Cache,我們常稱為分散式快取

 

 

分散式快取常見的有Memcached和Redis,兩者各有優缺點。

Memcached適合做簡單的key-value儲存,記憶體使用率比較高,而且由於是多核處理,對於比較大的資料,效能較好。

但是缺點也比較明顯,Memcached嚴格來講沒有叢集機制,橫向擴充套件完全靠客戶端來實現。另外Memcached無法持久化,一旦掛了資料就都丟失了,如果想實現高可用,也是需要客戶端進行雙寫才可以。

所以可以看出Memcached真的是設計出來,簡簡單單為了做一個快取的。

 

Redis的資料結構就豐富的多了,單執行緒的處理所有的請求,對於比較大的資料,效能稍微差一點。

 

 

Redis提供持久化的功能,包括RDB的全量持久化,或者AOF的增量持久化,從而使得Redis掛了,資料是有機會恢復的。

Redis提供成熟的主備同步,故障切換的功能,從而保證了高可用性。

所以很多地方管Redis稱為記憶體資料庫,因為他的一些特性已經有了資料庫的影子。

這也是很多人願意用Redis的原因,集合了快取和資料庫的優勢,但是往往會濫用這些優勢,從而忽略了架構層面的設計,使得Redis叢集有很大的風險。Java架構交流學習圈:681065582 面向具有Java開發經驗人群 幫助突破瓶頸 提升思維能力

很多情況下,會將Redis當做資料庫使用,開啟持久化和主備同步機制,以為就可以高枕無憂了。

 

 

然而Redis的持久化機制,全量持久化則往往需要額外較大的記憶體,而在高併發場景下,記憶體本來就很緊張,如果造成swap,就會影響效能。增量持久化也涉及到寫磁碟和fsync,也是會拖慢處理的速度,在平時還好,如果高併發場景下,仍然會影響吞吐量。

所以在架構設計角度,快取就是快取,要意識到資料會隨時丟失的,要意識到快取的存著的目的是攔截到資料庫的請求。如果為了保證快取的資料不丟失,從而影響了快取的吞吐量,甚至穩定性,讓快取響應不過來,甚至掛掉,所有的請求擊穿到資料庫,就是更加嚴重的事情了。

如果非常需要進行持久化,可以考慮使用levelDB此類的,對於隨機寫入效能較好的key-value持久化儲存,這樣只有部分的確需要持久化的資料,才進行持久化,而非無論什麼資料,通通往Redis裡面扔,同時統一開啟了持久化。

三、基於快取的架構設計要點

所以基於快取的設計:

1、多層次

這樣某一層的快取掛了,還有另一層可以撐著,等待快取的修復,例如分散式快取因為某種原因掛了,因為持久化的原因,同步機制的原因,記憶體過大的原因等,修復需要一段時間,在這段時間內,至少本地快取可以抗一陣,不至於一下子就擊穿資料庫。而且對於特別特別熱的資料,熱到導致集中式的快取處理不過來,網絡卡也被打滿的情況,由於本地快取不需要遠端呼叫,也是分佈在應用層的,可以緩解這種問題。

2、分場景

到底要解決什麼問題,可以選擇不同的快取。是要儲存大的無格式的資料,還是要儲存小的有格式的資料,還是要儲存一定需要持久化的資料。具體的場景下一節詳細談。

3、要分片

使得每一個快取例項都不大,但是例項數目比較多,這樣一方面可以實現負載均衡,防止單個例項稱為瓶頸或者熱點,另一方面如果一個例項掛了,影響面會小很多,高可用性大大增強。分片的機制可以在客戶端實現,可以使用中介軟體實現,也可以使用Redis的Cluster的方式,分片的演算法往往都是雜湊取模,或者一致性雜湊。

四、快取的使用場景

當你的應用扛不住,知道要使用快取了,應該怎麼做呢?

場景1:和資料庫中的資料結構保持一致,原樣快取

這種場景是最常見的場景,也是很多架構使用快取的適合,最先涉及到的場景。

基本就是資料庫裡面啥樣,我快取也啥樣,資料庫裡面有商品資訊,快取裡面也放商品資訊,唯一不同的是,資料庫裡面是全量的商品資訊,快取裡面是最熱的商品資訊。

每當應用要查詢商品資訊的時候,先查快取,快取沒有就查資料庫,查出來的結果放入快取,從而下次就查到了。

這個是快取最最經典的更新流程。這種方式簡單,直觀,很多快取的庫都預設支援這種方式。

場景2:列表排序分頁場景的快取

有時候我們需要獲得一些列表資料,並對這些資料進行排序和分頁。

例如我們想獲取點贊最多的評論,或者最新的評論,然後列出來,一頁一頁的翻下去。

在這種情況下,快取裡面的資料結構和資料庫裡面完全不一樣。

如果完全使用資料庫進行實現,則按照某種條件將所有的行查詢出來,然後按照某個欄位進行排序,然後進行分頁,一頁一頁的展示。

但是當資料量比較大的時候,這種方式往往成為瓶頸,首先涉及的資料庫行數比較多,而且排序也是個很慢的活,儘管可能有索引,分頁也是翻頁到最後,越是慢。

在快取裡面,就沒必要每行一個key了,而是可以使用Redis的列表方式進行儲存,當然列表的長短是有限制的,肯定放不下資料庫裡面這麼多,但是大家會發現其實對於所有的列表,使用者往往沒有耐心看個十頁八頁的,例如百度上搜個東西,也是有排序和分頁的,但是你每次都往後翻了嗎,每頁就十條,就算是十頁,或者一百頁,也就一千條資料,如果保持ID的話,完全放的下。Java架構交流學習圈:681065582 面向具有Java開發經驗人群 幫助突破瓶頸 提升思維能力

如果已經排好序,放在Redis裡面,那取出列表,翻頁就非常快了。

可以後臺有一個執行緒,非同步的初始化和重新整理快取,在快取裡面儲存一個時間戳,當有更新的時候,重新整理時間戳,非同步任務發現時間戳改變了,就重新整理快取。

場景3:計數快取

計數對於資料庫來講,是一個非常繁重的工作,需要查詢大量的行,最後得出計數的結論,當資料改變的時候,需要重新刷一遍,非常影響效能。

因此可以有一個計數服務,後端是一個快取,將計數作為結果放在快取裡面,當資料有改變的時候,呼叫計數服務增加或者減少計數,而非通過非同步資料庫count來更新快取。

計數服務可以使用Redis進行單個計數,或者hash表進行批量計數

場景4:重構維度快取

有時候資料庫裡面保持的資料的維度是為了寫入方便,而非為了查詢方便的,然而同時查詢過程,也需要處理高併發,因而需要為了查詢方便,將資料重新以另一個維度儲存一遍,或者說將多給資料庫的內容聚合一下,再儲存一遍,從而不用每次查詢的時候都重新聚合,如果還是放在資料庫,比較難維護,放在快取就好一些。

例如一個商品的所有的帖子和帖子的使用者,以及一個使用者發表過的所有的帖子就是屬於兩個維度。

這需要寫入一個維度的時候,同時非同步通知,更新快取中的另一個維度。

在這種場景下,資料量相對比較大,因而單純用記憶體快取memcached或者redis難以支撐,往往會選擇使用levelDB進行儲存,如果levelDB的效能跟不上,可以考慮在levelDB之前,再來一層memcached。

場景5:較大的詳情內容資料快取

對於評論的詳情,或者帖子的詳細內容,屬於非結構化的,而且內容比較大,因而使用memcached比較好。

五、快取三大矛盾問題

1、快取實時性和一致性問題:當有了寫入後咋辦?

雖然使用了快取,大家心裡都有一個預期,就是實時性和一致性得不到完全的保證,畢竟資料儲存了多份,資料庫一份,快取中一份,當資料庫中因寫入而產生了新的資料,往往快取是不會和資料庫操作放在一個事務裡面的,如何將新的資料更新到快取裡面,什麼時候更新到快取裡面,不同的策略不一樣。

從使用者體驗角度,當然是越實時越好,使用者體驗越流暢,完全從這個角度出發,就應該有了寫入,馬上廢棄快取,觸發一次資料庫的讀取,從而更新快取。但是這和第三個問題,高併發就矛盾了,如果所有的都實時從資料庫裡面讀取,高併發場景下,資料庫往往受不了。

2、快取的穿透問題:當沒有讀到咋辦?

為什麼會出現快取讀取不到的情況呢?

第一:可能讀取的是冷資料,原來從來沒有訪問過,所以需要到資料庫裡面查詢一下,然後放入快取,再返回給客戶。

第二:可能資料因為有了寫入,被實時的從快取中刪除了,就如第一個問題中描述的那樣,為了保證實時性,當資料庫中的資料更新了之後,馬上刪除快取中的資料,導致這個時候的讀取讀不到,需要到資料庫裡面查詢後,放入快取,再返回給客戶。

第三:可能是快取實效了,每個快取資料都會有實效時間,過了一段時間沒有被訪問,就會失效,這個時候資料就訪問不到了,需要訪問資料庫後,再放入快取。

第四:資料被換出,由於快取記憶體是有限的,當使用快滿了的時候,就會使用類似LRU策略,將不經常使用的資料換出,所以也要訪問資料庫。

第五:後端確實也沒有,應用訪問快取沒有,於是查詢資料庫,結果資料庫裡面也沒有,只好返回客戶為空,但是尷尬的是,每次出現這種情況的時候,都會面臨著一次資料庫的訪問,純屬浪費資源,常用的方法是,講這個key對應的結果為空的事實也進行快取,這樣快取可以命中,但是命中後告訴客戶端沒有,減少了資料庫的壓力。

無論哪種原因導致的讀取快取讀不到的情況,該怎麼辦?是個策略問題。

一種是同步訪問資料庫後,放入快取,再返回給客戶,這樣實時性最好,但是給資料庫的壓力也最大。

另一種方式就是非同步的訪問資料庫,暫且返回客戶一個fallback值,然後同時觸發一個非同步更新,這樣下次就有了,這樣資料庫壓力小很多,但是使用者就訪問不到實時的資料了。

3、快取對資料庫高併發訪問:都來訪問資料庫咋辦?

我們本來使用快取,是來攔截直接訪問資料庫請求的,從而保證資料庫大本營永遠處於健康的狀態。但是如果一遇到不命中,就訪問資料庫的話,平時沒有什麼問題,但是大促情況下,資料庫是受不了的。

一種情況是多個客戶端,併發狀態下,都不命中了,於是併發的都來訪問資料庫,其實只需要訪問一次就好,這種情況可以通過加鎖,只有一個到後端來實現。

另外就是即便採取了上述的策略,依然併發量非常大,後端的資料庫依然受不了,則需要通過降低實時性,將快取攔在資料庫前面,暫且撐住,來解決。

六、解決快取三大矛盾的重新整理策略

1、實時策略

所謂的實時策略,是平時快取使用的最常用的策略,也是保持實時性最好的策略。

讀取的過程,應用程式先從cache取資料,沒有得到,則從資料庫中取資料,成功後,放到快取中。如果命中,應用程式從cache中取資料,取到後返回。Java架構交流學習圈:681065582 面向具有Java開發經驗人群 幫助突破瓶頸 提升思維能力

寫入的過程,把資料存到資料庫中,成功後,再讓快取失效,失效後下次讀取的時候,會被寫入快取。那為什麼不直接寫快取呢?因為如果兩個執行緒同時更新資料庫,一個將資料庫改為10,一個將資料庫改為20,資料庫有自己的事務機制,可以保證如果20是後提交的,資料庫裡面改為20,但是回過頭來寫入快取的時候就沒有事務了,如果改為20的執行緒先更新快取,改為10的執行緒後更新快取,於是就會長時間出現快取中是10,但是資料庫中是20的現象。

這種方式實時性好,使用者體驗好,是預設應該使用的策略。

2、非同步策略

相關推薦

服務化快取設計

前言 在高併發場景下,需要通過快取來減少資料庫的壓力,使得大量的訪問進來能夠命中快取,只有少量的需要到資料庫層。由於快取基於記憶體,可支援的併發量遠遠大於基於硬碟的資料庫。所以對於高併發設計,快取的設計時必不可少的一環。 一、為什麼要使用快取 為什麼要使用快取呢?源於人類的一個夢想,就是多快好省

服務化服務拆分與服務發現

作者:劉超 一、服務拆分的前提 說到微服務,服務拆分是繞不過去的話題,但是微服務不是說拆就能拆的,有很多的前提條件,需要完成前面幾節所論述的部分。 首先要有一個持續整合的平臺,使得服務在拆分的過程中,功能的一致性,這種一致性不能通過人的經驗來,而需要經過大量的迴歸測試集,並且持續的

服務化無狀態化與容器化

此文已由作者劉超授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 一、為什麼要做無狀態化和容器化 很多應用拆分成微服務,是為了承載高併發,往往一個程序扛不住這麼大的量,因而需要拆分成多組程序,每組程序承載特定的工作,根據併發的壓力用多個副本公共承擔流量。 將一個程序變成多組程序,每

服務化的資料庫設計與讀寫分離

本文由 網易雲 釋出作者:劉超,網易雲解決方案架構師資料庫永遠是應用最關鍵的一環,同時越到高併發階段,資料庫往往成為瓶頸,如果資料庫表和索引不在一開始就進行良好的設計,則後期資料庫橫向擴充套件,分庫分表都會遇到困難。對於網際網路公司來講,一般都會使用MySQL資料庫。一、資料

[js高手路]設計模式系列課程-委托模式實戰博發布功能

i++ 發布 動態 use shee 內容 標題 cnblogs 文件 在實際開發中,經常需要為Dom元素綁定事件,如果頁面上有4個li元素,點擊對應的li,彈出對應的li內容,怎麽做呢?是不是很簡單? 大多數人的做法都是:獲取元素,綁定事件 1 <ul&

服務化的數據庫設計與讀寫分離

邏輯 例子 origin sele 持久化 能夠 ODB ima 來看 數據庫永遠是應用最關鍵的一環,同時越到高並發階段,數據庫往往成為瓶頸,如果數據庫表和索引不在一開始就進行良好的設計,則後期數據庫橫向擴展,分庫分表都會遇到困難。 對於互聯網公司來講,一般都會使用Mysq

acmeair專案 單體應用利用ServiceComb實現服務化和雲化

acmeair專案 單體應用利用ServiceComb實現微服務化和雲化之路 http://servicecomb.apache.org/cn/docs/go-to-cloud/ 匯入關鍵設定: <mirror> <id>alimaven</i

信小程式快取——不同頁面傳遞資料

1. 新增快取 單個金鑰允許儲存的最大資料長度為1MB,所有資料儲存上限為10MB。 // 儲存資訊到storage // 非同步儲存 set() { wx.setStorage({ key: 'user', data: 'cck', succe

乾貨 | 服務化的 10 個設計要點

微服務生態   微服務有哪些要點呢?先看下圖是 SpringCloud 的整個生態。   設計要點一:API 閘道器   在實施微服務的過程中,不免要面臨服務的聚合與拆分,當後端服務的拆分相對比較頻繁的時候,作為手機 App 來講,往

Redis快取設計快取穿透、快取雪崩

使用快取的優缺點: 優點: 提高系統響應速度,加速讀寫,Redis將數全都存放在記憶體中,響應速度更快。 降低了後臺的負載,減少了對後端的直接訪問 缺點: 資料一致性問題,快取層的資料與儲存層的資料可能存在不一致的問題 維護複雜度高了,加入快取後要同時處理快取曾和持

高效能網站架構設計快取篇(6)- Redis 叢集命令

叢集cluster info :列印叢集的資訊cluster nodes :列出叢集當前已知的所有節點( node),以及這些節點的相關資訊。節點cluster meet <ip> <port> :將 ip 和 port 所指定的節點新增到叢集當中,讓它成為叢集的一份子。cluster

高效能網站架構設計快取篇(3)- Redis 的配置

我們說Redis是一個強大的Key-Value儲存系統,在前面我們已遇到了兩個問題: 1、redis server 啟動後,獨佔程序,能不能修改為後臺服務呢? 2、redis server 服務是單執行緒的,而我的機器是多核的,能不能在同一臺機器上開啟多個例項更充分的利用 cpu 資源呢?但6379埠已經

高效能網站架構設計快取篇(1)- Redis的安裝與使用

一、什麼 Redis REmote DIctionary Server,簡稱 Redis,是一個類似於Memcached的Key-Value儲存系統。相比Memcached,它支援更豐富的資料結構,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)

高效能網站架構設計快取篇(5)- Redis 叢集(上)

叢集技術是構建高效能網站架構的重要手段,試想在網站承受高併發訪問壓力的同時,還需要從海量資料中查詢出滿足條件的資料,並快速響應,我們必然想到的是將資料進行切片,把資料根據某種規則放入多個不同的伺服器節點,來降低單節點伺服器的壓力。 上一篇我們講到了 Redis 的主從複製技術,當實現了多節點的 master

高效能網站架構設計快取篇(4)- Redis 主從複製

Redis 的主從複製配置非常容易,但我們先來了解一下它的一些特性。 redis 使用非同步複製。從 redis 2.8 開始,slave 也會週期性的告訴 master 現在的資料量。可能只是個機制,用途應該不大。 一個 master 可以擁有多個 slave,廢話,這也是業界的標配吧。

高效能網站架構設計快取篇(2)- Redis C#客戶端

在上一篇中我簡單的介紹瞭如何利用redis自帶的客戶端連線server並執行命令來操作它,但是如何在我們做的專案或產品中操作這個強大的記憶體資料庫呢?首先我們來了解一下redis的原理吧。 官方文件上是這樣說的:Redis在TCP埠6379上監聽到來的連線,客戶端連線到來時,Redis伺服器為此建立一個TC

高效能網站架構設計快取篇(6)- Redis 叢集(中)

昨天晚上釣魚回來,大發神經,寫了篇概括程式設計師生活現狀的文章,沒想到招來眾多人的口誅筆伐,大有上升到政治層面的趨勢。 我也許不會再發表任何衝擊心靈的文章,我希望給大家帶來更多的正能量,所以那篇文章已被我刪除。 我的本意只是想讓各位看過文章之後能冷靜地思考自己的程式人生,不管是對是錯,人都有選擇的權力,走

信7.0大改版UI設計分析

12月21日,微信在APP Store釋出了iOS V7.0版本,版本號從 6.7.5迭代到了 7.0,而6.0 的釋出是在2014年。本次更新是重大版本更新,icon的改變,介面全新改版,新增「時刻視訊」,「錢包」升級為「支付」,公眾號「點贊」變成了「好看」,並且可以在「看一看」入口發現等,可謂產品經理煞費

信網頁開發快取問題

通過網上搜索,得到以下幾種解決方法: 1.在使用window.location.href跳轉頁面時,在url後面加上“?datetime=”+new Date().getTime();保證每次瀏覽的網頁是最新的。 2.js或css更新後,在html引用的地方更新版本號,例如scr="../js/demo.js

.net core 服務日誌落盤設計

oss val www. strong 導致 在線 ron 計算器 name 原文:.net core 微服務之日誌落盤設計 目錄1、設計目標2、日誌流程3、串聯請求事務3.1