關於大型網站技術演進的思考(五)--存儲的瓶頸(5)[轉]

 上文裏我遺留了兩個問題,一個問題是數據庫作了水平拆分之後,若是咱們對主鍵的設計採起一種均勻分佈的策略,那麼它對於被水平拆分出的表後續的查詢操做將有何種影響,第二個問題就是水平拆分的擴容問題。這兩個問題在深刻下去,本系列就愈來愈技術化了,可能最終不少朋友讀完後仍是沒有找到解決實際問題的啓迪,並且我以爲這些問題都是像BAT這樣巨型互聯網公司纔會認真思考的,所以本篇我打算換個角度來闡述本文的後續內容。html

  這裏咱們首先要明確一個問題,究竟是什麼因素促使咱們去作數據庫的垂直拆分和水平拆分的呢?答案很簡單就是業務發展的需求,前文裏的水平拆分技術方案基本都是拋棄變幻無窮的業務規則的限制,儘可能將水平拆分的問題歸爲一個簡單的技術實現方案,而純技術手段時常是看起來很美,可是到了面對現實問題時候,經常會變得那麼蒼白和無力。mysql

  水平拆分的難題裏我還有個難題沒有講述,就是水平拆分後對查詢操做的影響,特別是對單表查詢的影響,這點估計也是大夥最爲關心的問題,今天我不在延着水平拆分的技術手段演進是闡述上文的遺留問題,而是我要把前面提到的技術手段和一些典型場景結合起來探討如何解決網站存儲的瓶頸問題。sql

  前文中我總結過一個解決存儲瓶頸的脈絡,具體以下:數據庫

  單庫數據庫-->數據庫讀寫分離-->緩存技術-->搜索技術-->數據的垂直拆分-->數據的水平拆分緩存

  這個脈絡給一些朋友產生了誤解,就是認爲這個過程應該是個串行的過程,其實在實際的場景下這個過程每每是並行的,可是裏面有一個元素應該是串行的或者說思考時候有個前後問題,那就是對數據庫層的操做,具體以下:數據結構

  單庫數據庫-->數據庫讀寫分離-->數據的垂直拆分-->數據的水平拆分oop

  而緩存技術和搜索技術在數據庫的任意階段裏均可以根據實際的業務需求隨時切入其中幫助數據庫減輕沒必要要的壓力。例如,當網站的後臺數據庫仍是單庫的時候,數據庫漸漸出現了瓶頸問題,而這個瓶頸又沒有達到須要採起大張旗鼓作讀寫分離方案的程度,那麼我這個時候能夠考慮引入緩存機制。不過要合理的使用緩存咱們首先要明確緩存自己的特色,這些特色以下所示:性能

  特色一:緩存主要是適用於讀操做,而且緩存的讀操做的效率要遠遠高於從數據庫以及硬盤讀取數據的效率。學習

  特色二:緩存的數據是存儲在內存當中,所以當系統重啓,宕機等等異常場景下,緩存數據就會不可逆的丟失,且沒法恢復,所以緩存不能做爲可靠存儲設備,這就致使一個問題,緩存裏的數據必須首先從數據庫裏同步到內存中,而使用緩存的目的就是爲了解決數據庫的讀操做效率低下的問題,數據庫的數據同步到緩存的操做會由於數據庫的效率低下而在性能上大打折扣,因此緩存適合的場景是那些固定不變的數據以及業務對實時性變化要求不高的數據。網站

  根據緩存的上述兩個特色,咱們能夠把數據庫裏和上述描述相似操做的相關數據遷移到緩存裏,那樣咱們就從數據庫上剝離了那些對數據庫價值不高的操做,讓數據庫專心作有價值的操做,這樣也是減輕數據庫壓力的一種手段。

  不過這個手段侷限性很強,侷限性主要是一臺計算機了用於存儲緩存的內存的大小都是遠遠要低於硬盤,而且內存的價格要遠貴於硬盤,若是咱們將大規模的數據從硬盤往內存遷移,從資源成本和利用率角度考慮性價比仍是很低的,所以緩存每每都是用於轉存那些不會常常變化的數據字典,以及常常會被讀,而修改較少的數據,可是這些數據的規模也是有必定限度的,所以當單庫數據庫出現了瓶頸時候立刻就着手進行讀寫分離方案的設計性價比仍是很高的。

  前文我講到咱們之因此選擇數據庫讀寫分離是主要緣由是由於數據庫的讀寫比例嚴重失衡所致,可是作了讀寫分離必然有個問題不可避免,寫庫向讀庫同步數據必定會存在必定的時間差,若是咱們想減少讀庫和寫庫數據的時間差,那麼任然會致使讀庫由於寫的粒度過細而發生部分性能的損失,可是時間差過大,或許又會沒法知足實際的業務需求,所以這個時間差的設計必定要基於實際的業務需求合理的設計。

  同步的時間差的問題仍是個小問題,也比較好解決,可是如何根據實際的業務需求作讀寫分離這其實仍是很是有挑戰性的,這裏我舉個很常見的例子來講明讀寫分離的難度問題,咱們這裏以淘寶爲例,淘寶是個C2C的電商網站,它是互聯網公司提供一個平臺,商家自助接入這個平臺,在這個平臺上賣東西,這個和線下不少大賣場的模式相似。淘寶是個大平臺,它的交易表裏必定是要記下全部商戶的交易數據,可是針對單個商家他們只會關心本身的網店的銷售數據,這就有一個問題了,若是某一個商家要查詢本身的交易信息,淘寶就要從成千上萬的交易信息裏檢索出該商家的交易信息,那麼若是咱們把全部交易信息放在一個交易表裏,確定有商家會有這樣的疑問,個人網店天天交易額不大,爲何我查詢交易數據的速度和那些大商家同樣慢了?那麼咱們到底該如何是解決這樣的場景了?

  碰到這樣的狀況,當網站的交易規模變大後就算咱們把交易表作了讀寫分離估計也是無法解決實際的問題,就算咱們作的完全點把交易表垂直拆分出來估計仍是解決不了問題,由於一個業務數據庫擁有不少張表,可是真正壓力大的表畢竟是少數,這個符合28原則,而數據庫大部分的關鍵問題又都是在那些數據壓力大的表裏,就算咱們把這些表單獨作讀寫分離甚至作垂直拆分,其實只是把數據庫最大的問題遷移出原來數據庫,而不是在解決該表的實際問題。

  若是咱們要解決交易表的問題咱們首先要對交易表作業務級的拆分,那麼咱們要爲交易表增長一個業務維度:實時交易和歷史交易,通常而言實時交易以當天及當天24小時爲界,歷史交易則是除去當天交易外的全部歷史交易數據。實時交易數據和歷史交易數據有着很大不一樣,實時交易數據讀與寫是比較均衡的,不少時候估計寫的頻率會遠高於讀的頻率,可是歷史交易表這點上和實時交易就徹底不一樣了,歷史交易表的讀操做頻率會遠大於寫操做頻率,若是咱們將交易表作了實時交易和歷史交易的拆分後,那麼讀寫分離方案適合的場景是歷史交易查詢而非實時交易查詢,不過歷史交易表的數據是從實時交易表裏同步過來的,根據這兩張表的業務特性,咱們能夠按以下方案設計,具體以下:

  咱們能夠把實時交易表設計成兩張表,把它們分別叫作a表和b表,a表和b表按天交替進行使用,例現在天咱們用a表記錄實時交易,明天咱們就用b表記錄實時交易,固然咱們事先能夠用個配置表記錄今天到底使用那張表進行實時交易記錄,爲何要如此麻煩的設計實時交易表了?這麼作的目的是爲了實時交易數據同步到歷史數據時候提供便利,通常咱們會在凌晨0點切換實時交易表,過時的實時交易表數據會同步到歷史交易表裏,這個時候須要數據遷移的實時交易表是全表數據遷移,效率是很是低下,假如實時交易表的數據量很大的時候,這種導入同步操做會變得十分耗時,因此咱們設計兩張實時交易表進行切換來把數據同步的風險降到最低。因而可知,歷史交易表天天基本都只作一次寫操做,除非同步出了問題,纔會重複進行寫操做,可是寫的次數確定是很低的,因此歷史交易表的讀寫比例失衡是很是嚴重的。不過實時交易表的切換也是有技術和業務風險的,爲了保證明時交易表的高效性,咱們通常在數據同步操做成功後會清空實時交易表的數據,可是咱們很難保證這個同步會不會有問題,所以同步時候咱們最好作下備份,此外,兩個表切換的時候確定會碰到這樣的場景,就是有人在凌晨0點前作了交易,可是這個交易是在零點後作完,假如實時交易表會記錄交易狀態的演變過程,那麼在切換時候就有可能兩個實時表的數據沒有作好接力,所以咱們同步到歷史交易表的數據必定要保持一個原則就是已經完成交易的數據,沒有完成的交易數據兩張實時交易還要完成一個業務上的接力,這就是業界常說的數據庫日切的問題。

  歷史交易表自己就是爲讀使用的,因此咱們從業務角度將交易表拆分紅實時交易表和歷史交易表自己就是在爲交易表作讀寫分離,竟然了設計了歷史交易表咱們就作的乾脆點,把歷史交易表作垂直拆分,將它從原數據庫裏拆分出來獨立建表,隨着歷史交易的增大,上文裏所說的某個商戶想快速檢索出本身的數據的難題並無獲得根本的改善,爲了解決這個難題咱們就要分析下難題的根源在那裏。這個根源很簡單就是咱們把全部商戶的數據不加區別的放進了一張表裏,無論是交易量大的商戶仍是交易量小的商戶,想要查詢出本身的數據都要進行全表檢索,而關係數據庫單表記錄達到必定數據量後全表檢索就會變的異常低效,例如DB2當數據量超過了1億多,mysql單表超過了100萬條後那麼全表查詢這些表的記錄都會存在很大的效率問題,那麼咱們就得對歷史交易表進一步拆分,由於問題根源是單表數據量太大了,那咱們就能夠對單表的數據進行拆分,把單表分紅多表,這個場景就和前面說的水平拆分裏把原表變成邏輯表,原表的數據分散到各個獨立的邏輯表裏的方式一致,不過這裏咱們沒有一開始作水平拆分,那是會把問題變麻煩,咱們只要在一個數據庫下對單表進行拆分便可,這樣也能知足咱們的要求,而且避免了水平拆分下的跨庫寫做的難題。接下來咱們又有一個問題了那就是咱們按什麼維度拆分這張單表呢?

  咱們按照前文講到的水平拆分裏主鍵設計方案執行嗎?固然不行哦,由於那些方案明顯提高不了商戶檢索數據的效率問題,因此咱們要首先分析下商戶檢索數據的方式,商戶通常會按這幾個維度檢索數據,這些維度分別是:商戶號、交易時間、交易類型,固然還有其餘的維度,我這裏就以這三個維度爲例闡述下面的內容,商戶查詢數據效率低下的根本緣由是全表檢索,其實商戶查詢至少有一個維度那就是商戶號來進行查詢,若是咱們把該商戶的數據存入到一張單獨的表裏,天然查詢的效率會有很大的提高,可是在實際系統開發裏咱們不多經過商戶號進行拆分表,這是爲何呢?由於一個電商平臺的商戶是個動態的指標,會常常發生變化,其次,商戶號的粒度很細,若是使用商戶號拆分表的必然會有這樣的後果那就是咱們可能要頻繁的建表,隨着商戶的增長表的數量也會增長,形成數據的碎片化,同時不一樣的商戶交易量是不同的,按商戶建表會形成數據存儲的嚴重不平衡。若是使用交易類型來拆分表,雖然維度的粒度比商戶號小,可是會形成數據的分散化,也就是說咱們查詢一個商戶的所有交易數據會存在很大問題。因而可知拆表時候如何有效的控制維度的粒度以及數據的彙集度是拆分的關鍵所在,由於使用交易時間這個維度就會讓拆分更加合理,不過期間的維度的設計也是頗有學問的,下面咱們看看騰訊分析的維度,以下所示:

 

  騰訊分析的維度是今天這個其實至關於實時交易查詢,除此以外都是對歷史數據查詢,它們分爲昨天、最近7天和最近30天,咱們若是要對歷史交易表進行拆分也是能夠參照騰訊分析的維度進行,不過無論咱們選擇什麼維度拆分數據,那麼都是犧牲該維度成全了其餘維度,例如咱們按騰訊分析的維度拆分數據,那麼咱們想靈活使用時間查詢數據將會受到限制。

  咱們把歷史交易數據經過交易時間維度進行了拆分,雖然獲得了效率提高,可是歷史交易數據表是個累積表,隨着時間推移,首先是月表,接下來是周表都會由於數據累積產生查詢效率低下的問題,這個時候咱們又該如何解決了?這個時候咱們須要再引進一個維度,那麼這個時候咱們能夠選擇商戶號這個維度,可是商戶號做爲拆分維度是有必定問題的,由於會形成數據分佈不均衡,那麼咱們就得將維度的粒度由小變粗,其實一個電商平臺上每每少數商戶是完成了大部分電商平臺的交易,所以咱們能夠根據必定指標把重要商戶拆分出來,單獨建表,這樣就能夠平衡了數據的分佈問題。

  咱們總結下上面的案例,咱們會獲得不少的啓迪,我將這些啓迪總結以下:

  啓迪一:數據庫的讀寫分離不是簡單的把主庫數據導入到讀庫裏就能解決問題,讀數據庫和寫數據的分離的目的是爲了讓讀和寫操做不能相互影響效率。

  啓迪二:解決讀的瓶頸問題的本質是減小數據的檢索範圍,數據檢索的範圍越小,讀的效率也就越高;

  啓迪三:數據庫的垂直拆分和水平拆分首先不該該從技術角度進行,而是經過業務角度進行,若是數據庫進行業務角度的水平拆分,那麼拆分的維度每每是要根據該表的某個字段進行的,這個字段選擇要有必定原則,這個原則主要是該字段的維度的粒度不能過細,該字段的維度範圍不能常常的動態發生變化,最後就是該維度不能讓數據分佈嚴重失衡。

  回到現實的開發裏,對於一個數據庫作拆表,分表的工做實際上是一件很讓人惱火的工做,這主要是有如下緣由所形成的,具體以下所述:

  緣由一:一個數據庫其實容納多少張表是有必定限制的,就算沒有超過這個限制,若是原庫原本有30張表,咱們拆分後變成了60張,接着是120張,那麼數據庫自己管理這麼多表也會消耗不少性能,所以公司的DBA每每會控制那些過多分表的行爲。

  緣由二:每次拆表後,都會牽涉到歷史數據的遷移問題,這個遷移風險很大,遷移方案若是設計的不完善可能會致使數據丟失或者損壞,若是關鍵數據發生了丟失和損壞,結果可能很是致命。所以在設計數據庫分表分庫方案時候咱們要儘可能讓受影響的數據範圍變得最小。

  緣由三:每次拆表和分表都會讓系統的相關方繃緊神經,方案執行後,會有很長時間的監控和觀察期,因此拆數據庫時常是一件使人討厭的事情。

  緣由四:爲了保證新方案執行後確保系統沒有問題,咱們經常會讓新舊系統並行運行一段時間,這樣能夠保證若是新方案出現問題,問題的影響面最低,可是這種作法也有一個惡果就是會致使數據遷移方案要進行動態調整,從而增長遷移數據的風險

  所以當公司不得不作這件事情時候,公司都會很天然去考慮第三種解決方案,第三種解決方案是指儘可能不改變原數據庫的功能,而是另起爐竈,使用新技術來解決咱們的問題,例如前文所說的搜索技術解決數據庫like的低效問題就是其中方案之一,該方案只要咱們將數據庫的表按必定時間導入到文件系統,而後對文件創建倒排索引,讓like查詢效率更好,這樣就不用改變原數據庫的功能,又能減輕數據庫的壓力。

  如今經常使用的第三種解決方案就是使用NoSql數據庫,NoSql數據庫大多都是針對文件進行的,所以咱們能夠和使用搜索引擎那樣把數據導入到文件裏就好了,NoSql基本都採用Key/Value這種簡單的數據結構,這種數據結構和關係數據庫比起來更加的靈活,對原始數據的約束最少,因此在NoSql數據庫裏建表咱們能夠很靈活的把列和行的特性交叉起來用,這句話可能不少人不太理解,下面我舉個例子解釋下,例如hadoop技術體系裏的hbase,hbase是一個基於列族的數據庫,使用hbase時候咱們就能夠經過列來靈活的拆分數據,好比咱們能夠把中國的省份做爲一個列,將該省份的數據都放入到這個列下面,在省這個維度下咱們能夠接着在定義一個列的維度,例如軟件行業,屬於軟件行業的數據放在這個列下面,最終提供用戶查詢時候咱們就能夠減小數據檢索的範圍,最終達到提高查詢效率的目的。因而可知當咱們用慣了關係數據庫後,學習像hbase這樣的Nosql數據庫咱們會很是的不適應,由於關係數據庫的表有固定模式,也就是咱們常說的結構化數據,當表的定義好了後,就算裏面沒有數據,那麼這個結構也就固定了,咱們使用表的時候都是按這個模型下面,咱們幾乎感受不到它,可是到了hbase的使用就不一樣了,hbase使用時候咱們都在不停的爲數據增長結構化模型,並且這個維度是以列爲維度的,而關係數據庫裏列肯定後咱們使用時候是沒法改變的,這就是學習hbase的最大困難之一。Hbase之因此這麼麻煩的設計這樣的計算模型,終極目的就是爲了讓海量數據按不一樣維度存儲起來,使用時候盡全力檢索數據檢索的數量,從而達到海量數據快速讀取的目的。

  好了,今天就寫到這裏,祝你們生活愉快。

 

轉自 http://www.cnblogs.com/sharpxiajun/p/4265853.html

相關文章
相關標籤/搜索