關於大型網站技術演進的思考(六)--儲存的瓶頸(6)
在講資料庫水平拆分時候,我列出了水平拆分資料庫需要解決的兩個難題,它們分別是主鍵的設計問題和單表查詢的問題,主鍵問題前文已經做了比較詳細的講述了,但是第二個問題我沒有講述,今天我將會講講如何解決資料表被水平拆分後的單表查詢問題。
要解決資料表被水平拆分後的單表查詢問題,我們首先要回到問題的源頭,我們為什麼需要將資料庫的表進行水平拆分。下面我們來推導下我們最終下定決心做水平拆分表的演進過程,具體如下:
第一個演進過程:進行了讀寫分離的表在資料增長後需要進行水平拆分嗎?回答這個疑問我們首先要想想進行讀寫分離操作的表真的是因為資料量大嗎?答案其實是否定的。最基本的讀寫分離的目的是為了解決資料庫的某張表讀寫比率嚴重失衡的問題
第二個演進過程:隨著公司業務的不斷增長,系統的執行的壓力也越來越大了,我們已經瞭解了系統的第一個瓶頸是從儲存開始了,如是我們開始談論方案如何解決儲存的問題,這時我們發現我們已經做了讀寫分離,也使用了快取,甚至連搜尋技術也用上了,那麼下個階段就是垂直拆分了,垂直拆分很簡單就是把表從資料庫裡拆出來,單獨建庫建表,但是這種直截了當的方案想想就能感到這樣的做法似乎沒有打中系統的痛點,那麼系統的痛點到底是什麼呢?根據資料庫本身的特性,我們會發現痛點主要是三個方面組成:
第一個方面:資料庫的連線數的限制。原庫的某些表可能承擔資料庫80%的連線,極端下甚至可以超過90%的連線,而且這些表的業務操作十分的頻繁,當其他小眾業務的表需要進行操作時候,搞不好因為連線數被全部佔用而不得不排隊等待空閒連線的出現,那麼這個時候我們就會考慮把這張表做垂直拆分,這樣就減輕了原資料庫連線的壓力,使得資料庫連線負載變得比較均衡。
第二個方面是資料庫的讀操作,第三個方面是資料庫的寫操作,雖然把讀和寫分成兩個方面,但是這兩個方面在我們做垂直拆分時候要結合起來考慮。首先我們要分析下資料庫的寫操作,單獨的寫操作效率都是很高的,不管我們的寫是單條記錄的寫操作,還是批量的寫操作,這些寫操作的資料量就是我們要去寫的資料的大小,因此控制寫的資料量的大小是一件很容易很天然的操作,所以這些操作不會造成資料庫太大負擔,詳細點的話,對於資料庫而言,新增操作無非是在原來資料後面追加些記錄,而修改操作或者刪除操作一般都是通過建立了高效索引的欄位來定位資料後再進行的操作,因此它的效能也是非常高的。而讀操作看起來比寫操作簡單(例如:讀操作不存在像事務這些烏七八糟因素的干擾),但是當讀操作面對海量資料時候就嚴重挑戰著資料庫和硬碟的極限能力,因此讀操作很容易產生瓶頸問題,而且這個瓶頸不管問題表是否讀寫失衡都會面臨的。前文裡我詳細列舉了一個交易表設計的案例,其中我們可以看到資料庫垂直拆分在實際應用裡的運用,在例子裡我們首先根據業務特點將交易表分成了實時交易表和歷史交易表,這個做法其實就是將原交易表的讀和寫進行分離,但是這種分離和純粹的讀寫分離相比會更加有深意,這個深意就是拆分實時和歷史交易表也就是在分拆原表的讀寫操作的關聯性,換句話說,如果我們不這麼做的話,那麼交易表的每次寫和每次讀幾乎等價,這樣我們沒法單獨解決讀的效能問題,分出了歷史交易表後我們再對歷史交易表來做讀的優化,那麼這也不會影響到寫操作,這樣把問題的複雜度給降低了。在案例裡我們對歷史交易表進行了業務級別的水平拆分,但是這個拆分是以如何提升讀的效率進行的,因此前文講到的水平拆分裡主鍵設計方案基本上派不上用場,因為這兩種水平拆分的出發點是不同的,那麼使用的手段和達到效果也將不一樣。
由上所述,我們可以把資料庫的水平拆分重新定義下,我在這幾篇文章裡一直講述的水平拆分本質是從資料庫技術來定義的,我把它們稱為狹義的水平拆分,與狹義相對的就是廣義的水平拆分,例如上文例子裡把交易表根據業務特性分為實時交易表和歷史交易表,這種行為也是一種水平拆分,但是這個拆分不會遵守我前面講到主鍵設計方案,但是它的確達到水平拆分的目的,所以這樣的水平拆分就屬於廣義的水平拆分了。
第三個演進過程:到了三個演進過程我們就會考慮到真正的水平拆分了,也就是上面提到的狹義的水平拆分了,狹義的水平拆分執行的理由有兩個,一個那就是資料量太大了,另一個是資料表的讀寫的關聯性很難進行拆分了,這點和垂直拆分有所不同,做垂直拆分的考慮不一定是因為資料量過大,例如某種表資料量不大,但是負載過重,很容易讓資料庫達到連線的極限值,我們也會採取垂直拆分手段來解決問題,此外,我們想減輕寫操作和讀操作的關聯性,從而能單獨對有瓶頸的寫操作或讀操作做優化設計,那麼我們也會考慮到垂直拆分,當然資料量實在是太大的表我們想優化,首先也會考慮到垂直拆分,因為垂直拆分是針對海量資料優化的起始手段,但是垂直拆分可不一定能解決海量資料的問題。
狹義水平拆分的使用的前提是因為資料量太大,到底多大了,我們舉個例子來說明下,假如某個電商平臺一天的交易筆數有2億筆,我們用來儲存資料的關係資料庫單表記錄到了5千萬條後,查詢效能就會嚴重下降,那麼如果我們把這兩億條資料全部存進這個資料庫,那麼隨著資料的累積,實時交易查詢基本已經沒法正常完成了,這個時候我們就得考慮把實時交易表進行狹義的水平拆分,狹義的水平拆分首先碰到的難點就是主鍵設計的問題,主鍵設計問題也就說明狹義水平拆分其實解決的是海量資料寫的問題,如果這張表讀操作很少,或者基本沒有,這個水平拆分是很好設計的,但是一張表只寫不讀,對於作為業務系統的後臺資料庫那基本是非常罕見的,。
前文講到的主鍵設計方案其實基本沒有什麼業務上的意義,它解決的主要問題是讓寫入的資料分佈均勻,從而能合理使用儲存資源,但是這個合理分散式儲存資源卻會給查詢操作帶來極大的問題,甚至有時可以說狹義水平拆分後資料查詢變得困難就是由這種看起來合理的主鍵設計方案所致。
我們還是以實時交易表的例項來說明問題,一個電商平臺下會接入很多不同的商戶,但是不同的商戶每天產生的交易量是不同,也就是說商戶的維度會讓我們使交易資料變得嚴重的不均衡,可能電商平臺下不到5%的商戶完成了全天交易量的80%,而其他95%的商戶僅僅完成20%的交易量,但是作為業務系統的資料表,進行讀操作首先被限制和約束的條件就是商戶號,如果要為我們設計的實時交易表進行狹義的水平拆分,做拆分前我們要明確這個拆分是由交易量大的少量商戶所致,而不是全部的商戶所致的。如果按照均勻分佈主鍵的設計方案,不加商戶區分的分佈資料,那麼就會發生產生少量交易資料的商戶的查詢行為也要承受交易量大的商戶資料的影響,而能產生大量交易資料的商戶也沒有因為自己的貢獻度而得到應有的高階服務,碰到這個問題其實非常好解決,就是在做狹義水平拆分前,我們先做一次廣義的水平拆分,把交易量大的商戶交易和交易量小的商戶交易拆分出來,交易量小的商戶用一張表記錄,這樣交易量小的商戶也會很happy的查詢出需要的資料,心裡也是美滋滋的。接下來我們就要對交易量大的商戶的交易表開始做狹義的水平拆分了,為這些重點商戶做專門的定製化服務。
做狹義水平拆分前,我們有個問題需要過一下,在狹義水平拆分前我們需要先做一下廣義的水平拆分嗎?這個我這裡不好說,具體要看實際的業務場景,但是針對我列舉的實時交易的例子而言,我覺得沒那個必要,因此拆分出的重點商戶交易量本來就很大,每個都在挑戰資料庫讀能力的極限,更重要的是實時交易資料的時間粒度已經很小了,再去做廣義水平拆分難度很大,而且很難做好,所以這個時候我們還是直接使用狹義的水平拆分。拆分完畢後我們就要解決查詢問題了。
做實時查詢的標準做法就是分頁查詢了,在講述如何解決分頁查詢前,我們看看我們在淘寶裡搜尋【衣服】這個條件的分頁情況,如下圖所示:
我們看到一共才100頁,淘寶上衣服的商品最多了,居然搜尋出來的總頁數只有100頁,這是不是在挑戰我們的常識啊,淘寶的這個做法也給我們在實現水平拆分後如何做分頁查詢一種啟迪。要說明這個啟迪前我們首先要看看傳統的分頁是如何做的,傳統分頁的做法是首先使用select count(1) form table這樣的語句查詢出需要查詢資料的總數,然後再根據每頁顯示的記錄條數,查詢出需要顯示的記錄,然後頁面根據記錄總數,每頁的條數,和查詢的結果來完成分頁查詢。回到我們的交易表例項裡,有一個重要商戶在做實時交易查詢,可是這個時候該商戶已經產生了1千萬筆交易了,假如每頁顯示10條,記錄那麼我們就要分成100萬頁,這要是真顯示在頁面上,絕對能讓我們這些開發人員像哥倫布發現新大陸那樣驚奇,反正我見過的最多分頁也就是200多頁,還是在百度搜索發現的。其實當資料庫一張表的資料量非常大的時候,select的count查詢效率就非常低下,這個查詢有時也會近似個全表檢索,所以count查詢還沒結束我們就會失去等待結果的耐心了,更不要是說等把資料查詢出來了,所以這個時候我們可以學習下淘寶的做法,當商戶第一次查詢我們准許他查詢有限的資料。我自己所做的一個專案的做法就是這樣的,當某個商戶的交易量實在是很大時候我們其實不會計算資料的總筆數,而是一次性查詢出1000條資料,這1000條資料查詢出來後存入到快取裡,頁面則只分100頁,當用戶一定要查詢100頁後的資料,我們再去追加查詢,不過實踐下來,商戶基本很少會查詢100頁後的資料,常常看了5,6頁就會停止查詢了。不過商戶也時常會有查詢全部資料的需求,但是商戶有這種需求的目的也不是想在分頁查詢裡看的,一般都是為了比對資料使用的,這個時候我們一般是提供一個發起下載查詢全部交易的功能頁面,商戶根據自己的條件先發起這樣的需求,然後我們系統會在後臺單獨起個執行緒查詢出全部資料,生成一個固定格式的檔案,最後通過一些有效手段通知商戶資料生成好了,讓商戶下載檔案即可。
對於進行了狹義水平拆分的表做分頁查詢我們通常都不會是全表查詢,而是抽取全域性的資料的一部分結果呈現給使用者,這個做法其實和很多市場調查的方式類似,市場調查我們通常是找一些樣本採集相關資料,通過分析這些樣本資料推匯出全域性的一個發展趨勢,那麼這些樣本選擇的合理性就和最終的結論有很大關係,回到狹義水平拆分的表做分頁查詢,我們為了及時滿足使用者需求,我們只是取出了全部資料中的一部分,但是這一部分資料是否滿足使用者的需求,這個問題是很有學問的,如果是交易表,我們往往是按時間先後順序查詢部分資料,所以這裡其實使用到了一個時間的維度,其他業務的表可能這個維度會不一樣,但肯定是有個維度約束我們到底返回那些部分的資料。這個維度可以用一個專有的名詞指代那就是排序,具體點就是要那個欄位進行升序還是降序查詢,看到這裡肯定有人會有異議,那就是這種抽樣式的查詢,肯定會導致查詢的命中率的問題,即查出來的資料不一定全部都是我們要的,其實要想讓資料排序正確,最好就是做全量排序,但是一到全量排序那就是全表查詢,做海量資料的全表排序查詢對於分頁這種場景是無法完成的。回到淘寶的例子,我們相信淘寶肯定沒有返回全部資料,而是抽取了部分資料分頁,也就是淘寶查詢時候加入了維度,每個淘寶的店家都希望自己的商戶放在搜尋的前列,那麼淘寶就可以讓商家掏錢,付了錢以後淘寶改變下商家在這個維度裡的權重,那麼商家的商品就可以排名靠前了。
狹義水平拆分的本身對排序也有很大的影響,水平拆分後我們一個分頁查詢可能要從不同資料庫不同的物理表裡去取資料,單表下我們可以先通過資料庫的排序演算法得到一定的資料,但是區域性的排序到了全域性可能就不正確了,這個又該怎麼辦了?其實由上面內容我們可以知道要滿足對海量資料的所有查詢限制是非常難的,時常是根本就無法滿足,我們只能做到儘量多滿足些查詢限制,也就是海量查詢只能做到儘量接近查詢限制的條件,而很難完全滿足,這個時候我前面提到的主鍵分佈方案就能起到作用了,我們在設計狹義水平拆分表主鍵分佈時候是儘量保持資料分佈均衡,那麼如果我們查詢要從多張不同物理表裡取的時候,例如我們要查1000條資料,而狹義水平拆分出了兩個物理資料庫,那麼我們就可以每個資料庫查詢500條,然後在服務層歸併成1000條資料,在服務層排序,這種場景下如果我們的主鍵設計時候還包含點業務意義,那麼這個排序的精確度就會得到很大提升。假如使用者對排序不敏感,那就更好做了,分頁時候如果每頁規定顯示10條,我們可以把10條資料平均分配給兩個資料庫,也就是顯示10條A庫的資料,再顯示5條B庫的資料。
看到這裡有些細心的朋友可能還會有疑問,那就是居然排序是分頁查詢的痛點,那麼我們可以不用資料庫查詢,而使用搜索技術啊,NoSql資料庫啊,的確這些技術可以更好的解決分頁問題,但是關係資料庫過渡到搜尋引擎和NoSql資料庫首先需要我們轉化資料,而狹義的水平拆分的資料表本身資料量很大,這個轉化過程我們是沒法快速完成的,如果我們對延時容忍度那麼高,其實我們就沒必要去做資料庫的狹義水平拆分了。這個問題反過來說明了使用狹義拆分資料表的業務場景,那就是:針對資料量很大的表同時該表的讀寫的關聯性是沒法有效拆分的。
最後我要講的是,如果系統到了狹義水平拆分都沒法解決時候,我們就要拋棄傳統的關係資料方案了,將該業務全部使用NoSql資料庫解決或者像很多大型網際網路公司那樣,改寫開源的mysql資料庫。文章寫道這裡,我還是想說一個觀點,如果一個系統有很強烈需求去做狹義的水平拆分,那麼這個公司的某個業務那肯定是非常的大了,所以啊,這個方案以公司為單位應該有點小眾了。
好了,今天寫到這裡,祝大家晚安,生活愉快。
相關推薦
關於大型網站技術演進的思考(六)--儲存的瓶頸(6)
在講資料庫水平拆分時候,我列出了水平拆分資料庫需要解決的兩個難題,它們分別是主鍵的設計問題和單表查詢的問題,主鍵問題前文已經做了比較詳細的講述了,但是第二個問題我沒有講述,今天我將會講講如何解決資料表被水平拆分後的單表查詢問題。 要解決資料表被水平拆分後的單表查詢問題,我們首先要回到問題的源頭,我們
關於大型網站技術演進的思考(十六)--網站靜態化處理—前後端分離—下(8)
我第一次聽說nodejs技術大概是在2009年年末,不過我真正認真在網路上進一步瞭解nodejs還是在2010年年中,當時對nodejs的認識和我現在對nodejs的認識有著天壤的區別,開始想了解nodejs我只是為了感慨谷歌公司開發的V8引擎居然如此強大,它不僅僅可以作為chrome瀏覽器的javasc
關於大型網站技術演進的思考(六):儲存的瓶頸(6)
在講資料庫水平拆分時候,我列出了水平拆分資料庫需要解決的兩個難題,它們分別是主鍵的設計問題和單表查詢的問題,主鍵問題前文已經做了比較詳細的講述了,但是第二個問題我沒有講述,今天我將會講講如何解決資料表被垂直拆分後的單表查詢問題。 要解決資料表被水平拆分後的單表查詢
關於大型網站技術演進的思考(五)--存儲的瓶頸(5)
做了 技術分享 表數 例子 執行 同時 設備 系統重啟 拆分 原引:http://www.cnblogs.com/sharpxiajun/p/4265853.html 上文裏我遺留了兩個問題,一個問題是數據庫做了水平拆分以後,如果我們對主鍵的設計采取一種均勻分布的策略,那麽
關於大型網站技術演進的思考(十四)--網站靜態化處理—前後端分離—上(6)
前文講到了CSI技術,這就說明網站靜態化技術的講述已經推進到了瀏覽器端了即真正到了web前端的範疇了,而時下web前端技術的前沿之一就是前後端分離技術了,那麼在這裡網站靜態化技術和前後端分離技術產生了交集,所以今天我將討論下前後端分離技術,前後端分離技術討論完後,下一篇文章我將會以網站靜態化技術的角度回過
關於大型網站技術演進的思考(十)--網站靜態化處理—動靜整合方案(2)
上篇文章我簡要的介紹了下網站靜態化的演進過程,有朋友可能認為這些知識有點過於稀鬆平常了,而且網站靜態化的技術基點也不是那麼高深和難以理解,因此它和時下日新月異的web前端技術相比,就顯得不倫不類了。其實當我打算寫本系列的之前我個人覺得web前端有一個點是很多人都知道重要,但是有常常低估它作用的,那就是we
關於大型網站技術演進的思考(二)--儲存的瓶頸(2)
上篇裡我講到某些網站在高併發下會報出503錯誤,503錯誤的含義是指網站服務端暫時無法提供服務的含義,503還表達了網站服務端現在有問題但是以後可能會提供正常的服務,對http協議熟悉的人都知道,5開頭的響應碼錶達了服務端出現了問題,在我們開發測試時候最為常見的是500錯誤,500代表的含義是服務端程式出
關於大型網站技術演進的思考(十五)--網站靜態化處理—前後端分離—中(7)
上篇裡我講到了一種前後端分離方案,這套方案放到服務端開發人員面前比放在web前端開發人員面前或許得到的掌聲會更多,我想很多資深前端工程師看到這樣的技術方案可能會有種說不出來的矛盾心情,當我的工作逐漸走向越來越專業化的前端開發後,我就時常被這套前後端分離方案所困惑,最近我終於明白了這個困惑的本源在哪裡了,那
關於大型網站技術演進的思考(十七)--網站靜態化處理—滿足靜態化的前後端分離(9)
前後端分離的主題雖然講完了,但是前後端分離的內容並沒有結束,本篇將繼續前後端分離的問題,只不過這次前後端分離的講述將會圍繞著本系列的主題網站靜態化進行。在講本篇主題之前,我需要糾正一下前後端分離主題講述中會讓朋友們產生誤導的地方,這種誤導就是對時下流行的一些前後端分離方案(沒有使用nodejs的前後端分離
關於大型網站技術演進的思考(九)--網站靜態化處理--總述(1)
在儲存瓶頸的開篇我提到像hao123這樣的導航網站只要它部署的web伺服器數量足夠,它可以承載超大規模的併發訪問量,如果是一個動態的網站,特別是使用到了資料庫的網站是很難做到通過增加web伺服器數量的方式來有效的增加網站併發訪問能力的。但是現實情況是像淘寶、京東這樣的大型動態網站在承擔高併發的情況下任然能
關於大型網站技術演進的思考(十九)--網站靜態化處理—web前端優化—上(11)
對於一個網路請求的處理,是由兩個不同型別的操作共同完成,這兩個操作是CPU的計算操作和IO操作,如果我們以處理效率角度來評判這兩個操作,CPU操作效率是光速的,而IO操作就不盡然了,計算機裡的IO操作就是對儲存資料介質的操作,計算機裡有如下幾個介質可以儲存資料,它們分別是:CPU的一級快取、二級快取、記憶
關於大型網站技術演進的思考(十一)--網站靜態化處理—動靜分離策略(3)
前文裡我講到了網站靜態化的關鍵點是動靜分離,動靜分離是讓動態網站裡的動態網頁根據一定規則把不變的資源和經常變的資源區分開來,動靜資源做好了拆分以後,我們就可以根據靜態資源的特點將其做快取操作,這就是網站靜態化處理的核心思路。由此可見,網站靜態化處理的核心就是動靜分離和快取兩大方面,上篇我簡單講述了動靜整合
關於大型網站技術演進的思考(一)--儲存的瓶頸(1)
前不久公司請來了位網際網路界的技術大牛跟我們做了一次大型網站架構的培訓,兩天12個小時資訊量非常大,知識的廣度和難度也非常大,培訓完後我很難完整理出全部聽到的知識,今天我換了個思路是回味這次培訓,這個思路就是通過本人目前的經驗和技術水平來思考下大型網站技術演進的過程。 首先我們要思考一個問題,什麼樣
關於大型網站技術演進的思考(四)--儲存的瓶頸(4)
如果資料庫需要進行水平拆分,這其實是一件很開心的事情,因為它代表公司的業務正在迅猛的增長,對於開發人員而言那就是有不盡的專案可以做,雖然會感覺很忙,但是人過的充實,心裡也踏實。 資料庫水平拆分簡單說來就是先將原資料庫裡的一張表在做垂直拆分出來放置在單獨的資料庫和單獨的表裡後更進一步的把本來是一個整體
關於大型網站技術演進的思考(七)--儲存的瓶頸(7)
本文開篇提個問題給大家,關係資料庫的瓶頸有哪些?我想有些朋友看到這個問題肯定會說出自己平時開發中碰到了一個跟資料庫有關的什麼什麼問題,然後如何解決的等等,這樣的答案沒問題,但是卻沒有代表性,如果出現了一個新的儲存瓶頸問題,你在那個場景的處理經驗可以套用在這個新問題上嗎?這個真的很難說。 其實不管什麼
關於大型網站技術演進的思考(十八)--網站靜態化處理—反向代理(10)
反向代理也是一種可以幫助實現網站靜態化的重要技術,今天我就來講講反向代理這個主題。那麼首先我們要了解下什麼是反向代理。和反向代理相對應的是正向代理,正向代理也就是我們常說的代理服務,正向代理是非常常見的,例如在某些公司裡我們想使用網際網路,那麼我們就得在瀏覽器裡設定一個代理伺服器,通過代理伺服器我們才能正
關於大型網站技術演進的思考(五)--儲存的瓶頸(5)
上文裡我遺留了兩個問題,一個問題是資料庫做了水平拆分以後,如果我們對主鍵的設計採取一種均勻分佈的策略,那麼它對於被水平拆分出的表後續的查詢操作將有何種影響,第二個問題就是水平拆分的擴容問題。這兩個問題在深入下去,本系列就越來越技術化了,可能最終很多朋友讀完後還是沒有找到解決實際問題的啟迪,而且我覺得這些問
關於大型網站技術演進的思考(三)--儲存的瓶頸(3)
儲存的瓶頸寫到現在就要進入到深水區了,如果我們所做的網站已經到了做資料庫垂直拆分和水平拆分的階段,那麼此時我們所面臨的技術難度的挑戰也會大大增強。 這裡我們先回顧下資料庫的垂直拆分和水平拆分的定義: 垂直拆分:把一個數據庫中不同業務單元的資料分到不同的資料庫裡。 水平拆分:是根據一定的規
關於大型網站技術演進的思考(十三)--網站靜態化處理—CSI(5)
講完了SSI,ESI,下面就要講講CSI了 ,CSI是瀏覽器端的動靜整合方案,當我文章發表後有朋友就問我,CSI技術是不是就是通過ajax來載入資料啊,我當時的回答只是說你的理解有點片面,那麼到底什麼是CSI技術了?這個其實要和動靜資源整合的角度來定義。 CSI技術其實是在頁面進行動靜分離後,將頁面
關於大型網站技術演進的思考(十二)--網站靜態化處理—快取(4)
上篇我補充了下SSI的知識,SSI是一個十分常見的技術,記得多年前我看到很多入口網站頁面的字尾是.shtml,那麼這就說明很多入口網站都曾經使用過SSI技術,其實現在搜狐網站也還在用shtml,如下圖所示: 由此可見SSI在網際網路的應用還是非常廣泛的。其實網際網路很多網頁如果我們按照動靜分離