首頁  >  文章  >  運維  >  網站系統的快取機制的建立與最佳化

網站系統的快取機制的建立與最佳化

藏色散人
藏色散人轉載
2019-04-13 17:24:313611瀏覽

講完了Web系統的外部網路環境,現在我們開始關注我們Web系統本身的效能問題。

我們的Web網站隨著造訪量的上升,會遇到很多的挑戰,解決這些問題不只是擴容機器這麼簡單,建立並使用適當的快取機制才是根本。

最開始,我們的Web系統架構可能是這樣的,每個環節,都可能只有1台機器。

網站系統的快取機制的建立與最佳化

一、 MySQL資料庫內部快取使用

MySQL的快取機制,就從先從MySQL內部開始,下面的內容將以最常見的InnoDB儲存引擎為主。

1. 建立恰當的索引

最簡單的是建立索引,索引在表資料比較大的時候,起到快速檢索資料的作用,但是成本也是有的。首先,佔用了一定的磁碟空間,其中組合索引最突出,使用需要謹慎,它產生的索引甚至會比來源資料更大。其次,建立索引之後的資料insert/update/delete等操作,因為需要更新原來的索引,耗時會增加。當然,實際上我們的系統從整體來說,是以select查詢操作居多,因此,索引的使用仍然對系統效能有大幅提升的作用。

2. 資料庫連接執行緒池快取

如果,每個資料庫操作請求都需要創建和銷毀連接的話,對資料庫來說,無疑也是一種巨大的開銷。為了減少這類型的開銷,可以在MySQL中設定thread_cache_size來表示保留多少執行緒用於重複使用。線程不夠的時候,再創建,空閒過多的時候,則銷毀。

其實,還有更激進一點的做法,使用pconnect(資料庫長連接),線程一旦創建在很長時間內都保持著。但是,在訪問量比較大,機器比較多的情況下,這種用法很可能會導致“資料庫連接數耗盡”,因為建立連接並不回收,最終達到資料庫的max_connections(最大連接數)。因此,長連接的用法通常需要在CGI和MySQL之間實作一個「連接池」服務,控制CGI機器「盲目」創建連接數。

3. Innodb快取設定(innodb_buffer_pool_size)

innodb_buffer_pool_size這是個用來保存索引和資料的記憶體快取區,如果機器是MySQL獨佔的機器,一般推薦為機器實體記憶體的80 %。在取表資料的場景中,它可以減少磁碟IO。一般來說,這個值設定越大,cache命中率會越高。

4. 分庫/分錶/分區。

MySQL資料庫表一般承受資料量在百萬級,再往上成長,各項效能將會出現大幅下降,因此,當我們預見資料量會超過這個量級的時候,建議進行分庫/分錶/分區等操作。最好的做法,是服務在搭建之初就設計為分庫分錶的儲存模式,從根本上杜絕中後期的風險。不過,會犧牲一些便利性,例如列表式的查詢,同時,也增加了維護的複雜度。不過,到了數據量千萬等級或以上的時候,我們會發現,它們都是值得的。

二、 MySQL資料庫多台服務建置

1台MySQL機器,其實是高風險的單點,因為如果它掛了,我們Web服務就不可用了。而且,隨著Web系統存取量持續增加,終於有一天,我們發現1台MySQL伺服器無法支撐下去,我們開始需要使用更多的MySQL機器。當引進多台MySQL機器的時候,很多新的問題又會產生。

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

這個做法純粹為了解決「單點故障」的問題,在主函式庫出故障的時候,切換到從函式庫。不過,這種做法其實有點浪費資源,因為從庫實際上被閒著了。

網站系統的快取機制的建立與最佳化

2. MySQL讀寫分離,主函式庫寫,從函式庫讀。

兩台資料庫做讀寫分離,主庫負責寫入類別的操作,從函式庫負責讀取的操作。而且,如果主庫發生故障,仍然不影響讀取的操作,同時也可以將全部讀寫都暫時切換到從庫中(需要注意流量,可能會因為流量過大,把從庫也拖垮)。 

網站系統的快取機制的建立與最佳化

3. 主主互備。

兩台MySQL之間互為彼此的從函式庫,同時又是主函式庫。這種方案,既做到了訪問量的壓力分流,同時也解決了「單點故障」問題。任何一台故障,都還有另外一套可供使用的服務。

不過,這個方案,只能用在兩台機器的場景。如果業務拓展還是很快的話,可以選擇將業務分離,建立多個主主互備。

網站系統的快取機制的建立與最佳化

三、 在Web伺服器與資料庫之間建立快取

#實際上,解決大訪問量的問題,不能只著眼於資料庫層面。根據“二八定律”,80%的請求只關注在20%的熱點數據上。因此,我們應該建立Web伺服器和資料庫之間的快取機制。這種機制,可以用磁碟作為緩存,也可以用記憶體緩存的方式。透過它們,將大部分的熱點資料查詢,阻擋在資料庫之前。

1. 頁面靜態化

使用者造訪網站的某個頁面,頁面上的大部分內容在很長一段時間內,可能都是沒有變化的。例如一篇新聞報道,一旦發布幾乎是不會修改內容的。這樣的話,透過CGI產生的靜態html頁面快取到Web伺服器的磁碟本地。除了第一次,是透過動態CGI查詢資料庫取得之外,之後都直接將本機磁碟檔案回傳給使用者。

在Web系統規模比較小的時候,這種做法看似完美。但是,一旦Web系統規模變大,例如當我有100台的Web伺服器的時候。那樣這些磁碟文件,將會有100份,這個是資源浪費,也不好維護。這時候有人會想,可以集中一台伺服器存起來,呵呵,不如看看下面一種快取方式吧,它就是這樣做的。

2. 單一記憶體快取

透過頁面靜態化的例子中,我們可以知道將「快取」搭建在Web機器本機是不好維護的,會帶來更多問題(實際上,透過PHP的apc拓展,可透過Key/value操作Web伺服器的本機記憶體)。因此,我們選擇搭建的記憶體快取服務,也必須是獨立的服務。

記憶體快取的選擇,主要有redis/memcache。從性能上說,兩者差異不大,從功能豐富程度來說,Redis更勝一籌。

3. 記憶體快取叢集

當我們搭建單一記憶體快取完畢,我們又會面臨單點故障的問題,因此,我們必須將它變成一個叢集。簡單的做法,就是增加一個slave當備份機器。但是,如果請求量真的很多,我們發現cache命中率不高,需要更多的機器記憶體呢?因此,我們更建議將它配置成一個叢集。例如,類似redis cluster。

Redis cluster叢集內的Redis互為多組主從,同時每個節點都可以接受請求,在拓展叢集的時候比較方便。客戶端可以向任意一個節點發送請求,如果是它的「負責」的內容,則直接傳回內容。否則,尋找實際負責Redis節點,然後將位址告知客戶端,客戶端重新請求。

對於使用快取服務的客戶端來說,這一切都是透明的。

記憶體快取服務在切換的時候,是有一定風險的。從A集群切換到B集群的過程中,必須保證B集群提前做好「預熱」(B集群的內存中的熱點數據,應該盡量與A集群相同,否則,切換的一瞬間大量請求內容,在B叢集的記憶體快取中查找不到,流量直接衝擊後端的資料庫服務,很可能導致資料庫宕機)。

4. 減少資料庫「寫」

上面的機制,都實現減少資料庫的「讀」的操作,但是,寫的操作也是一個大的壓力。寫的操作,雖然無法減少,但是可以透過合併請求,來起到減輕壓力的效果。這個時候,我們需要在記憶體快取叢集和資料庫叢集之間,建立一個修改同步機制。

先將修改請求生效在cache中,讓外界查詢顯示正常,然後將這些sql修改放入到一個隊列中儲存起來,隊列滿或每隔一段時間,合併為一個請求到資料庫中更新資料庫。 

除了上述改變系統架構的方式提升寫入的效能外,MySQL本身也可以透過設定參數innodb_flush_log_at_trx_commit來調整寫入磁碟的策略。如果機器成本允許,從硬體層面解決問題,可以選擇老一點的RAID(Redundant Arrays of independent Disks,磁碟列陣)或比較新的SSD(Solid State Drives,固態硬碟)。

5. NoSQL儲存

不管資料庫的讀取或寫入,當流量再進一步上漲,終會達到「人力有窮時」的場景。繼續加機器的成本比較高,不一定可以真正解決問題的時候。這時候,部分核心數據,就可以考慮使用NoSQL的資料庫。 NoSQL存儲,大部分都是採用key-value的方式,這裡比較建議使用上面介紹過Redis,Redis本身是一個內存cache,同時也可以當做一個存儲來使用,讓它直接將數據落地到磁碟。

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

國內第一線網路公司,架構上採用的解決方案很多是類似上述方案,不過,使用的cache服務卻不一定是Redis,他們會有更豐富的其他選擇,甚至根據自身業務特徵開發出自己的NoSQL服務。

6. 空節點查詢問題

當我們搭建完前面所說的全部服務,認為Web系統已經很強的時候。我們還是那句話,新的問題還是會來的。空節點查詢,是指那些資料庫中根本不存在的資料請求。例如,我請求查詢一個不存在人員信息,系統會從各級快取逐級查找,最後查到到資料庫本身,然後才得出查找不到的結論,返回給前端。因為各級cache對它無效,這個請求是非常消耗系統資源的,而如果大量的空節點查詢,是可以衝擊到系統服務的。

以上是網站系統的快取機制的建立與最佳化的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:hcoder.net。如有侵權,請聯絡admin@php.cn刪除