1. 程式人生 > >大型網站應用之海量資料和高併發解決方案總結

大型網站應用之海量資料和高併發解決方案總結

一、網站應用背景

開發一個網站的應用程式,當用戶規模比較小的時候,使用簡單的:一臺應用伺服器+一臺資料庫伺服器+一臺檔案伺服器,這樣的話完全可以解決一部分問題,也可以通過堆硬體的方式來提高網站應用的訪問效能,當然,也要考慮成本的問題。

當問題的規模在經濟條件下通過堆硬體的方式解決不了的時候,我們應該通過其他的思路去解決問題,網際網路發展至今,已經提供了很多成熟的解決方案,但並不是都具有適用性,你把淘寶的技術全部都搬過來也不一定達到現在淘寶的水平,道理很簡單。

當然,很多文章都在強調,一個網站的發展水平,是逐漸的演變過來的,並不是一朝一夕的事情。雖然目前的情況網際網路的泡沫越來越大,但是整個網際網路技術的發展確實為我們提供了方便快捷的上網體驗。下邊是一張早期的淘寶官網的介面:

目前,博主正在跟隨導師做一個創業專案,使用的技術是SSM+MySQL+Linux這些,但是由於資金的限制和考慮到使用者群體的特殊性,系統的架構無奈的選擇的就是最簡單的方式:一臺應用伺服器、一臺資料庫伺服器、一臺檔案系統伺服器,沒有用到高階的技術,也沒有用到分散式部署的方案。下邊整理的是一些針對海量資料和高併發情況下的解決方案,技術水平有限,歡迎留言指導。 二、針對海量資料和高併發的主要解決方案 海量資料的解決方案: 1.使用快取; 2.頁面靜態化技術; 3.資料庫優化; 4.分離資料庫中活躍的資料; 5.批量讀取和延遲修改; 6.讀寫分離; 7.使用NoSQL和Hadoop等技術; 8.分散式部署資料庫; 9.應用服務和資料服務分離; 10.使用搜索引擎搜尋資料庫中的資料; 11.進行業務的拆分; 高併發情況下的解決方案: 1.應用程式和靜態資原始檔進行分離; 2.頁面快取; 3.叢集與分散式; 4.反向代理; 5.CDN; 三、海量資料的解決方案 (1)使用快取

網站訪問資料的特點大多數呈現為“二八定律”:80%的業務訪問集中在20%的資料上。

例如:在某一段時間內百度的搜尋熱詞可能集中在少部分的熱門詞彙上;新浪微博某一時期也可能大家廣泛關注的主題也是少部分事件。

總的來說就是使用者只用到了總資料條目的一小部分,當網站發展到一定規模,資料庫IO操作成為效能瓶頸的時候,使用快取將這一小部分的熱門資料快取在記憶體中是一個很不錯的選擇,不但可以減輕資料庫的壓力,還可以提高整體網站的資料訪問速度。

使用快取的方式可以通過程式程式碼將資料直接儲存到記憶體中,例如通過使用Map或者ConcurrentHashMap;另一種,就是使用快取框架:Redis、Ehcache、Memcache等。

使用快取框架的時候,我們需要關心的就是什麼時候建立快取和快取失效策略。

快取的建立可以通過很多的方式進行建立,具體也需要根據自己的業務進行選擇。例如,新聞首頁的新聞應該在第一次讀取資料的時候就進行快取;對於點選率比較高的文章,可以將其文章內容進行快取等。

記憶體資源有限,選擇如何建立快取是一個值得思考的問題。另外,對於快取的失效機制也是需要好好研究的,可以通過設定失效時間的方式進行設定;也可以通過對熱門資料設定優先順序,根據不同的優先順序設定不同的失效時間等;

需要注意的是,當我們刪除一條資料的時候,我們要考慮到刪除該條快取,還要考慮在刪除該條快取之前該條資料是否已經到達快取失效時間等各種情況!

使用快取的時候還要考慮到快取伺服器發生故障時候如何進行容錯處理,是使用N多臺伺服器快取相同的資料,通過分散式部署的方式對快取資料進行控制,當一臺發生故障的時候自動切換到其他的機器上去;還是通過Hash一致性的方式,等待快取伺服器恢復正常使用的時候重新指定到該快取伺服器。Hash一致性的另一個作用就是在分散式快取伺服器下對資料進行定位,將資料分佈在不用快取伺服器上。關於資料快取的Hash一致性也是一個比較打的問題,這裡只能大致描述一下,關於Hash一致性的瞭解,推薦一篇文章:http://blog.csdn.net/liu765023051/article/details/49408099 (2)頁面靜態化技術

使用傳統的JSP介面,前端介面的顯示是通過後臺伺服器進行渲染後返回給前端遊覽器進行解析執行,如下圖:

當然,現在提倡前後端分離,前端介面基本都是HTML網頁程式碼,通過Angular JS或者NodeJS提供的路由向後端伺服器發出請求獲取資料,然後在遊覽器對資料進行渲染,這樣在很大程度上降低了後端伺服器的壓力。

還可以將這些靜態的HTML、CSS、JS、圖片資源等放置在快取伺服器上或者CDN伺服器上,一般使用最多的應該是CDN伺服器或者Nginx伺服器提供的靜態資源功能。

另外,在《高效能網站建設進階指南-Web開發者效能優化最佳實踐(口碑網前端團隊 翻譯)》這本書中,對網站效能的前端介面提供了一些很寶貴的經驗,如下:

因此,在這些靜態資源的處理上,選擇正確的處理方式還是對整體網站效能還是有很大幫助的! (3)資料庫優化

資料庫優化是整個網站效能優化的最基礎的一個環節,因為,大多數網站效能的瓶頸都是開在資料庫IO操作上,雖然提供了快取技術,但是對資料庫的優化還是一個需要認真的對待。一般公司都有自己的DBA團隊,負責資料庫的建立,資料模型的確立等問題,不像我們現在幾個不懂資料庫優化的人只能在網上找一篇篇資料庫優化的文章,自己去摸索,並沒有形成一個系統的資料庫優化思路。

對於資料庫的優化來說,是一種用技術換金錢的方式。資料庫優化的方式很多,常見的可以分為:資料庫表結構優化、SQL語句優化、分割槽、分表、索引優化、使用儲存過程代替直接操作等 。 1、表結構優化

對於資料庫的 開發規範與使用技巧以及設計和優化,前邊的時候總結了一些文章,這裡偷個懶直接放地址,有需要的可以移步看一下: a) MySQL開發規範與使用技巧總結:http://blog.csdn.net/xlgen157387/article/details/48086607 b) 在一個千萬級的資料庫查尋中,如何提高查詢效率?:http://blog.csdn.net/xlgen157387/article/details/44156679

另外,再設計資料庫表的時候需不需要建立外來鍵,使用外來鍵的好處之一可以方便的進行級聯刪除操作,但是現在在進行資料業務操作的時候,我們都通過事物的方式來保證資料讀取操作的一致性,我感覺相比於使用外來鍵關聯MySQL自動幫我們完成級聯刪除的操作來說,還是自己使用事物進行刪除操作來的更放心一些。當然可能也是有適用的場景,大家如有很好的建議,歡迎留言! 2、SQL優化

對於SQL的優化,主要是針對SQL語句處理邏輯的優化,而且還要根據索引進行配合使用。另外,對於SQL語句的優化我們可以針對具體的業務方法進行優化,我們可以將執行業務邏輯操作的資料庫執行時間記錄下來,來進行有針對性的優化,這樣的話效果還是很不錯的!例如下圖,展示了一條資料庫操作執行呼叫的時間:

關於SQL優化的一些建議,以前整理了一些,還請移步檢視:

a) 19個MySQL效能優化要點解析:http://blog.csdn.net/xlgen157387/article/details/50735269

b) MySQL批量SQL插入各種效能優化:http://blog.csdn.net/xlgen157387/article/details/50949930

分表

分表是將一個大表按照一定的規則分解成多張具有獨立儲存空間的實體表,我們可以稱為子表,每個表都對應三個檔案,MYD資料檔案,.MYI索引檔案,.frm表結構檔案。這些子表可以分佈在同一塊磁碟上,也可以在不同的機器上。資料庫讀寫操作的時候根據事先定義好的規則得到對應的子表名,然後去操作它。

例如:使用者表 使用者的角色有很多種,可以通過列舉型別的方式將使用者分為不同類別category:學生、教師、企業等 ,這樣的話,我們就可以根據類別category來對資料庫進行分表,這樣的話每次查詢的時候現根據使用者的型別鎖定一個較小的範圍。

不過分表之後,如果需要查詢完整的順序就需要使用多表操作了。 分割槽

資料庫分割槽是一種物理資料庫設計技術,DBA和資料庫建模人員對其相當熟悉。雖然分割槽技術可以實現很多效果,但其主要目的是為了在特定的SQL操作中減少資料讀寫的總量以縮減響應時間。

分割槽和分表相似,都是按照規則分解表。不同在於分表將大表分解為若干個獨立的實體表,而分割槽是將資料分段劃分在多個位置存放,可以是同一塊磁碟也可以在不同的機器。分割槽後,表面上還是一張表,但資料雜湊到多個位置了。資料庫讀寫操作的時候操作的還是大表名字,DMS自動去組織分割槽的資料。

當一張表中的資料變得很大的時候,讀取資料,查詢資料的效率非常低下,很容易的就是講資料分到不同的資料表中進行儲存,但是這樣分表之後會使得操作起來比較麻煩,因為,將同類的資料分別放在不同的表中的話,在搜尋資料的時候需要便利查詢這些表中的資料。想進行CRUD操作還需要先找到對應的所有表,如果涉及到不同的表的話還要進行跨表操作,這樣操作起來還是很麻煩的。

使用分割槽的方式可以解決這個問題,分割槽是將一張表中的資料按照一定的規則分到不同的區中進行儲存,這樣進行資料查詢的時候如果資料的範圍在同一個區域內那麼就可以支隊一個區中的資料進行操作,這樣的話操作起來資料量更少,操作速度更快,而且該方法是對程式透明的,程式不需要進行任何的修改。

索引優化

索引的大致原理是在資料發生變化的時候就預先按指定欄位的順序排列後儲存到一個類似表的結構中,這樣在查詢索引欄位為條件記錄時就可以很快地從索引中找到對應記錄的指標並從表中獲取到相應的資料,這樣速度是很快地。

不過,雖然查詢的效率大大提高了,但是在進行增刪改的時候,因為資料的變化都需要更新相應的索引,也是一種資源的浪費。

關於使用索引的問題,對待不同的問題,還是需要進行不同的討論,根據具體的業務需求選擇合適的索引對效能的提高效果是很明顯的一個舉措!

推薦文章閱讀:

a) 資料庫索引的作用和優點缺點以及索引的11中用法:http://blog.csdn.net/xlgen157387/article/details/45030829

b) 資料庫索引原理:http://blog.csdn.net/kennyrose/article/details/7532032

使用儲存過程代替直接操作

儲存過程(Stored Procedure)是在大型資料庫系統中,一組為了完成特定功能的SQL 語句集,儲存在資料庫中,經過第一次編譯後再次呼叫不需要再次編譯,使用者通過指定儲存過程的名字並給出引數(如果該儲存過程帶有引數)來執行它。儲存過程是資料庫中的一個重要物件,任何一個設計良好的資料庫應用程式都應該用到儲存過程。

在操作過程比較複雜並且呼叫頻率比較高的業務中,可以將編寫好的sql語句用儲存過程的方式來代替,使用儲存過程只需要進行一次變異,而且可以在一個儲存過程裡做一些複雜的操作。

(4)分離資料庫中活躍的資料

正如前邊提到的“二八定律”一樣,網站的資料雖然很多,但是經常被訪問的資料還是有限的,因此可以講這些相對活躍的資料進行分離出來單獨進行儲存來提高處理效率。

其實前邊使用快取的思想就是一個很明顯的分離資料庫中活躍的資料的使用案例,將熱門資料快取在記憶體中。

還有一種場景就是,例如一個網站的所用註冊使用者量很大千萬級別,但是經常登入的使用者只有百萬級別,剩下的基本都是很長時間都沒有進行登入操作,如果不把這些“殭屍使用者”單獨分離出去,那麼我們每次查詢其他登入使用者的時候,就白白浪費了這些殭屍使用者的查詢操作。

(5)批量讀取和延遲修改

批量讀取和延遲修改的原理是通過減少操作資料庫的操作來提高效率。

批量讀取是將多次查詢合併到一次中進行讀取,因為每一個數據庫的請求操作都需要連結的建立和連結的釋放,還是佔用一部分資源的,批量讀取可以通過非同步的方式進行讀取。

延遲修改是對於一些高併發的並且修改頻繁修改的資料,在每次修改的時候首先將資料儲存到快取中,然後定時將快取中的資料儲存到資料庫中,程式可以在讀取資料時可以同時讀取資料庫中和快取中的資料。

例如:我現在在編寫這份部落格,我一開始寫了一寫內容然後點擊發布了,在次回到Markdown進行修改操作,我有一個習慣,沒寫一些東西總是按一下上邊的 “儲存”按鈕,然後我在另一個頁面開啟這篇部落格檢視,我的修改已經被更新,但是我還在 編輯!

不知道CSDN的技術是不是在我沒有點擊發布之前,這些資料都是先放到快取裡邊的。

(6) 讀寫分離

讀寫分離的實質是將應用程式對資料庫的讀寫操作分配到多個數據庫伺服器上,從而降低單臺數據庫的訪問壓力。

讀寫分離一般通過配置主從資料庫的方式,資料的讀取來自從庫,對資料庫增加修改刪除操作主庫。

相關文章請移步觀看: a) MySQL5.6 資料庫主從(Master/Slave)同步安裝與配置詳解:http://blog.csdn.net/xlgen157387/article/details/51331244 b) MySQL主從複製的常見拓撲、原理分析以及如何提高主從複製的效率總結:http://blog.csdn.net/xlgen157387/article/details/52451613

(7)使用NoSQL和Hadoop等技術

NoSQL是一種非結構化的非關係型資料庫,由於其靈活性,突破了關係型資料庫的條條框框,可以靈活的進行操作,另外,因為NoSQL通過多個塊儲存資料的特點,其操作大資料的速度也是 ---------------------