夏雨++朱信忠++徐慧英++顧雅麗++盧山富
摘 要 隨著互聯(lián)網(wǎng)技術(shù)突飛猛進的發(fā)展,網(wǎng)站規(guī)模的不斷擴大,數(shù)據(jù)量的增加,大數(shù)據(jù)的時代已經(jīng)真正到來,性能和安全的問題也日益突出,很多技術(shù)被提出,并且用來提升網(wǎng)絡以及服務器的性能和安全,各種各樣的架構(gòu)也應運而生,如何在高并發(fā),高性能之中尋找一個平衡點,已經(jīng)成為亟需解決的問題,很多人可能知道應對高并發(fā)要用分布式緩存,要用分布式和負載均衡。但為什么要這么用,怎么用才能用好,怎樣根據(jù)實際需求設計最適合的架構(gòu)完全不懂。本文基于.net平臺,提出了并設計了支持.Net平臺的千萬級規(guī)模分布式高性能高并發(fā)WEB軟件開發(fā)架構(gòu)設計方案。
【關鍵詞】互聯(lián)網(wǎng)技術(shù) 并發(fā)業(yè)務系統(tǒng) 方案設計
1 系統(tǒng)產(chǎn)生并發(fā)的業(yè)務節(jié)點
并發(fā)產(chǎn)生的原因:
(1)短時間大量請求競爭低速存儲設備或者競爭處理器資源。
(2)不合理的程序處理邏輯導致請求無法盡快完成。
(3)其他瓶頸,如帶寬、服務器資源等。
2 應對并發(fā)的解決方案
2.1 針對并發(fā)量不高的情況的方案——分庫分表,實質(zhì)上是分布式系統(tǒng)的一種模式
具體邏輯如下:
將原本屬于同一臺服務器的同一個數(shù)據(jù)庫拆分到多臺服務器中,甚至可以將原本同屬于一張表的字段拆分到不同的服務器上。其目的為將一個大的讀寫請求分解到多臺服務器中,從而在異步并行執(zhí)行中減少請求執(zhí)行時間,以達到并發(fā)周期中可承載更多的請求。
2.2 使用分布式處理,結(jié)合CDN負載均衡技術(shù)
2.2.1 鏡像模式
將一臺服務器的鏡像副本復制到多臺服務器中,通過windows的NLB或者第三方CDN方案如nginx實現(xiàn)分布式處理,請求均衡。此種分布式模式在windows下只能使用NBL,因為NBL僅支持鏡像式分布式服務器集群,理論上NLB可以支持32臺服務器。
NLB模式的分布式系統(tǒng)的優(yōu)勢在于通過負載均衡能夠充分發(fā)揮分布式系統(tǒng)在讀數(shù)據(jù)比較頻繁的模式下發(fā)揮最大的效益,而且彼此之間互為備份。其缺點也是明顯的,因為同一條數(shù)據(jù)的寫入(更新或者刪除等寫操作)必須通過事務在集群內(nèi)每臺服務器上同步。
2.2.2 分段模式
多臺服務器通過CDN來決定數(shù)據(jù)寫入那一臺或者多臺服務器中,數(shù)據(jù)在讀取時需要在多臺服務器中輪詢,增加了數(shù)據(jù)讀取的復雜度。
2.3 使用分布式緩存技術(shù)
分布式緩存技術(shù)已經(jīng)得到廣泛應用,其實現(xiàn)算法為平衡樹(紅黑樹),使用緩存的目的是要減少訪問服務器低速存儲設備以及I/O帶來的性能損耗,從而提高系統(tǒng)單位時間內(nèi)的響應能力,以達到提高并發(fā)能力的目的。
分布式緩存有很多典型應用,例如聊天室。早期的聊天室沒有使用socket前,基本上使用的都是脈沖模式(服務器輪詢),不斷向低速存儲設備輪詢的成本是很可觀的。所以寫入內(nèi)存當中能夠更快速的使客戶端訪問到,一旦聊天結(jié)束,服務器并不持久化聊天數(shù)據(jù)。
投注業(yè)務具有短時間集中并發(fā)寫入數(shù)據(jù)的特點,將數(shù)據(jù)寫入內(nèi)存,并在內(nèi)存中操作并非復雜的事情。但如涉及到數(shù)據(jù)持久化,就存在數(shù)據(jù)一致性的問題。分布式緩存解決方案redis提供了數(shù)據(jù)持久化,能夠保證數(shù)據(jù)的一致性。
實踐證明,10萬級別的并發(fā)場景中,向低速存儲設備(關系數(shù)據(jù)庫)寫入數(shù)據(jù)如果以整形為主,使用緩存和不使用緩存的差別并不明顯。
2.4 NOSQL的使用
NOSQL數(shù)據(jù)庫的數(shù)據(jù)吞吐能力可以達到關系數(shù)據(jù)庫的百倍以上,天然支持分布式模式。能夠提供高速讀寫的優(yōu)異性能,提供高I/O操作。谷歌、百度、淘寶等都使用了NOSQL。
但NOSQL也并非沒有缺點,大多數(shù)應用都是關系型的,也就是要保證數(shù)據(jù)操作的原子性和唯一性,NOSQL無法保證這一點。因此多數(shù)NOSQL應用需要數(shù)據(jù)庫中間件來保證關系數(shù)據(jù)的原子性。常用的NOSQL如MongoDB、Hadoop等。
3 并發(fā)解決方案的實現(xiàn)
分庫分表的實現(xiàn):
A:為什么要分庫分表?
在只有一臺服務器的情況下,大量的select會被同時執(zhí)行的update和delete阻塞,導致并發(fā)數(shù)嚴重受限。當然,這種場景非常適合讀遠大于寫的應用,當讀寫基本相當?shù)那闆r下,單臺服務器依舊存在讀寫互斥的情況,不適合并發(fā)量較大的應用。
B:分庫的作用在于多臺服務器分擔客戶端的讀寫請求,大大降低了并發(fā)的可能性。
在web應用中,通常web服務器應與數(shù)據(jù)庫服務分離,這在未來實現(xiàn)CDN的時候非常有意義,來自客戶端的請求基本會在web服務器之間進行分配,由web服務器根據(jù)CDN決定訪問哪些數(shù)據(jù)庫服務器。
數(shù)據(jù)庫服務器分離后,多臺數(shù)據(jù)庫服務器之間通過異步數(shù)據(jù)事務保持一致性,當然這種數(shù)據(jù)同步會有一些時間上的延遲,這種延遲基本上都是毫秒級別的。當然,低級別的分庫可以直接由邏輯層通過事務保證數(shù)據(jù)一致性。
操作系統(tǒng)對磁盤的讀寫有控制機制,在只有一臺服務器的情況下,各種請求競爭資源,包括對處理機及內(nèi)存、磁盤等的控制權(quán)。因此,一臺服務器的并發(fā)能力是非常有限的,這也是分庫的原因所在。
3.1 關于讀寫分離
前文已提到讀寫互斥,這將給讀取數(shù)據(jù)帶來很大延遲,尤其是大量讀寫的情況下這種延遲是無法接受的。因此在讀業(yè)務場景中,應保持數(shù)據(jù)隨時處于可讀取狀態(tài)。
通過分庫、分表以達到讀寫分離的目標,讓一些(庫)表專門用來寫數(shù)據(jù),而另一些表(庫)用來讀數(shù)據(jù)。而數(shù)據(jù)庫與數(shù)據(jù)庫之間通過數(shù)據(jù)一致性組件保持數(shù)據(jù)復制作業(yè),如syncNavigator或者數(shù)據(jù)庫自帶的訂閱發(fā)布功能。用于寫操作的數(shù)據(jù)庫一旦有新數(shù)據(jù)寫入,則根據(jù)數(shù)據(jù)同步策略來同步數(shù)據(jù)到讀服務器(數(shù)據(jù)庫、表)中。
3.2 關于緩存
Memcached 是一個高性能的分布式內(nèi)存 對象緩存系統(tǒng),用于動態(tài)Web應用以減輕數(shù)據(jù)庫負載。它通過在內(nèi)存中緩存數(shù)據(jù)和對象 來減少讀取數(shù)據(jù)庫的次數(shù),從而提供動態(tài)、數(shù)據(jù)庫驅(qū)動網(wǎng)站的速度。
memcache會預先生成很多的內(nèi)存塊,比如有96byte,120byte,150byte,200byte,800byte。
預先生成一批slab的好處是什么?可以根據(jù)item的大小,放到合適的slab上面去。
原因:找內(nèi)存所消耗的時間比遠大于釋放時間。
但是Memcached對內(nèi)存資源的有效利用:
(1)重復利用已經(jīng)分配的內(nèi)存,也就是說不會去刪除已有的數(shù)據(jù)而且釋放,而是數(shù)據(jù)過期后,用戶將數(shù)據(jù)不可見。
(2)Memcached 還是用了一種Lazy Expiration (延遲過期[姑且這樣翻譯]) 技術(shù),就是Memcached不會去監(jiān)視服務器上的數(shù)據(jù)是否過期,而是等待get的時候檢查時間戳是否過期,減少Memcached在監(jiān)控數(shù)據(jù)上所用到的時間。
(3) Memcached 不會去釋放已經(jīng)使用的內(nèi)存空間,但是如果分配的內(nèi)存空間已經(jīng)滿了,而Memcached 是如何去保證內(nèi)存空間的重復使用呢!Memcached 是用了 Least Recently Used(LRU) 機制來協(xié)調(diào)內(nèi)存空間的使用。LRU 意思就是最少最近使用,當此處內(nèi)存空間數(shù)據(jù)最長時間沒有使用,而且使用次數(shù)很少,在存儲新的數(shù)據(jù)的同時就會覆蓋此處空間。
一致性哈希
當hash遇上分布式,單臺機子的hashmap存儲已經(jīng)不能滿足我們的key-value需求,怎么辦,我們需要把存儲內(nèi)容分布到不同的實體機上,這時需要一種把key映射到不同機器的方法,我們想起了hash,可以把實體機當做是桶,采用和hashmap實現(xiàn)一樣的思路,通過和實體機的數(shù)量取模,自然映射到不同的機器。
但是這就會導致一個問題:數(shù)據(jù)分布不均勻。大部分數(shù)據(jù)都分配到server1了,只有小部分數(shù)據(jù)分布在server2。在服務器數(shù)據(jù)很少的時候,數(shù)據(jù)不均 勻會表現(xiàn)的非常明顯。
解決這個問題的方法是使用虛擬節(jié)點,一個真實服務器對應多個虛擬節(jié)點,所有虛擬節(jié)點按hash值分布在一致性哈希圓環(huán)上。具體實現(xiàn)方法可以這樣做,為真實服務器設置副本數(shù)量,然后根據(jù)各真實服務器的IP和端口號再加上一個遞增的索引數(shù)計算hash值。
如圖2所示。
分布式緩存可以解決以下幾種問題:
比如用來緩存Web 頁面的內(nèi)容片段,包括HTML、CSS 和圖片等,多應用于社交網(wǎng)站等;
應用對象緩存:緩存系統(tǒng)作為ORM 框架的二級緩存對外提供服務,目的是減輕數(shù)據(jù)庫的負載壓力,加速應用訪問;
狀態(tài)緩存:緩存包括Session 會話狀態(tài)及應用橫向擴展時的狀態(tài)數(shù)據(jù)等,這類數(shù)據(jù)一般是難以恢復的,對可用性要求較高,多應用于高可用集群;
并行處理:通常涉及大量中間計算結(jié)果需要共享;
事件處理:分布式緩存提供了針對事件流的連續(xù)查詢(continuous query)處理技術(shù),滿足實時性需求;
極限事務處理:分布式緩存為事務型應用提供高吞吐率、低延時的解決方案,支持高并發(fā)事務請求處理,多應用于鐵路、金融服務和電信等領域。
3.3 關于NOSQL
以SQL SERVER2012作為數(shù)據(jù)倉庫存儲邏輯數(shù)據(jù),當關系型數(shù)據(jù)庫無法滿足并發(fā)要求的時候,后端增加使用非關系型數(shù)據(jù)庫mongodb作為高吞吐數(shù)據(jù)庫使用,為避免NOSQL數(shù)據(jù)庫的非原子性操作帶來的一些問題,架構(gòu)使用SQL SERVER作為數(shù)據(jù)一致性中間件,以分布式緩存作為業(yè)務快速存儲載體,提高整個系統(tǒng)的并發(fā)響應能力。
MongoDB一個文件存儲片段最大2GB,所以隨著數(shù)量的增加,MongoDB會一個又一個增加數(shù)據(jù)存儲文件,正因為是多個文件,所以可以并行從多個文件中讀取和寫入數(shù)據(jù),但是因為是多個文件的并行處理,帶來了高IO,也帶來了致命的缺陷,原因非常明顯,那就是關系數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性,使用了原子性約束,也就是我們說的原子性操作,比如添加數(shù)據(jù)庫記錄時,不允許出現(xiàn)兩條數(shù)據(jù)完全相同,比如主鍵都是2,這樣的話就沒辦法唯一標識這條數(shù)據(jù)了,所以關系數(shù)據(jù)庫普遍采用了原子性(唯一性)約束,非關系數(shù)據(jù)庫和關系數(shù)據(jù)庫在這一點上的區(qū)別非常鮮明,那就是無法保證原子性操作,非關系數(shù)據(jù)庫不保證原子性,也就無法保證數(shù)據(jù)的唯一性,這樣在連表查詢時就無法獲取正確的數(shù)據(jù),所以一般情況下都是由中間件或者關系數(shù)據(jù)庫來保證數(shù)據(jù)的唯一性,比如NOSQL數(shù)據(jù)庫專門用來讀操作,你不是I/O能力很強嗎(是關系數(shù)據(jù)庫的100倍),那你就專門用來讀,數(shù)據(jù)通過各種手段先寫入關系數(shù)據(jù)庫,然后再同步到非關系數(shù)據(jù)庫,你從關系數(shù)據(jù)庫里讀出來的數(shù)據(jù)都具有唯一性,再同步到NOSQL,寫數(shù)據(jù)庫時底層用的是socket,來監(jiān)聽多個異步操作是否返回了正確的結(jié)果。如果是則萬事大吉,如果不是,則向緩存拿數(shù)據(jù)再次寫入數(shù)據(jù)庫,直到寫入為止,除非有新的請求告訴你丟棄這個操作。最后再同步到NOSQL來讀。
參考文獻
[1]包立輝,黃彥飛.高并發(fā)網(wǎng)站的架構(gòu)研究及解決力案[J].計算機科學,2012,39(10):184-187.
[2]薛質(zhì).電子商務平臺的性能優(yōu)化和高可靠性研究與實現(xiàn)[D].上海:上海交通大學,2007:41-45.
作者單位
浙江師范大學 浙江省金華市 321004