Web系統(tǒng)的緩存機(jī)制的建立和優(yōu)化

2018-02-24 15:45 更新

Web系統(tǒng)的緩存機(jī)制的建立和優(yōu)化

剛剛我們講完了Web系統(tǒng)的外部網(wǎng)絡(luò)環(huán)境,現(xiàn)在我們開始關(guān)注我們Web系統(tǒng)自身的性能問題。我們的Web站點(diǎn)隨著訪問量的上升,會遇到很多的挑戰(zhàn),解決這些問題不僅僅是擴(kuò)容機(jī)器這么簡單,建立和使用合適的緩存機(jī)制才是根本。

最開始,我們的Web系統(tǒng)架構(gòu)可能是這樣的,每個環(huán)節(jié),都可能只有1臺機(jī)器。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

我們從最根本的數(shù)據(jù)存儲開始看哈。

一、 MySQL數(shù)據(jù)庫內(nèi)部緩存使用

MySQL的緩存機(jī)制,就從先從MySQL內(nèi)部開始,下面的內(nèi)容將以最常見的InnoDB存儲引擎為主。

1. 建立恰當(dāng)?shù)乃饕?/p>

最簡單的是建立索引,索引在表數(shù)據(jù)比較大的時候,起到快速檢索數(shù)據(jù)的作用,但是成本也是有的。首先,占用了一定的磁盤空間,其中組合索引最突出,使用需要謹(jǐn)慎,它產(chǎn)生的索引甚至?xí)仍磾?shù)據(jù)更大。其次,建立索引之后的數(shù)據(jù)insert/update/delete等操作,因為需要更新原來的索引,耗時會增加。當(dāng)然,實(shí)際上我們的系統(tǒng)從總體來說,是以select查詢操作居多,因此,索引的使用仍然對系統(tǒng)性能有大幅提升的作用。

2. 數(shù)據(jù)庫連接線程池緩存

如果,每一個數(shù)據(jù)庫操作請求都需要創(chuàng)建和銷毀連接的話,對數(shù)據(jù)庫來說,無疑也是一種巨大的開銷。為了減少這類型的開銷,可以在MySQL中配置thread_cache_size來表示保留多少線程用于復(fù)用。線程不夠的時候,再創(chuàng)建,空閑過多的時候,則銷毀。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

其實(shí),還有更為激進(jìn)一點(diǎn)的做法,使用pconnect(數(shù)據(jù)庫長連接),線程一旦創(chuàng)建在很長時間內(nèi)都保持著。但是,在訪問量比較大,機(jī)器比較多的情況下,這種用法很可能會導(dǎo)致“數(shù)據(jù)庫連接數(shù)耗盡”,因為建立連接并不回收,最終達(dá)到數(shù)據(jù)庫的max_connections(最大連接數(shù))。因此,長連接的用法通常需要在CGI和MySQL之間實(shí)現(xiàn)一個“連接池”服務(wù),控制CGI機(jī)器“盲目”創(chuàng)建連接數(shù)。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

建立數(shù)據(jù)庫連接池服務(wù),有很多實(shí)現(xiàn)的方式,PHP的話,我推薦使用swoole(PHP的一個網(wǎng)絡(luò)通訊拓展)來實(shí)現(xiàn)。

3. Innodb緩存設(shè)置(innodb_buffer_pool_size)

innodb_buffer_pool_size這是個用來保存索引和數(shù)據(jù)的內(nèi)存緩存區(qū),如果機(jī)器是MySQL獨(dú)占的機(jī)器,一般推薦為機(jī)器物理內(nèi)存的80%。在取表數(shù)據(jù)的場景中,它可以減少磁盤IO。一般來說,這個值設(shè)置越大,cache命中率會越高。

4. 分庫/分表/分區(qū)。

MySQL數(shù)據(jù)庫表一般承受數(shù)據(jù)量在百萬級別,再往上增長,各項性能將會出現(xiàn)大幅度下降,因此,當(dāng)我們預(yù)見數(shù)據(jù)量會超過這個量級的時候,建議進(jìn)行分庫/分表/分區(qū)等操作。最好的做法,是服務(wù)在搭建之初就設(shè)計為分庫分表的存儲模式,從根本上杜絕中后期的風(fēng)險。不過,會犧牲一些便利性,例如列表式的查詢,同時,也增加了維護(hù)的復(fù)雜度。不過,到了數(shù)據(jù)量千萬級別或者以上的時候,我們會發(fā)現(xiàn),它們都是值得的。?

二、 MySQL數(shù)據(jù)庫多臺服務(wù)搭建

1臺MySQL機(jī)器,實(shí)際上是高風(fēng)險的單點(diǎn),因為如果它掛了,我們Web服務(wù)就不可用了。而且,隨著Web系統(tǒng)訪問量繼續(xù)增加,終于有一天,我們發(fā)現(xiàn)1臺MySQL服務(wù)器無法支撐下去,我們開始需要使用更多的MySQL機(jī)器。當(dāng)引入多臺MySQL機(jī)器的時候,很多新的問題又將產(chǎn)生。

1. 建立MySQL主從,從庫作為備份

這種做法純粹為了解決“單點(diǎn)故障”的問題,在主庫出故障的時候,切換到從庫。不過,這種做法實(shí)際上有點(diǎn)浪費(fèi)資源,因為從庫實(shí)際上被閑著了。

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

不過,這種方案,只能用在兩臺機(jī)器的場景。如果業(yè)務(wù)拓展還是很快的話,可以選擇將業(yè)務(wù)分離,建立多個主主互備。

三、 MySQL數(shù)據(jù)庫機(jī)器之間的數(shù)據(jù)同步

每當(dāng)我們解決一個問題,新的問題必然誕生在舊的解決方案上。當(dāng)我們有多臺MySQL,在業(yè)務(wù)高峰期,很可能出現(xiàn)兩個庫之間的數(shù)據(jù)有延遲的場景。并且,網(wǎng)絡(luò)和機(jī)器負(fù)載等,也會影響數(shù)據(jù)同步的延遲。我們曾經(jīng)遇到過,在日訪問量接近1億的特殊場景下,出現(xiàn),從庫數(shù)據(jù)需要很多天才能同步追上主庫的數(shù)據(jù)。這種場景下,從庫基本失去效用了。

于是,解決同步問題,就是我們下一步需要關(guān)注的點(diǎn)。

1. MySQL自帶多線程同步

MySQL5.6開始支持主庫和從庫數(shù)據(jù)同步,走多線程。但是,限制也是比較明顯的,只能以庫為單位。MySQL數(shù)據(jù)同步是通過binlog日志,主庫寫入到binlog日志的操作,是具有順序的,尤其當(dāng)SQL操作中含有對于表結(jié)構(gòu)的修改等操作,對于后續(xù)的SQL語句操作是有影響的。因此,從庫同步數(shù)據(jù),必須走單進(jìn)程。

2. 自己實(shí)現(xiàn)解析binlog,多線程寫入。

以數(shù)據(jù)庫的表為單位,解析binlog多張表同時做數(shù)據(jù)同步。這樣做的話,的確能夠加快數(shù)據(jù)同步的效率,但是,如果表和表之間存在結(jié)構(gòu)關(guān)系或者數(shù)據(jù)依賴的話,則同樣存在寫入順序的問題。這種方式,可用于一些比較穩(wěn)定并且相對獨(dú)立的數(shù)據(jù)表。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

國內(nèi)一線互聯(lián)網(wǎng)公司,大部分都是通過這種方式,來加快數(shù)據(jù)同步效率。還有更為激進(jìn)的做法,是直接解析binlog,忽略以表為單位,直接寫入。但是這種做法,實(shí)現(xiàn)復(fù)雜,使用范圍就更受到限制,只能用于一些場景特殊的數(shù)據(jù)庫中(沒有表結(jié)構(gòu)變更,表和表之間沒有數(shù)據(jù)依賴等特殊表)。?

四、 在Web服務(wù)器和數(shù)據(jù)庫之間建立緩存

實(shí)際上,解決大訪問量的問題,不能僅僅著眼于數(shù)據(jù)庫層面。根據(jù)“二八定律”,80%的請求只關(guān)注在20%的熱點(diǎn)數(shù)據(jù)上。因此,我們應(yīng)該建立Web服務(wù)器和數(shù)據(jù)庫之間的緩存機(jī)制。這種機(jī)制,可以用磁盤作為緩存,也可以用內(nèi)存緩存的方式。通過它們,將大部分的熱點(diǎn)數(shù)據(jù)查詢,阻擋在數(shù)據(jù)庫之前。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

對于使用緩存服務(wù)的客戶端來說,這一切是透明的。

?

內(nèi)存緩存服務(wù)在切換的時候,是有一定風(fēng)險的。從A集群切換到B集群的過程中,必須保證B集群提前做好“預(yù)熱”(B集群的內(nèi)存中的熱點(diǎn)數(shù)據(jù),應(yīng)該盡量與A集群相同,否則,切換的一瞬間大量請求內(nèi)容,在B集群的內(nèi)存緩存中查找不到,流量直接沖擊后端的數(shù)據(jù)庫服務(wù),很可能導(dǎo)致數(shù)據(jù)庫宕機(jī))。

4. 減少數(shù)據(jù)庫“寫”

上面的機(jī)制,都實(shí)現(xiàn)減少數(shù)據(jù)庫的“讀”的操作,但是,寫的操作也是一個大的壓力。寫的操作,雖然無法減少,但是可以通過合并請求,來起到減輕壓力的效果。這個時候,我們就需要在內(nèi)存緩存集群和數(shù)據(jù)庫集群之間,建立一個修改同步機(jī)制。

先將修改請求生效在cache中,讓外界查詢顯示正常,然后將這些sql修改放入到一個隊列中存儲起來,隊列滿或者每隔一段時間,合并為一個請求到數(shù)據(jù)庫中更新數(shù)據(jù)庫。?

?

除了上述通過改變系統(tǒng)架構(gòu)的方式提升寫的性能外,MySQL本身也可以通過配置參數(shù)innodb_flush_log_at_trx_commit來調(diào)整寫入磁盤的策略。如果機(jī)器成本允許,從硬件層面解決問題,可以選擇老一點(diǎn)的RAID(Redundant Arrays of independent Disks,磁盤列陣)或者比較新的SSD(Solid State Drives,固態(tài)硬盤)。

5. NoSQL存儲

不管數(shù)據(jù)庫的讀還是寫,當(dāng)流量再進(jìn)一步上漲,終會達(dá)到“人力有窮時”的場景。繼續(xù)加機(jī)器的成本比較高,并且不一定可以真正解決問題的時候。這個時候,部分核心數(shù)據(jù),就可以考慮使用NoSQL的數(shù)據(jù)庫。NoSQL存儲,大部分都是采用key-value的方式,這里比較推薦使用上面介紹過Redis,Redis本身是一個內(nèi)存cache,同時也可以當(dāng)做一個存儲來使用,讓它直接將數(shù)據(jù)落地到磁盤。

這樣的話,我們就將數(shù)據(jù)庫中某些被頻繁讀寫的數(shù)據(jù),分離出來,放在我們新搭建的Redis存儲集群中,又進(jìn)一步減輕原來MySQL數(shù)據(jù)庫的壓力,同時因為Redis本身是個內(nèi)存級別的Cache,讀寫的性能都會大幅度提升。?

?

國內(nèi)一線互聯(lián)網(wǎng)公司,架構(gòu)上采用的解決方案很多是類似于上述方案,不過,使用的cache服務(wù)卻不一定是Redis,他們會有更豐富的其他選擇,甚至根據(jù)自身業(yè)務(wù)特點(diǎn)開發(fā)出自己的NoSQL服務(wù)。

6. 空節(jié)點(diǎn)查詢問題

當(dāng)我們搭建完前面所說的全部服務(wù),認(rèn)為Web系統(tǒng)已經(jīng)很強(qiáng)的時候。我們還是那句話,新的問題還是會來的??展?jié)點(diǎn)查詢,是指那些數(shù)據(jù)庫中根本不存在的數(shù)據(jù)請求。例如,我請求查詢一個不存在人員信息,系統(tǒng)會從各級緩存逐級查找,最后查到到數(shù)據(jù)庫本身,然后才得出查找不到的結(jié)論,返回給前端。因為各級cache對它無效,這個請求是非常消耗系統(tǒng)資源的,而如果大量的空節(jié)點(diǎn)查詢,是可以沖擊到系統(tǒng)服務(wù)的。

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

在我曾經(jīng)的工作經(jīng)歷中,曾深受其害。因此,為了維護(hù)Web系統(tǒng)的穩(wěn)定性,設(shè)計適當(dāng)?shù)目展?jié)點(diǎn)過濾機(jī)制,非常有必要。

我們當(dāng)時采用的方式,就是設(shè)計一張簡單的記錄映射表。將存在的記錄存儲起來,放入到一臺內(nèi)存cache中,這樣的話,如果還有空節(jié)點(diǎn)查詢,則在緩存這一層就被阻擋了。?

億級Web系統(tǒng)搭建——單機(jī)到分布式集群 - hansionxu - 技術(shù)的天空?

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號