MySQL9-可擴充套件性設計之資料切分
何謂資料切分
簡單來說,就是指通過某種特定的條件,將我們存放在同一個資料庫中的資料分散存放到多個數據庫(主機)上面,以達到分散單臺裝置負載的效果。資料的切分同時還可以提高系統的總體可用性,因為單臺裝置Crash 之後,只有總體資料的某部分不可用,而不是所有的資料。
資料的切分(Sharding)根據其切分規則的型別,可以分為兩種切分模式:一種是按照不同的表(或者Schema)來切分到不同的資料庫(主機)之上,這種切可以稱之為資料的垂直(縱向)切分;另外一種則是根據表中的資料的邏輯關係,將同一個表中的資料按照某種條件拆分到多臺資料庫(主機)上面,這種切分稱之為資料的水平(橫向)切分。
垂直切分的最大特點就是規則簡單,實施也更為方便,尤其適合各業務之間的耦合度非常低,相互影響很小,業務邏輯非常清晰的系統。在這種系統中,可以很容易做到將不同業務模組所使用的表分拆到不同的資料庫中。根據不同的表來進行拆分,對應用程式的影響也更小,拆分規則也會比較簡單清晰。
水平切分於垂直切分相比,相對來說稍微複雜一些。因為要將同一個表中的不同資料拆分到不同的資料庫中,對於應用程式來說,拆分規則本身就較根據表名來拆分更為複雜,後期的資料維護也會更為複雜一些。
資料的垂直切分
一個架構設計較好的應用系統,其總體功能肯定是由很多個功能模組所組成的,而每一個功能模組所需要的資料對應到資料庫中就是一個或者多個表。而在架構設計中,各個功能模組相互之間的互動點越統一越少,系統的耦合度就越低,系統各個模組的維護性以及擴充套件性也就越好。這樣的系統,實現資料的垂直切分也就越容易。
當我們的功能模組越清晰,耦合度越低,資料垂直切分的規則定義也就越容易。完全可以根據功能模組來進行資料的切分,不同功能模組的資料存放於不同的資料庫主機中,可以很容易就避免掉跨資料庫的Join 存在,同時系統架構也非常的清晰。
當然,很難有系統能夠做到所有功能模組所使用的表完全獨立,完全不需要訪問對方的表或者需要兩個模組的表進行Join 操作。這種情況下,我們就必須根據實際的應用場景進行評估權衡。決定是遷就應用程式將需要Join 的表的相關某快都存放在同一個資料庫中,還是讓應用程式做更多的事情,也就是程式完全通過模組介面取得不同資料庫中的資料,然後在程式中完成Join 操作。
垂直切分的優點
◆ 資料庫的拆分簡單明瞭,拆分規則明確;
◆ 應用程式模組清晰明確,整合容易;
◆ 資料維護方便易行,容易定位;
垂直切分的缺點
◆ 部分表關聯無法在資料庫級別完成,需要在程式中完成;
◆ 對於訪問極其頻繁且資料量超大的表仍然存在效能平靜,不一定能滿足要求;
◆ 事務處理相對更為複雜;
◆ 切分達到一定程度之後,擴充套件性會遇到限制;
◆ 過讀切分可能會帶來系統過渡複雜而難以維護。
資料的水平切分
簡單來說,我們可以將資料的水平切分理解為是按照資料行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其他的資料庫中。當然,為了能夠比較容易的判定各行資料被切分到哪個資料庫中了,切分總是都需要按照某種特定的規則來進行的。如根據某個數字型別欄位基於特定數目取模,某個時間型別欄位的範圍,或者是某個字元型別欄位的hash 值。如果整個系統中大部分核心表都可以通過某個欄位來進行關聯,那這個欄位自然是一個進行水平分割槽的上上之選了,當然,非常特殊無法使用就只能另選其他了。
水平切分的優點
◆ 表關聯基本能夠在資料庫端全部完成;
◆ 不會存在某些超大型資料量和高負載的表遇到瓶頸的問題;
◆ 應用程式端整體架構改動相對較少;
◆ 事務處理相對簡單;
◆ 只要切分規則能夠定義好,基本上較難遇到擴充套件性限制;
水平切分的缺點
◆ 切分規則相對更為複雜,很難抽象出一個能夠滿足整個資料庫的切分規則;
◆ 後期資料的維護難度有所增加,人為手工定位資料更困難;
◆ 應用系統各模組耦合度較高,可能會對後面資料的遷移拆分造成一定的困難。
垂直與水平聯合切分的使用
聯合切分的優點
◆ 可以充分利用垂直切分和水平切分各自的優勢而避免各自的缺陷;
◆ 讓系統擴充套件性得到最大化提升;
聯合切分的缺點
◆ 資料庫系統架構比較複雜,維護難度更大;
◆ 應用程式架構也相對更復雜;
資料切分及整合方案
總的來說,存在兩種解決思路:
1. 在每個應用程式模組中配置管理自己需要的一個(或者多個)資料來源,直接訪問各個資料庫,在模組內完成資料的整合;
2. 通過中間代理層來統一管理所有的資料來源,後端資料庫叢集對前端應用程式透明;
可能90%以上的人在面對上面這兩種解決思路的時候都會傾向於選擇第二種,尤其是系統不斷變得龐大複雜的時候。確實,這是一個非常正確的選擇,雖然短期內需要付出的成本可能會相對更大一些,但是對整個系統的擴充套件性來說,是非常有幫助的。
①自行開發中間代理層。
②利用MySQL Proxy 實現資料切分及整合。
③利用Amoeba 實現資料切分及整合。
④利用HiveDB 實現資料切分及整合
⑤其他實現資料切分及整合的解決方案
資料切分與整合中可能存在的問題
問題:引入分散式事務的問題
在MySQL 各個版本中,只有從MySQL 5.0 開始以後的各個版本才開始對分散式事務提供支援,而且目前僅有Innodb 提供分散式事務支援。不僅如此,即使我們剛好使用了支援分散式事務的MySQL 版本,同時也是使用的Innodb 儲存引擎,分散式事務本身對於系統資源的消耗就是很大的,效能本身也並不是太高。而且引入分散式事務本身在異常處理方面就會帶來較多比較難控制的因素。
其實我們可以可以通過一個變通的方法來解決這種問題,首先需要考慮的一件事情就是:是否資料庫是唯一一個能夠解決事務的地方呢?其實並不是這樣的,我們完全可以結合資料庫以及應用程式兩者來共同解決。各個資料庫解決自己身上的事務,然後通過應用程式來控制多個數據庫上面的事務。也就是說,只要我們願意,完全可以將一個跨多個數據庫的分散式事務分拆成多個僅處於單個數據庫上面的小事務,並通過應用程式來總控各個小事務。當然,這樣作的要求就是我們的應用程式必須要有足夠的健壯性,當然也會給應用程式帶來一些技術難度。
問題:跨節點Join 的問題
對待這類問題,我還是推薦通過應用程式來進行處理,在驅動表所在的MySQL Server中取出相應的驅動結果集,然後根據驅動結果集再到被驅動表所在的MySQL Server 中取出相應的資料。可能很多讀者朋友會認為這樣做對效能會產生一定的影響,是的,確實是會對效能有一定的負面影響,但是除了此法,基本上沒有太多其他更好的解決辦法了。而且,由於資料庫通過較好的擴充套件之後,每臺MySQL Server 的負載就可以得到較好的控制,單純針對單條Query 來說,其響應時間可能比不切分之前要提高一些,所以效能方面所帶來的負面影響也並不是太大。更何況,類似於這種需要跨節點Join 的需求也並不是太多,相對於總體效能而言,可能也只是很小一部分而已。所以為了整體效能的考慮,偶爾犧牲那麼一點點,其實是值得的,畢竟系統優化本身就是存在很多取捨和平衡的過程。
問題:跨節點合併排序分頁問題
如何解決?解決的思路大體上和跨節點Join 的解決類似,但是有一點和跨節點Join不太一樣,Join 很多時候都有一個驅動與被驅動的關係,所以Join 本身涉及到的多個表之間的資料讀取一般都會存在一個順序關係。但是排序分頁就不太一樣了,排序分頁的資料來源基本上可以說是一個表(或者一個結果集),本身並不存在一個順序關係,所以在從多個數據源取資料的過程是完全可以並行的。這樣,排序分頁資料的取數效率我們可以做的比跨庫Join 更高,所以帶來的效能損失相對的要更小,在有些情況下可能比在原來未進行資料切分的資料庫中效率更高了。當然,不論是跨節點Join 還是跨節點排序分頁,都會使我們的應用伺服器消耗更多的資源,尤其是記憶體資源,因為我們在讀取訪問以及合併結果集的這個過程需要比原來處理更多的資料。