當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]這篇想聊的話題是:「分布式多級(jí)緩存架構(gòu)的終章」,如何解決大流量、高并發(fā)這樣的業(yè)務(wù)場(chǎng)景,取決于你能不能成為這個(gè)領(lǐng)域金字塔上層的高手?

這篇想聊的話題是:「分布式多級(jí)緩存架構(gòu)的終章」,如何解決大流量、高并發(fā)這樣的業(yè)務(wù)場(chǎng)景,取決于你能不能成為這個(gè)領(lǐng)域金字塔上層的高手? 能不能把這個(gè)問題思考清楚決定了你的成長(zhǎng)速度。

很多人在一個(gè)行業(yè)5年、10年,依然未達(dá)到這個(gè)行業(yè)的中層甚至還停留在底層,因?yàn)樗麄儚膩聿魂P(guān)心這樣的話題。作為砥礪前行的踐行者,我覺得有必要給大家來分享一下。
關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

開篇

服務(wù)端緩存是整個(gè)緩存體系中的重頭戲,從開始的網(wǎng)站架構(gòu)演進(jìn)中,想必你已看到服務(wù)端緩存在系統(tǒng)性能的重要性。

但數(shù)據(jù)庫(kù)確是整個(gè)系統(tǒng)中的“半吊子|慢性子”,有時(shí)數(shù)據(jù)庫(kù)調(diào)優(yōu)卻能夠以小搏大,在不改變架構(gòu)和代碼邏輯的前提下,緩存參數(shù)的調(diào)整往往是條捷徑。

在系統(tǒng)開發(fā)的過程中,可直接在平臺(tái)側(cè)使用緩存框架,當(dāng)緩存框架無法滿足系統(tǒng)對(duì)性能的要求時(shí),就需要在應(yīng)用層自主開發(fā)應(yīng)用級(jí)緩存。

緩存常用的就是Redis這東西,那到底什么是平臺(tái)級(jí)、應(yīng)用級(jí)緩存呢?

后面給大家揭曉。但有一點(diǎn)可表明,「平臺(tái)級(jí)就是你所選擇什么開發(fā)語(yǔ)言來實(shí)現(xiàn)緩存」,而應(yīng)用級(jí)緩存,則是通過應(yīng)用程序來達(dá)到目的。

01數(shù)據(jù)庫(kù)緩存

為何說數(shù)據(jù)庫(kù)是“慢性子”呢?對(duì)現(xiàn)在喜歡「快」的你來說,慢是解決不了問題的。就好像總感覺感覺妹子回復(fù)慢

因?yàn)閿?shù)據(jù)庫(kù)屬于「IO密集型應(yīng)」用,「主要負(fù)責(zé)數(shù)據(jù)的管理及存儲(chǔ)」。數(shù)據(jù)一多查詢本身就有可能變慢, 這也是為啥數(shù)據(jù)上得了臺(tái)面時(shí),查詢愛用索引提速的原因。當(dāng)然數(shù)據(jù)庫(kù)自身也有“緩存”來解決這個(gè)問題。

數(shù)據(jù)多了查詢不應(yīng)該都慢嗎?小白說吒吒輝你不懂額

。。。這個(gè),你說的也不全是,還得分情況。例如:數(shù)據(jù)有上億行

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

「原因:」

  1. 因?yàn)楹?jiǎn)單的SQL的結(jié)果不會(huì)特別多。你請(qǐng)求也不大,磁盤跟的上
  2. 并發(fā)總量超過磁盤吞吐上限,是誰都沒招

就算你們不喜歡吒吒輝,我也要奮筆疾書

數(shù)據(jù)庫(kù)緩存是自身一類特殊的緩存機(jī)制。大多數(shù)數(shù)據(jù)庫(kù)不需要配置就可以快速運(yùn)行,但并沒有為特定的需求進(jìn)行優(yōu)化。在數(shù)據(jù)庫(kù)調(diào)優(yōu)的時(shí)候,緩存優(yōu)化你可以考慮下。

以MySQL為例,MySQL中使用了查詢緩沖機(jī)制,將SELECT語(yǔ)句和查詢結(jié)果存放在緩沖區(qū)中,以鍵值對(duì)的形式存儲(chǔ)。以后對(duì)于同樣的SELECT語(yǔ)句,將直接從緩沖區(qū)中讀取結(jié)果,以節(jié)省查詢時(shí)間,提高了SQL查詢的效率。

1.1.MySQL查詢緩存

Query cache作用于整個(gè)MySQL實(shí)例,「主要用于緩存MySQL中的ResultSet」,也就是一條SQL語(yǔ)句執(zhí)行的結(jié)果集,所以它只針對(duì)select語(yǔ)句。

當(dāng)打開 Query Cache 功能,MySQL在接收到一條select語(yǔ)句的請(qǐng)求后,如果該語(yǔ)句滿足Query Cache的條件,MySQL會(huì)直接根據(jù)預(yù)先設(shè)定好的HASH算法將接收到的select語(yǔ)句以字符串方式進(jìn)行 hash,然后到Query Cache中直接查找是否已經(jīng)緩存。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

如果結(jié)果集已經(jīng)在緩存中,該select請(qǐng)求就會(huì)直接將數(shù)據(jù)返回,從而省略后面所有的步驟(如SQL語(yǔ)句的解析,優(yōu)化器優(yōu)化以及向存儲(chǔ)引擎請(qǐng)求數(shù)據(jù)等),從而極大地提高了性能。

當(dāng)然,若數(shù)據(jù)變化非常頻繁的情況下,使用Query Cache可能會(huì)得不償失。

這是為啥,用它不是提速嗎?咋還得不償失

因?yàn)镸ySQL只要涉及到數(shù)據(jù)更改,就會(huì)重新維護(hù)緩存。

  1. 如果SQL請(qǐng)求量比較大,你在維護(hù)的時(shí)候,就透過緩存走磁盤檢索。這樣數(shù)據(jù)庫(kù)的壓力肯定大。
  2. 重建緩存數(shù)據(jù),它需要mysql后臺(tái)線程來工作。也會(huì)增加數(shù)據(jù)庫(kù)的負(fù)載。

所以在MySQL8已經(jīng)取消了它。故一般在讀多寫少,數(shù)據(jù)不怎么變化的場(chǎng)景可用它,例如:博客

Query Cache使用需要多個(gè)參數(shù)配合,其中最為關(guān)鍵的是query_cache_size和query_cache_type, 前者用于設(shè)置緩存ResultSet的內(nèi)存大小,后者設(shè)置在何種場(chǎng)景下使用Query Cache。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

這樣可以通過計(jì)算Query Cache的命中率來進(jìn)行調(diào)整緩存大小。

1.2.檢驗(yàn)Query Cache的合理性

檢查Query Cache設(shè)置的是否合理,可以通過在MySQL控制臺(tái)執(zhí)行以下命令觀察:

  • SHOW VARIABLES LIKE '%query_cache%';
  • SHOW STATUS LIKE 'Qcache%';  通過檢查以下幾個(gè)參數(shù)可以知道query_cache_size設(shè)置得是否合理:
    • Qcache_inserts:表示Cache多少次未命中然后插入到緩存
    • Qcache_hits: 表示命中多少次,它可反映出緩存的使用效果。

如果Qcache_hits的值非常大,則表明查詢緩沖使用非常頻繁,如果該值較小反而會(huì)影響效率,那么可以考慮不用查詢緩存;

  • Qcache_lowmem_prunes: 表示多少條Query因?yàn)閮?nèi)存不足而被清除出Query_Cache。

如果Qcache_lowmem_prunes的值非常大,則表明經(jīng)常出現(xiàn)緩沖不夠的情況,因增加緩存容量。

  • Qcache_free_blocks:表示緩存區(qū)的碎片

Qcache_free_blocks值非常大,則表明緩存區(qū)中的碎片很多,可能需要尋找合適的機(jī)會(huì)進(jìn)行整理。

通過 「Qcache_hits」「Qcache_inserts」 兩個(gè)參數(shù)可以算出Query Cache的命中率:

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

通過 Qcache_lowmem_prunes 和 Qcache_free_memory 相互結(jié)合,能更清楚地了解到系統(tǒng)中Query Cache的內(nèi)存大小是否真的足夠,是否頻繁的出現(xiàn)因內(nèi)存不足而有Query被換出的情況。

1.3.InnoDB的緩存性能

當(dāng)選擇 InnoDB 時(shí),「innodb_buffer_pool_size」 參數(shù)可能是影響性能的最為關(guān)鍵的一個(gè)參數(shù),它用來設(shè)置緩存「InnoDB索引及數(shù)據(jù)塊、自適應(yīng)HASH、寫緩沖等內(nèi)存區(qū)域大小」,更像是Oracle數(shù)據(jù)庫(kù)的 db_cache_size。

簡(jiǎn)單來說,當(dāng)操作InnoDB表的時(shí)候,「返回的所有數(shù)據(jù)或者查詢過程中用到的任何一個(gè)索引塊,都會(huì)在這個(gè)內(nèi)存區(qū)域中去查詢一遍」

「MyISAM引擎中的 key_buffer_size」 一樣,innodb_buffer_pool_size設(shè)置了 InnoDB 引擎需求最大的一塊內(nèi)存區(qū)域,直接關(guān)系到InnoDB存儲(chǔ)引擎的性能,所以如果有足夠的內(nèi)存,盡可將該參數(shù)設(shè)置到足夠大,將盡可能多的InnoDB的索引及數(shù)據(jù)都放入到該緩存區(qū)域中,直至全部。

說到緩存肯定少不了,緩存命中率。那innodb該如何計(jì)算?

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

計(jì)算出緩存命中率后,在根據(jù)命中率來對(duì)innodb_buffer_pool_size 參數(shù)大小進(jìn)行優(yōu)化

「除開查詢緩存。數(shù)據(jù)庫(kù)查詢的性能也與MySQL的連接數(shù)有關(guān)」

table_cache 用于設(shè)置 table 高速緩存的數(shù)量。

show global status like 'open%_tables'; # 查看參數(shù)

由于每個(gè)客戶端連接都會(huì)至少訪問一個(gè)表,因此該參數(shù)與max_connections有關(guān)。當(dāng)某一連接訪問一個(gè)表時(shí),MySQL會(huì)檢查當(dāng)前已緩存表的數(shù)量。

如果該表已經(jīng)在緩存中打開,則會(huì)直接訪問緩存中的表以加快查詢速度;如果該表未被緩存,則會(huì)將當(dāng)前的表添加進(jìn)緩存在進(jìn)行查詢。

在執(zhí)行緩存操作之前,table_cache參數(shù)用于限制緩存表的最大數(shù)目:

如果當(dāng)前已經(jīng)緩存的表未達(dá)到table_cache數(shù)目,則會(huì)將新表添加進(jìn)來;若已經(jīng)達(dá)到此值,MySQL將根據(jù)緩存表的最后查詢時(shí)間、查詢率等規(guī)則釋放之前的緩存。

02平臺(tái)級(jí)緩存

什么是平臺(tái)級(jí)緩存,說的這個(gè)玄乎?

平臺(tái)級(jí)緩存是指你所用什么開發(fā)語(yǔ)言,具體選擇的是那個(gè)平臺(tái),畢竟緩存本身就是提供給上層調(diào)用。主要針對(duì)帶有緩存特性的應(yīng)用框架,或者可用于緩存功能的專用庫(kù)。

如:

  • PHP中的Smarty模板庫(kù)
  • Java中,緩存框架更多,如Ehcache,Cacheonix,Voldemort,JBoss Cache,OSCache等等。

Ehcache是現(xiàn)在最流行的純Java開源緩存框架,配置簡(jiǎn)單、結(jié)構(gòu)清晰、功能強(qiáng)大,是從hibernate的緩存開始被廣泛使用起來的。EhCache有如下特點(diǎn):

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

Ehcache的系統(tǒng)結(jié)構(gòu)如圖所示:

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

什么是分布式緩存呢?好像我還沒搞明白,小吒哥

首先得看看恒古不變的“分布式”,即它是獨(dú)立的部署到多個(gè)服務(wù)節(jié)點(diǎn)上或者獨(dú)立的進(jìn)程,彼此之間僅僅通過消息傳遞進(jìn)行通信和協(xié)調(diào)。

「也就是說分布式緩存,它要么是在單機(jī)上有多個(gè)實(shí)例,要么就獨(dú)立的部署到不同服務(wù)器,從而把緩存分散到各處」

最后通過客戶端連接到對(duì)應(yīng)的節(jié)點(diǎn)來進(jìn)行緩存操作。

Voldemort是一款基于Java開發(fā)的分布式鍵-值緩存系統(tǒng),像JBoss的緩存一樣,Voldemort同樣支持多臺(tái)服務(wù)器之間的緩存同步,以增強(qiáng)系統(tǒng)的可靠性和讀取性能。

Voldemort有如下特點(diǎn):

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了
Voldemort的邏輯架構(gòu)圖
關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

Voldemort相當(dāng)于是Amazon Dynamo的一個(gè)開源實(shí)現(xiàn),LinkedIn用它解決了網(wǎng)站的高擴(kuò)展性存儲(chǔ)問題。

簡(jiǎn)單來說,就平臺(tái)級(jí)緩存而言,只需要在框架側(cè)配置一下屬性即可,而不需要調(diào)用特定的方法或函數(shù)。

系統(tǒng)中引入緩存技術(shù)往往就是從平臺(tái)級(jí)緩存開始,平臺(tái)級(jí)緩存也通常會(huì)作為一級(jí)緩存使用。

既然平臺(tái)級(jí)緩存都使用框架配置來實(shí)現(xiàn),這咋實(shí)現(xiàn)緩存的分布式呢?節(jié)點(diǎn)之間都沒有互相的消息通訊了

如果單看,框架緩存的調(diào)用,那確實(shí)沒辦法做到分布式緩存,因?yàn)樽陨頉]得像Redis那樣分布式的部署方式,通過網(wǎng)絡(luò)把各節(jié)點(diǎn)連接 。
但本地平臺(tái)緩存可通過遠(yuǎn)程過程調(diào)用,來操作分布在各個(gè)節(jié)點(diǎn)上的平臺(tái)緩存數(shù)據(jù)。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

在 Ehcache 中:

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

03應(yīng)用級(jí)緩存

當(dāng)平臺(tái)級(jí)緩存不能滿足系統(tǒng)的性能時(shí),就要考慮使用應(yīng)用級(jí)緩存。應(yīng)用級(jí)緩存,需要開發(fā)者通過代碼來實(shí)現(xiàn)緩存機(jī)制。

有些許 一方有難,八方支援 的感覺。自己搞不定 ,請(qǐng)教別人

這是NoSQL的戰(zhàn)場(chǎng),不論是Redis還是MongoDB,以及Memcached都可作為應(yīng)用級(jí)緩存的技術(shù)支持。
「一種典型的方式是每分鐘或一段時(shí)間后統(tǒng)一生成某類頁(yè)面存儲(chǔ)在緩存中,或者可以在熱數(shù)據(jù)變化時(shí)更新緩存。」

為啥平臺(tái)緩存還不能滿足系統(tǒng)性能要求呢?它不是還可以減少應(yīng)用緩存的網(wǎng)絡(luò)開銷嗎 那你得看這幾點(diǎn):

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

3.1面向Redis的緩存應(yīng)用

Redis是一款開源的、基于BSD許可的高級(jí)鍵值對(duì)緩存和存儲(chǔ)系統(tǒng),例如:新浪微博有著幾乎世界上最大的Redis集群。

為何新浪微博是世界上最大的Redis集群呢?

微博是一個(gè)社交平臺(tái),其中用戶關(guān)注與被關(guān)注、微博熱搜榜、點(diǎn)擊量、高可用、緩存穿透等業(yè)務(wù)場(chǎng)景和技術(shù)問題。Redis都有對(duì)應(yīng)的hash、ZSet、bitmap、cluster等技術(shù)方案來解決。

在這種數(shù)據(jù)關(guān)系復(fù)雜、易變化的場(chǎng)景上面用到它會(huì)顯得很簡(jiǎn)單。比如:

「用戶關(guān)注與取消」:用hash就可以很方便的維護(hù)用戶列表,你可以直接找到key,然后更改value里面的關(guān)注用戶即可。

如果你像  memcache ,那只能先序列化好用戶關(guān)注列表存儲(chǔ),更改在反序列化。然后再緩存起來,像大V有幾百萬、上千萬的用戶,一旦關(guān)注/取消。當(dāng)前任務(wù)的操作就會(huì)有延遲。

Reddis主要功能特點(diǎn)

  • 主從同步
    Redis支持主從同步,數(shù)據(jù)可以從主服務(wù)器向任意數(shù)量的從服務(wù)器同步, 「從服務(wù)器可做為」關(guān)聯(lián)其他從服務(wù)器的主服務(wù)器。這使得Redis可執(zhí)行單層樹狀復(fù)制。
關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了
  • 發(fā)布/訂閱
    由于實(shí)現(xiàn)了 「發(fā)布/訂閱機(jī)制」,使得從服務(wù)器在任何地方同步樹的時(shí)候,可訂閱一個(gè)頻道并接收主服務(wù)器完整的消息發(fā)布記錄。同步對(duì)讀取操作的可擴(kuò)展性和數(shù)據(jù)冗余很有幫助。
關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了
  • 集群
    Redis 3.0版本加入cluster功能, 「解決了Redis單點(diǎn)無法橫向擴(kuò)展的問題」。Redis集群采用無中心節(jié)點(diǎn)方式實(shí)現(xiàn),無需proxy代理,客戶端直接與Redis集群的每個(gè)節(jié)點(diǎn)連接,根據(jù)同樣的哈希算法計(jì)算出key對(duì)應(yīng)的slot,然后直接在slot對(duì)應(yīng)的Redis上執(zhí)行命令。

從Redis視角來看,響應(yīng)時(shí)間是最苛刻的條件,增加一層帶來的開銷是不能接受的。因此,Redis實(shí)現(xiàn)了客戶端對(duì)節(jié)點(diǎn)的直接訪問,為了去中心化,節(jié)點(diǎn)之間通過Gossip協(xié)議交換相互的狀態(tài),以及探測(cè)新加入的節(jié)點(diǎn)信息。Redis集群支持動(dòng)態(tài)加入節(jié)點(diǎn),動(dòng)態(tài)遷移slot,以及自動(dòng)故障轉(zhuǎn)移。

Redis集群的架構(gòu)示意如圖所示。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

那什么是 Gossip 協(xié)議呢?感覺好高大上,各種協(xié)議頻繁出現(xiàn)

Gossip 協(xié)議是一個(gè)多播協(xié)議,基本思想是:
一個(gè)節(jié)點(diǎn)想要分享一些信息給網(wǎng)絡(luò)中的其他的一些節(jié)點(diǎn)。于是,它周期性的隨機(jī)選擇一些節(jié)點(diǎn),并把信息傳遞給這些節(jié)點(diǎn)。這些收到信息的節(jié)點(diǎn)接下來會(huì)做同樣的事情,即把這些信息傳遞給其他一些隨機(jī)選擇的節(jié)點(diǎn)。直至全部的節(jié)點(diǎn)。

即,Redis集群中添加、剔除、選舉主節(jié)點(diǎn),都是基于這樣的方式。

例如:當(dāng)加入新節(jié)點(diǎn)時(shí)(meet),集群中會(huì)隨機(jī)選擇一個(gè)節(jié)點(diǎn)來邀請(qǐng)「新節(jié)點(diǎn)」,此時(shí)只有邀請(qǐng)節(jié)點(diǎn)和被邀請(qǐng)節(jié)點(diǎn)知道這件事,其余節(jié)點(diǎn)要等待 ping 消息一層一層擴(kuò)散。除了 Fail 是立即全網(wǎng)通知的,其他諸如新節(jié)點(diǎn)、節(jié)點(diǎn)重上線、從節(jié)點(diǎn)選舉成為主節(jié)點(diǎn)、槽變化等,都需要等待被通知到,所以Gossip協(xié)議也是最終一致性的協(xié)議。

這種多播的方式,是不是忽然有種好事不出門,壞事傳千里的感腳

然而,Gossip協(xié)議也有不完美的地方,例如,拜占庭問題(Byzantine)。即,如果有一個(gè)惡意傳播消息的節(jié)點(diǎn),Gossip協(xié)議的分布式系統(tǒng)就會(huì)出問題。

注:Redis集群節(jié)點(diǎn)通信消息類型

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

所有的Redis節(jié)點(diǎn)通過PING-PONG機(jī)制彼此互聯(lián),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。

這個(gè)ping為啥能提高傳輸速度和帶寬?感覺不大清楚,小吒哥。那這里和OSI網(wǎng)絡(luò)層級(jí)模式有關(guān)系了

在OSI網(wǎng)絡(luò)層級(jí)模型下,ping協(xié)議隸屬網(wǎng)絡(luò)層,所以它會(huì)減少網(wǎng)絡(luò)層級(jí)傳輸?shù)拈_銷,而二進(jìn)制是用最小單位0,1表示的位。

帶寬是固定的,如果你發(fā)送的數(shù)據(jù)包都很小,那傳輸就很快,并不會(huì)出現(xiàn)數(shù)據(jù)包很大還要拆包等復(fù)雜工作。
相當(dāng)于別人出差1斤多MacPro。你出差帶5斤的戰(zhàn)神電腦。

Redis的瓶頸是什么呢?  吒吒輝給安排

Redis本身就是內(nèi)存數(shù)據(jù)庫(kù),讀寫I/O是它的強(qiáng)項(xiàng),瓶頸就在單線程I/O上與內(nèi)存的容量上。目前已經(jīng)有多線程了,

例如:Redis6具備網(wǎng)絡(luò)傳輸?shù)亩嗑€程模式,keydb直接就是多線程。
啥?還沒了解多Redis6多線程模式,后面單獨(dú)搞篇來聊聊

集群節(jié)點(diǎn)故障如何發(fā)現(xiàn)?

「節(jié)點(diǎn)故障」是通過「集群中超過半數(shù)的節(jié)點(diǎn)檢測(cè)失效時(shí)才會(huì)生效」??蛻舳伺cRedis節(jié)點(diǎn)直連,客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可。

Redis Cluster把所有的物理節(jié)點(diǎn)映射到slot上,cluster負(fù)責(zé)維護(hù)node、slot和value的映射關(guān)系。當(dāng)節(jié)點(diǎn)發(fā)生故障時(shí),選舉過程是集群中所有master參與的,如果半數(shù)以上master節(jié)點(diǎn)與當(dāng)前master節(jié)點(diǎn)間的通信超時(shí),則認(rèn)為當(dāng)前master節(jié)點(diǎn)掛掉。

這為何不沒得Slave節(jié)點(diǎn)參與呢?

集群模式下,請(qǐng)求在集群模式下會(huì)自動(dòng)做到讀寫分離,即讀從寫主。但現(xiàn)在是選擇主節(jié)點(diǎn)。只能由主節(jié)點(diǎn)來進(jìn)行身份參與。

畢竟集群模式下,主節(jié)點(diǎn)有多個(gè),每個(gè)從節(jié)點(diǎn)只對(duì)應(yīng)一個(gè)主節(jié)點(diǎn),那這樣,你別個(gè)家的從節(jié)點(diǎn)能夠參與選舉整個(gè)集群模式下的主節(jié)點(diǎn)嗎?

就好像小姐姐有了對(duì)象,那就是名花有主,你還能在有主的情況下,去選一個(gè)?小心遭到社會(huì)的毒打

如果集群中超過半數(shù)以上master節(jié)點(diǎn)掛掉,無論是否有slave集群,Redis的整個(gè)集群將處于不可用狀態(tài)。

當(dāng)集群不可用時(shí),所有對(duì)集群的操作都不可用,都將收到錯(cuò)誤信息:

[(error)CLUSTERDOWN The cluster is down]。

支持Redis的客戶端編程語(yǔ)言眾多,可以滿足絕大多數(shù)的應(yīng)用,如圖所示。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

3.2.多級(jí)緩存實(shí)例

一個(gè)使用了Redis集群和其他多種緩存技術(shù)的應(yīng)用系統(tǒng)架構(gòu)如圖所示

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

負(fù)載均衡

首先,用戶的請(qǐng)求被負(fù)載均衡服務(wù)分發(fā)到Nginx上,此處常用的負(fù)載均衡算法是輪詢或者一致性哈希,輪詢可以使服務(wù)器的請(qǐng)求更加均衡,而一致性哈??梢蕴嵘齆ginx應(yīng)用的緩存命中率。

什么是一致性hash算法?

hash算法計(jì)算出的結(jié)果值本身就是唯一的,這樣就可以讓每個(gè)用戶的請(qǐng)求都落到同一臺(tái)服務(wù)器。
默認(rèn)情況下,用戶在那臺(tái)在服務(wù)器登錄,就生成會(huì)話session文件到該服務(wù)器,但如果下次請(qǐng)求重新分發(fā)給其他服務(wù)器就又需要重新登錄。

而有了一致性hash算法就可以治愈它,它把請(qǐng)求都專心交給同一臺(tái)服務(wù)器,鐵打的專一,從而避免上述問題。 當(dāng)然這里的一致性hash原理就沒給大家講了。后面安排

nginx本地緩存

請(qǐng)求進(jìn)入到Nginx應(yīng)用服務(wù)器,首先讀取本地緩存,實(shí)現(xiàn)本地緩存的方式可以是Lua Shared Dict,或者面向磁盤或內(nèi)存的 Nginx Proxy Cache,以及本地的Redis實(shí)現(xiàn)等,如果本地緩存命中則直接返回。

這本地緩存怎么感覺那么特別呢?好像你家附近的小姐姐,離得這么近,可惜吃不著。呸呸呸,跑題啦

  • Lua Shard Dict是指在nginx上,通過lua開辟一塊內(nèi)存空間來存儲(chǔ)緩存數(shù)據(jù)。相當(dāng)于用的是nginx的進(jìn)程資源
  • nginx Cache指nginx獲取上游服務(wù)的數(shù)據(jù)緩存到本地。
  • 本地Redis指nginx和Redis部署在同一臺(tái)服務(wù)上,由nginx直接操作Redis

啥!nginx還可直接操作Redis呀,聽我細(xì)細(xì)到來

這些方式各有千秋,Lua Shard Dict 是通過Lua腳本控制緩存數(shù)據(jù)的大小并可以靈活的通過邏輯處理來修改相關(guān)緩存數(shù)據(jù)。

而Nginx Proxy Cache開發(fā)相對(duì)簡(jiǎn)單,就是獲取上游數(shù)據(jù)到本地緩存處理。而「本地Redis則需要通過lua腳本編寫邏輯來設(shè)置」,雖然操作繁瑣了,但解決了本地內(nèi)存局限的問題。
所以nginx操作Redis是需要借助于 Lua 噠

nginx本地緩存有什么優(yōu)點(diǎn)?

Nginx應(yīng)用服務(wù)器使用本地緩存可以提升整體的吞吐量,降低后端的壓力,尤其應(yīng)對(duì)熱點(diǎn)數(shù)據(jù)的反復(fù)讀取問題非常有效。

本地緩存未命中時(shí)如何解決?

如果Nginx應(yīng)用服務(wù)器的本地緩存沒有命中,就會(huì)進(jìn)一步讀取相應(yīng)的分布式緩存——Redis分布式緩存的集群,可以考慮使用主從架構(gòu)來提升性能和吞吐量,如果分布式緩存命中則直接返回相應(yīng)數(shù)據(jù),并回寫到Nginx應(yīng)用服務(wù)器的本地緩存中。

如果Redis分布式緩存也沒有命中,則會(huì)回源到Tomcat集群,在回源到Tomcat集群時(shí)也可以使用輪詢和一致性哈希作為負(fù)載均衡算法。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

那我是PHP技術(shù)棧咋辦?都不會(huì)用到j(luò)ava的Tomcat呀
nginx常用于反向代理層。而這里的Tomcat更多是屬于應(yīng)用服務(wù)器,如果換成PHP,那就由php-fpm或者swoole服務(wù)來接受請(qǐng)求。即不管什么語(yǔ)言,都應(yīng)該找對(duì)應(yīng)語(yǔ)言接受請(qǐng)求分發(fā)的東西。

當(dāng)然,如果Redis分布式緩存沒有命中的話,Nginx應(yīng)用服務(wù)器還可以再嘗試一次讀主Redis集群操作,目的是防止當(dāng)從Redis集群有問題時(shí)可能發(fā)生的流量沖擊。

這樣的設(shè)計(jì)方案我在下表示看不懂

如果你網(wǎng)站流量比較大,如果一次在Redis分布式緩存中未讀取到的話,直接透過到數(shù)據(jù)庫(kù),那流量可能會(huì)把數(shù)據(jù)庫(kù)沖垮。這里的一次讀主也是考慮到Redis集群中的主從延遲問題,為的就是防止緩存擊穿。

在Tomcat | PHP-FPM集群應(yīng)用中,首先讀取本地平臺(tái)級(jí)緩存,如果平臺(tái)級(jí)緩存命中則直接返回?cái)?shù)據(jù),并會(huì)同步寫到主Redis集群,在由主從同步到從Redis集群。

此處可能存在多個(gè)Tomcat實(shí)例同時(shí)寫主Redis集群的情況,可能會(huì)造成數(shù)據(jù)錯(cuò)亂,需要注意緩存的更新機(jī)制和原子化操作。

如何保證原子化操作執(zhí)行呢?

當(dāng)多個(gè)實(shí)例要同時(shí)要寫Redis緩存時(shí),為了保持原子化,起碼得在涉及這塊業(yè)務(wù)多個(gè)的 Key 上采用lua腳本進(jìn)行封裝,然后再通過分布式鎖或去重相同請(qǐng)求并入到一個(gè)隊(duì)列來獲取,讓獲取到鎖或從隊(duì)列pop的請(qǐng)求去讀取Redis集群中的數(shù)據(jù)。

如果所有緩存都沒有命中,系統(tǒng)就只能查詢數(shù)據(jù)庫(kù)或其他相關(guān)服務(wù)獲取相關(guān)數(shù)據(jù)并返回,當(dāng)然,我們已經(jīng)知道數(shù)據(jù)庫(kù)也是有緩存的。是不是安排得明明白白。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

這就是多級(jí)緩存的使用,才能保障系統(tǒng)具備優(yōu)良的性能。

什么時(shí)候,小姐姐也能明白俺的良苦心。。。。默默的獨(dú)自流下了淚水

3.3.緩存算法

緩存一般都會(huì)采用內(nèi)存來做存儲(chǔ)介質(zhì),使用索引成本相對(duì)來說還是比較高的。所以在使用緩存時(shí),需要了解緩存技術(shù)中的幾個(gè)術(shù)語(yǔ)。

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

緩存淘汰算法

替代策略的具體實(shí)現(xiàn)就是緩存淘汰算法。

使用頻率:
關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了
  1. Least-Recently-Used(LRU) 替換掉最近被請(qǐng)求最少的對(duì)象。

在CPU緩存淘汰和虛擬內(nèi)存系統(tǒng)中效果很好。然而在直接應(yīng)用與代理緩存中效果欠佳,因?yàn)閃eb訪問的時(shí)間局部性常常變化很大。
瀏覽器就一般使用了LRU作為緩存算法。新的對(duì)象會(huì)被放在緩存的頂部,當(dāng)緩存達(dá)到了容量極限,底部的對(duì)象被去除,方法就是把最新被訪問的緩存對(duì)象放到緩存池的頂部。

  1. Least-Frequently-Used(LFU) 替換掉訪問次數(shù)最少的緩存,這一策略意圖是保留最常用的、最流行的對(duì)象,替換掉很少使用的那些數(shù)據(jù)。

然而,有的文檔可能有很高的使用頻率,但之后再也不會(huì)用到。傳統(tǒng)的LFU策略沒有提供任何移除這類文件的機(jī)制,因此會(huì)導(dǎo)致“緩存污染”,即一個(gè)先前流行的緩存對(duì)象會(huì)在緩存中駐留很長(zhǎng)時(shí)間,這樣,就阻礙了新進(jìn)來可能會(huì)流行的對(duì)象對(duì)它的替代。

  1. Pitkow/Recker 替換最近最少使用的對(duì)象

除非所有對(duì)象都是今天訪問過的。如果是這樣,則替換掉最大的對(duì)象。這一策略試圖符合每日訪問Web網(wǎng)頁(yè)的特定模式。這一策略也被建議在每天結(jié)束時(shí)運(yùn)行,以釋放被“舊的”、最近最少使用的對(duì)象占用的空間。

  1. Adaptive Replacement Cache(ARC) ARC介于LRU和LFU之間,為了提高效果,由2個(gè)LRU組成。

第一個(gè)包含的條目是最近只被使用過一次的,而第二個(gè)LRU包含的是最近被使用過兩次的條目,因此,得到了新的對(duì)象和常用的對(duì)象。ARC能夠自我調(diào)節(jié),并且是低負(fù)載的。

  1. Most Recently Used(MRU) MRU與LRU是相對(duì),移除最近最多被使用的對(duì)象。

當(dāng)一次訪問過來的時(shí)候,有些事情是無法預(yù)測(cè)的,并且在存系統(tǒng)中找出最少最近使用的對(duì)象是一項(xiàng)時(shí)間復(fù)雜度非常高的運(yùn)算,這時(shí)會(huì)考慮MRU,在數(shù)據(jù)庫(kù)內(nèi)存緩存中比較常見。

訪問計(jì)數(shù)
  1. Least Recently Used2  (LRU2)

LRU的變種,把被兩次訪問過的對(duì)象放入緩存池,當(dāng)緩存池滿了之后,會(huì)把有兩次最少使用的緩存對(duì)象去除。

因?yàn)樾枰檶?duì)象2次,訪問負(fù)載就會(huì)隨著緩存池的增加而增加。

  1. Two Queues(2Q) Two Queues是LRU的另一個(gè)變種。

把被訪問的數(shù)據(jù)放到LRU的緩存中,如果這個(gè)對(duì)象再一次被訪問,就把他轉(zhuǎn)移到第二個(gè)、更大的LRU緩存,使用了多級(jí)緩存的方式。去除緩存對(duì)象是為了保持第一個(gè)緩存池是第二個(gè)緩存池的1/3。

當(dāng)緩存的訪問負(fù)載是固定的時(shí)候,把LRU換成LRU2,就比增加緩存的容量更好。

緩存容量算法
  1. SIZE 替換占用空間最大的對(duì)象,這一策略通過淘汰一個(gè)大對(duì)象而不是多個(gè)小對(duì)象來提高命中率。不過,可能有些進(jìn)入緩存的小對(duì)象永遠(yuǎn)不會(huì)再被訪問。SIZE策略沒有提供淘汰這類對(duì)象的機(jī)制,也會(huì)導(dǎo)致“緩存污染”。

  2. LRU-Threshold 不緩存超過某一size的對(duì)象,其他與LRU相同。

  3. Log(Size)+LRU 替換size最大的對(duì)象,當(dāng)size相同時(shí),按LRU進(jìn)行替換。

緩存時(shí)間
  1. Hyper-G LFU的改進(jìn)版,同時(shí)考慮上次訪問時(shí)間和對(duì)象size。

  2. Lowest-Latency-First 替換下載時(shí)間最少的文檔。顯然它的目標(biāo)是最小化平均延遲。

緩存評(píng)估
  1. Hybrid Hybrid 有一個(gè)目標(biāo)是減少平均延遲。

對(duì)緩存中的每個(gè)文檔都會(huì)計(jì)算一個(gè)保留效用,保留效用最低的對(duì)象會(huì)被替換掉。位于服務(wù)器S的文檔f的效用函數(shù)定義如下:

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

Cs是與服務(wù)器s的連接時(shí)間;
bs是服務(wù)器s的帶寬;frf代表f的使用頻率;sizef是文檔f的大小,單位字節(jié)。K1和K2是常量,Cs和bs是根據(jù)最近從服務(wù)器s獲取文檔的時(shí)間進(jìn)行估計(jì)的。

  1. Lowest Relative Value(LRV) LRV也是基于計(jì)算緩存中文檔的保留效用,然后替換保留效用最低的文檔。
隨機(jī)與隊(duì)列算法
  1. First in First out(FIFO)

FIFO通過一個(gè)隊(duì)列去跟蹤所有的緩存對(duì)象,最近最常用的緩存對(duì)象放在后面,而更早的緩存對(duì)象放在前面,當(dāng)緩存容量滿時(shí),排在前面的緩存對(duì)象會(huì)被踢走,然后把新的緩存對(duì)象加進(jìn)去。

  1. Random Cache 隨機(jī)緩存就是隨意的替換緩存數(shù)據(jù),比FIFO機(jī)制好,在某些情況下,甚至比LRU好,但是通常LRU都會(huì)比隨機(jī)緩存更好些。

還有很多的緩存算法,例如Second Chance、Clock、Simple time-based、Extended time-based expiration、Sliding time-based expiration……各種緩存算法沒有優(yōu)劣之分,不同的實(shí)際應(yīng)用場(chǎng)景,會(huì)用到不同的緩存算法。在實(shí)現(xiàn)緩存算法的時(shí)候,通常會(huì)考慮**使用頻率、獲取成本、緩存容量和時(shí)間等因素。 **

04.使用公有云的緩存服務(wù)

關(guān)于分布式多級(jí)緩存架構(gòu),也許你一直考慮的太簡(jiǎn)單了

國(guó)內(nèi)的共有云服務(wù)提供商如阿里云、青云、百度云等都推出了基于Redis的云存儲(chǔ)服務(wù),這些服務(wù)的有如下特點(diǎn):

  • 動(dòng)態(tài)擴(kuò)容:

用戶可以通過控制面板升級(jí)所需Redis的存儲(chǔ)空間,「擴(kuò)容過程中服務(wù)不需要中斷或停止」,整個(gè)擴(kuò)容過程對(duì)用戶是透明且無感知的,而自主使用集群解決Redis平滑擴(kuò)容是個(gè)很煩瑣的任務(wù),現(xiàn)在需要用你的小手按幾下鼠標(biāo)就能搞定,大大減少了運(yùn)維的負(fù)擔(dān)。

  • 數(shù)據(jù)多備:
    數(shù)據(jù)保存在一主一備兩臺(tái)機(jī)器中,其中一臺(tái)機(jī)器宕機(jī)了,數(shù)據(jù)還在另外一臺(tái)機(jī)器上有備份。
  • 自動(dòng)容災(zāi):
    主機(jī)宕機(jī)后系統(tǒng)能自動(dòng)檢測(cè)并切換到備機(jī)上,實(shí)現(xiàn)了服務(wù)的高可用性。
  • 成本較低:
    在很多情況下,為使Redis的性能更好,需要購(gòu)買一臺(tái)專門的服務(wù)器用于Redis的存儲(chǔ)服務(wù),但這樣會(huì)導(dǎo)致某些資源的浪費(fèi),購(gòu)買Redis云存儲(chǔ)服務(wù)就能很好地解決這樣的問題。

有了Redis云存儲(chǔ)服務(wù),能使后臺(tái)開發(fā)人員從煩瑣的運(yùn)維中解放出來。應(yīng)用后臺(tái)服務(wù)中,如果自主搭建一個(gè)高可用、高性能的Redis集群服務(wù),是需要投入相當(dāng)?shù)倪\(yùn)維成本和精力。

如果使用云服務(wù),就沒必要投入這些成本和精力,可以讓后臺(tái)應(yīng)用的開發(fā)人員更專注于業(yè)務(wù)。


免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

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

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉