存儲的壓力說白了就是隨著系統的演化,需求的增加,可能表的數量會逐漸增多,比如一段時間上個新功能就得加個表。並且隨著用戶量的增多類似用戶表的行數肯定會增多,訂單表的數據肯定會隨著時間而增多,當這種數據量達到千萬甚至上億的時候,讀寫分離就已經滿足不了,讀寫性能下降嚴重。 ...
之前一篇文章已經談到了資料庫集群之主從集群也就是讀寫分離,也提到了讀寫分離其實只是分擔了訪問的壓力,但是存儲的壓力沒有解決。
存儲的壓力說白了就是隨著系統的演化,需求的增加,可能表的數量會逐漸增多,比如一段時間上個新功能就得加個表。並且隨著用戶量的增多類似用戶表的行數肯定會增多,訂單表的數據肯定會隨著時間而增多,當這種數據量達到千萬甚至上億的時候,讀寫分離就已經滿足不了,讀寫性能下降嚴重。
也就是一臺伺服器的資源例如CPU、記憶體、IO、磁碟等是有限的,所以這時候分庫分表就上啦!
分庫
分庫講白了就是比如現在你有一個資料庫伺服器,資料庫中有兩張表分別是用戶表和訂單表。如果要分庫的話現在你需要買兩台機子,搞兩個資料庫分別放在兩台機子上,並且一個資料庫放用戶表,一個資料庫放訂單表
這樣存儲壓力就分擔到兩個伺服器上了,但是會帶來新的問題,所以東西變複雜了都會有新的問題產生。
1、聯表查詢問題 也就是join了,之前在一個資料庫裡面可以用上join用一條sql語句就可以聯表查詢得到想要的結果,但是現在分為多個資料庫了,所以join用不上了。就比如現在要查註冊時間在2019年之後用戶的訂單信息,你就需要先去資料庫A中用戶表查詢註冊在2019年之後的信息,然後得到用戶id,再拿這些id去資料庫B訂單表中查找訂單信息,然後再拼接這些信息返回。所以等於得多寫一些代碼了。
2、事務問題 搞資料庫基本上都離不開事務,但是現在不同的資料庫事務就不是以前那個簡單的本地事務了,而是分散式事務了,而引入分散式事務也提高了系統的複雜性,並且有些效率不高還會影響性能例如Mysql XA。還有基於消息中間件實現分散式事務的等等這裡不展開講述。
分表
我們已經做了分庫了,但是現在情況是我們的表裡面的數據太多了,就一不小心你的公司的產品火了,像抖音這種,所有用戶如果就存在一張表裡吃不消,所以這時候得分表。分別又分垂直分表和水平分表。
垂直分表
垂直分表的意思形象點就像坐標軸的y軸,把x軸切成了兩半,對應到我們的表就是比如我們表有10列,現在一刀切下去,分成了兩張表,其中一張表3列,另一張表7列。
這個一刀切下去讓兩個表分別有幾列不是固定的,垂直分表適合表中存在不常用並且占用了大量空間的表拆分出去。
就拿頭條的用戶信息,比如用戶表只有用戶id、昵稱、手機號、個人簡介這4個欄位。但是手機號和個人簡介這種信息就屬於不太常用的,占用的空間也不小,個人簡介有些人寫了一坨。所以就把手機號和個人簡介這兩列拆分出去。
那垂直分表影響就是之前只要一個查詢的,現在需要兩次查詢才能拿到分表之前的完整用戶表信息。
水平分表
水平分表的意思形象點就像坐標軸的x軸,把y軸切成了兩半(當然不僅限於切一刀,可以切好幾份)。也拿用戶表來說比如現在用戶表有5000萬行數據,我們切5刀,分成5個表,每個表1000萬行數據。
水平分表就適合用戶表行數很多的情況下,一般單表行數超過5000萬就得分表,如果單表的數據比較複雜那可能2000萬甚至1000萬就得分了,這個得看實際情況有些表很簡單可能一億行都不用分。所以當一個表行數超過千萬級別的時候關註一下,如果沒有性能問題就可以再等等看,不要急著分表,因為分表會是帶來很多問題。
水平分表的問題比垂直分表就更煩了。
要考慮怎麼切,講的高級點就叫路由
1、按id也就是範圍路由,比如id 值1999萬的放一張表,1000萬1999放一張表,一次類推。這個得試的,因為範圍分的大了,可能性能還有問題,範圍分的小了。。那表不得多死。
這種分法的好處就是容易切啊,簡單粗暴,以後新增的數據分表都不會影響到之前的數據,之前的數據都不需要移動。
2、哈希路由 就是取幾列哈希一下看看數據哪個庫,比如拿id來做哈希,1500取餘8等於4,所以這條記錄就放在user_4這個表中,2011取餘8等於3,所以這條記錄就放在user_3中。這種分法好處就是分的很均勻,基本上每個表的數據都差不多,但是以後新增數據又得分表了咋辦,以前的數據都得動,比較煩!
3、搞一張表來存儲路由關係 還是拿用戶表來說,就是弄一個路由表,裡面存userId和表編號,表示這個userId是這張user表的的。這種方式也簡單,之後又要分表了之後改改路由表,遷移一部分數據。但是這種方法導致每次查詢都得查兩次,並且如果路由表太大了,那路由表又成為瓶頸了!
再說說查詢時候的問題。
比如你要查註冊時間最早的前100名用戶,這就等於你得在水平分的每一張表都order by 一下註冊時間並且取100個,然後再把每個表的100個結果對比一下得到最終的結果。首先操作變麻煩了,以前一個order by就搞定的事情現在變的複雜了,而且還得考慮一個因素就是時間的問題,如果你拆成了20個表,那你得執行20個order by,如果是串列執行的話,這個時間開銷也是個問題!
分庫分表的實現
具體實現也分為程式代碼封裝、資料庫中間件封裝。實現難度會比讀寫分離更大,至於兩種封裝的比較在講讀寫分離時候已經說了,這裡不再贅述。
總結
說了這麼多好像分庫分表一點都不好啊,沒錯會引入很多問題,所以在架構設計要遵循演化原則,任何東西都不是一蹴而就的,在不同場景適配不同的架構,架構只有合適的,沒有一個架構可以適配任何場景。
在軟體中簡單夠用就是好的,技術沒有貴賤,不是用了分散式就牛逼,越複雜的系統維護的成本和難度越高,出現問題的幾率越大。這種架構的演化往往都是被用戶所驅動的,可以說是"不得已而為之"。
基本上單機資料庫可以支撐10萬用戶量級別。所以一般情況下像資料庫吃不消就升級硬體,優化資料庫配置、優化代碼、引入redis等。只有在真的不行了才上這些更複雜的東西。