標籤:
轉自(http://blog.sina.com.cn/s/blog_821512b50101hyc1.html)MySQL Sharding詳解
一 背景
我們知道,當資料庫中的資料量越來越大時,不論是讀還是寫,壓力都會變得越來越大。採用MySQL Replication多master多slave方案,在上層做負載平衡,雖然能夠一定程度上緩解壓力。但是當一張表中的資料變得非常龐大時,壓力還是 非常大的。試想,如果一張表中的資料量達到了千萬甚至上億層級的時候,不管是建索引,最佳化緩衝等,都會面臨巨大的效能壓力。
二 定義
資料sharding,也稱作資料切分,或分區。是指通過某種條件,把同一個資料庫中的資料分散到多個資料庫或多台機器上,以減小單台機器壓力。
三 分類
資料分區根據切分規則,可以分為兩類:
1、垂直切分
資料的垂直切分,也可以稱之為縱向切分。將資料庫想象成為由很多個一大塊一大塊的“資料區塊”(表)組成,我們垂直的將這些“資料區塊”切開,然後將他們分散 到多台資料庫主機上面。這樣的切分方法就是一個垂直(縱向)的資料切分。以表為單位,把不同的表分散到不同的資料庫或主機上。規則簡單,實施方便,適合業 務之間耦合度低的系統。
Sharding詳解" title="MySQL Sharding詳解" height="373" width="553">
垂直切分的優點
(1)資料庫的拆分簡單明了,拆分規則明確;
(2)應用程式模組清晰明確,整合容易;
(3)資料維護方便易行,容易定位;
垂直切分的缺點
(1)部分表關聯無法在資料庫層級完成,需要在程式中完成;
(2)對於訪問極其頻繁且資料量超大的表仍然存在效能平靜,不一定能滿足要求;
(3)交易處理相對更為複雜;
(4) 切分達到一定程度之後,擴充性會遇到限制;
(5)過讀切分可能會帶來系統過渡複雜而難以維護。
2、水平切分
一般來說,簡單的水平切分主要是將某個訪問極其平凡的表再按照某個欄位的某種規則來分散到多個表之中,每個表中包含一部分資料。以行為單位,將同一個表中的資料按照某種條件拆分到不同的資料庫或主機上。相對複雜,適合單表巨大的系統。
Sharding詳解" title="MySQL Sharding詳解" height="372" width="553">
水平切分的優點
(1)表關聯基本能夠在資料庫端全部完成;
(2)不會存在某些超大型資料量和高負載的表遇到瓶頸的問題;
(3)應用程式端整體架構改動相對較少;
(4)交易處理相對簡單;
(5)只要切分規則能夠定義好,基本上較難遇到擴充性限制;
水平切分的缺點
(1)切分規則相對更為複雜,很難抽象出一個能夠滿足整個資料庫的切分規則;
(2)後期資料的維護難度有所增加,人為手工定位元據更困難;
(3)應用系統各模組耦合度較高,可能會對後面資料的遷移拆分造成一定的困難。
3、聯合切分
實際的應用情境中,除了那些負載並不是太大,商務邏輯也相對較簡單的系統可以通過上面兩種切分方法之一來解決擴充性問題之外,恐怕其他大部分商務邏輯稍微 複雜一點,系統負載大一些的系統,都無法通過上面任何一種資料的切分方法來實現較好的擴充性,而需要將上述兩種切分方法結合使用,不同的情境使用不同的切 分方法。
Sharding詳解" title="MySQL Sharding詳解" height="480" width="342">
聯合切分的優點
(1)可以充分利用垂直切分和水平切分各自的優勢而避免各自的缺陷;
(2)讓系統擴充性得到最大化提升;
聯合切分的缺點
(1)資料庫系統架構比較複雜,維護難度更大;
(2)應用程式架構也相對更複雜;
四 實現方案
現在 Sharding 相關的軟體實現其實不少,基於資料庫層、DAO 層、不同語言下也都不乏案例。限於篇幅,此處只作一下簡要的介紹。
1、 Mysql Proxy + HASCALE
一套比較有潛力的方案。其中MySQL Proxy 是用 Lua 指令碼實現的,介於用戶端與伺服器端之間,扮演 Proxy 的角色,提供查詢分析、失敗接管、查詢過濾、調整等功能。目前的 0.6 版本還做不到讀、寫分離。HSCALE 則是針對 MySQL Proxy 外掛程式,也是用 Lua 實現的,對 Sharding 過程簡化了許多。需要指出的是,MySQL Proxy 與 HSCALE 各自會帶來一定的開銷,但這個開銷與集中式資料處理方式單條查詢的開銷還是要小的。
MySQLProxy是MySQL官方提供的一個資料庫代理層產品,和MySQLServer一樣,同樣是一個基於GPL開源協議的開源產品。可用來監視、分析或者傳輸他們之間的通訊資訊。他的靈活性允許你最大限度的使用它,目前具備的功能主要有連線路由,Query分析,Query過濾和修改,負載平衡,以及基本的HA機制等。
實際上,MySQLProxy本身並不具有上述所有的這些功能,而是提供了實現上述功能的基礎。要實現這些功能,還需要通過我們自行編寫LUA指令碼來實現。
MySQLProxy實際上是在用戶端請求與MySQLServer之間建立了一個串連池。所有用戶端請求都是發向MySQLProxy,然後經由MySQLProxy進行相應的分析,判斷出是讀操作還是寫操作,分發至對應的MySQLServer上。對於多節點Slave叢集,也可以起做到負載平衡的效果。以下是MySQLProxy的基本架構圖:
Sharding詳解" title="MySQL Sharding詳解" height="480" width="420">
通過上面的架構簡圖,我們可以很清晰的看出MySQLProxy在實際應用中所處的位置,以及能做的基本事情。關於MySQLProxy更為詳細的實施細則在MySQL官方文檔中有非常詳細的介紹和樣本,感興趣的讀者朋友可以直接從MySQL官方網站免費下載或者線上閱讀,我這裡就不累述浪費紙張了。
http://forge.mysql.com/wiki/MySQL_Proxy
2、 Hibernate Shards
這是 Google 技術團隊貢獻的項目(http://www.hibernate.org/414.html),該項目是在對Google 財務系統資料 Sharding 過程中誕生的。因為是在架構層實現的,所以有其獨特的特性:標準的 Hibernate 編程模型,會用 Hibernate 就能搞定,技術成本較低;相對彈性的 Sharding 策略以及支援虛擬 Shard 等。
3、 Spock Proxy
這也是在實際需求中產生的一個開源項目,基於Mysql Proxy擴充。Spock(http://www.spock.com/)是一個人員尋找的 Web 2.0 網站。通過對自己的單一 DB 進行有效 Sharding化 而產生了Spock Proxy(http://spockproxy.sourceforge.net/ ) 項目,Spock Proxy 算得上 MySQL Proxy 的一個分支,提供基於範圍的 Sharding 機制。Spock 是基於 Rails 的,所以Spock Proxy 也是基於 Rails 構建,關注 ROR 的朋友不應錯過這個項目。
http://spockproxy.sourceforge.net/
4、 Amoeba for MySQL
Amoeba是一個基於Java開發的,專註於解決分散式資料庫資料來源整合Proxy程式的開源架構,基於GPL3開源協議。目前,Amoeba已經具有Query路由,Query過濾,讀寫分離,負載平衡以及HA機制等相關內容。
Amoeba 主要解決的以下幾個問題:
(1)資料切分後複雜資料來源整合;
(2)提供資料切分規則並降低資料切分規則給資料庫帶來的影響;
(3)降低資料庫與用戶端的串連數;
(4)讀寫分離路由。
我們可以看出,Amoeba所做的事情,正好就是我們通過資料切分來提升資料庫的擴充性所需要的。
Amoeba並不是一個代理層的Proxy程式,而是一個開發資料庫代理層Proxy程式的開發架構,目前基於Amoeba所開發的Proxy程式有AmoebaForMySQL和AmoebaForAladin兩個。
AmoebaForMySQL主要是專門針對MySQL資料庫的解決方案,前端應用程式請求的協議以及後端串連的資料來源資料庫都必須是MySQL。對於用戶端的任何應用程式來說,AmoebaForMySQL和一個MySQL資料庫沒有什麼區別,任何使用MySQL協議的用戶端請求,都可以被AmoebaForMySQL解析並進行相應的處理。下如可以告訴我們AmoebaForMySQL的架構資訊(出自Amoeba開發人員部落格):
Sharding詳解" title="MySQL Sharding詳解" height="480" width="384">
AmoebaForAladin則是一個適用更為廣泛,功能更為強大的Proxy程式。他可以同時串連不同資料庫的資料來源為前端應用程式提供服務,但是僅僅接受符合MySQL協議的用戶端應用程式請求。也就是說,只要前端應用程式通過MySQL協議串連上來之後,AmoebaForAladin會自動分析Query語句,根據Query語句中所請求的資料來自動識別出該所Query的資料來源是在什麼類型資料庫的哪一個物理主機上面。展示了AmoebaForAladin的架構細節(出自Amoeba開發人員部落格):
Sharding詳解" title="MySQL Sharding詳解" height="480" width="384">
咋一看,兩者好像完全一樣嘛。細看之後,才會發現兩者主要的區別僅在於通過MySQLProtocalAdapter處理之後,根據分析結果判斷出資料來源資料庫,然後選擇特定的JDBC驅動和相應協議串連後端資料庫。
其實通過上面兩個架構圖大家可能也已經發現了Amoeba的特點了,他僅僅只是一個開發架構,我們除了選擇他已經提供的ForMySQL和ForAladin這兩款產品之外,還可以基於自身的需求進行相應的二次開發,得到更適應我們自己應用特點的Proxy程式。
當對於使用MySQL資料庫來說,不論是AmoebaForMySQL還是AmoebaForAladin都可以很好的使用。當然,考慮到任何一個系統越是複雜,其效能肯定就會有一定的損失,維護成本自然也會相對更高一些。所以,對於僅僅需要使用MySQL資料庫的時候,我還是建議使用AmoebaForMySQL。
AmoebaForMySQL的使用非常簡單,所有的設定檔都是標準的XML檔案,總共有四個設定檔。分別為:
(1)amoeba.xml:主設定檔,配置所有資料來源以及Amoeba自身的參數設定;
(2)rule.xml:配置所有Query路由規則的資訊;
(3)functionMap.xml:配置用於解析Query中的函數所對應的Java實作類別;
(4)rullFunctionMap.xml:配置路由規則中需要使用到的特定函數的實作類別;
如果您的規則不是太複雜,基本上僅需要使用到上面四個設定檔中的前面兩個就可完成所有工作。Proxy程式常用的功能如讀寫分離,負載平衡等配置都在amoeba.xml中進行。此外,Amoeba已經支援了實現資料的垂直切分和水平切分的自動路由,路由規則可以在rule.xml進行設定。
目前Amoeba少有欠缺的主要就是其線上管理功能以及對事務的支援了,曾經在與相關開發人員的溝通過程中提出過相關的建議,希望能夠提供一個可以進行線上維護管理的命令列管理工具,方便線上維護使用,得到的反饋是管理專門的管理模組已經納入開發議程了。另外在事務支援方面暫時還是Amoeba無法做到的,即使用戶端應用在提交給Amoeba的請求是包含事務資訊的,Amoeba也會忽略事務相關資訊。當然,在經過不斷完善之後,我相信事務支援肯定是Amoeba重點考慮增加的feature。
關於Amoeba更為詳細的使用方法讀者朋友可以通過Amoeba開發人員部落格(http://amoeba.sf.net)上面提供的使用手冊擷取,這裡就不再細述了。
案例(http://pengranxiang.iteye.com/blog/1145342)
操作文檔(http://docs.hexnova.com/amoeba/chap-getting-started.html)
5、 HiveDB
和前面的MySQLProxy以及Amoeba一樣,HiveDB同樣是一個基於Java針對MySQL資料庫的提供資料切分及整合的開源架構,只是目前的HiveDB僅僅支援資料的水平切分。主要解決大資料量下資料庫的擴充性及資料的高效能訪問問題,同時支援資料的冗餘及基本的HA機制。
HiveDB的實現機制與MySQLProxy和Amoeba有一定的差異,他並不是藉助MySQL的Replication功能來實現資料的冗餘,而是自行實現了資料冗餘機制,而其底層主要是基於HibernateShards來實現的資料切分工作。
在HiveDB中,通過使用者自訂的各種Partitionkeys(其實就是制定資料切分規則),將資料分散到多個MySQLServer中。在訪問的時候,在運行Query請求的時候,會自動分析過濾條件,並行從多個MySQLServer中讀取資料,併合並結果集返回給用戶端應用程式。
單純從功能方面來講,HiveDB可能並不如MySQLProxy和Amoeba那樣強大,但是其資料切分的思路與前面二者並無本質差異。此外,HiveDB並不僅僅只是一個開源愛好者所共用的內容,而是存在商業公司支援的開源項目。
下面是HiveDB官方網站上面一章圖片,描述了HiveDB如何來組織資料的基本資料,雖然不能詳細的表現出太多架構方面的資訊,但是也基本可以展示出其在資料切分方面獨特的一面了。
Sharding詳解" title="MySQL Sharding詳解" height="471" width="553">
http://www.hivedb.org/
6、 DataFabric
application-level sharding
master/slave replication
https://github.com/bpot/data_fabric
7、PL/Proxy
前面幾個都是針對MySQL 的 Sharding 方案,PL/Proxy 則是針對 PostgreSQL 的,設計思想類似 Teradata 的 Hash 機制,資料存放區對用戶端是透明的,客戶請求發送到 PL/Proxy 後,由這裡分布式預存程序調用,統一分發。 PL/Proxy 的設計初衷就是在這一層充當”資料匯流排”的職責,所以,當資料輸送量支撐不住的時候,只需要增加更多的 PL/Proxy 伺服器即可。大名鼎鼎的 Skype 用的就是 PL/Proxy 的解決方案。
8、Pyshards
這是個基於Python的解決方案。該工具的設計目標還有個 Re-balancing 在裡面,這倒是個比較激進的想法。目前只支援 MySQL 資料庫。
http://code.google.com/p/pyshards/wiki/Pyshards
9、其他實現資料切分及整合的解決方案
除了上面介紹的幾個資料切分及整合的整體解決方案之外,還存在很多其他同樣提供了資料切分與整合的解決方案。如基於MySQLProxy的基礎上做了進一步擴充的HSCALE,通過Rails構建的SpockProxy,以及基於Pathon的Pyshards等等。
不管大家選擇使用哪一種解決方案,總體設計思路基本上都不應該會有任何變化,那就是通過資料的垂直和水平切分,增強資料庫的整體服務能力,讓應用系統的整體擴充能力儘可能的提升,擴充方式儘可能的便捷。
只要我們通過中介層Proxy應用程式較好的解決了資料切分和資料來源整合問題,那麼資料庫的線性擴充能力將很容易做到像我們的應用程式一樣方便,只需要通過添加廉價的PCServer伺服器,即可線性增加資料庫叢集的整體服務能力,讓資料庫不再輕易成為應用系統的效能瓶頸。
五 注意事項
下面我們所說的分區,主要是指水平資料分割。
1、在實施分區前,我們可以查看所安裝版本的mysql是否支援分區:
mysql> show variables like "%partition%";
如果支援則會顯示:
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| have_partitioning | YES |
+-------------------+-------+
2、分區適用於一個表的所有資料和索引,不能只對資料分區而不對索引分割區,反之亦然,同時也不能只對錶的一部分進行分區。
3、分區類型
(1)RANGE 分區:基於屬於一個給定連續區間的列值,把多行分配給分區。
(2)LIST 分區:類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
(3)HASH分區:基於使用者定義的運算式的傳回值來進行選擇的分區,該運算式使用將要插入到表中的這些行的列值進行計算(新浪微博採用的方案)。
(4)KEY 分區:類似於按HASH分區,區別在於KEY分區只支援計算一列或多列,且MySQL 伺服器提供其自身的雜湊函數。必須有一列或多列包含整數值。
無論使用何種類型的分區,分區總是在建立時就自動的順序編號,且從0開始記錄。當有一新行插入到一個分區表中時,就是使用這些分區編號來識別正確的分區。
4、MySQL提供了許多修改分區表的方式。添加、刪除、重新定義、合并或拆分已經存在的分區是可能的。所有這些操作都可以通過使用ALTER TABLE 命令的分區擴充來實現。
5、可以對已經存在的表進行分區,直接使用alter table命令即可。
六 資料切分與整合可能存在的問題
這裡,大家應該對資料切分與整合的實施有了一定的認識了,或許很多讀者朋友都已經根據各種解決方案各自特性的優劣基本選定了適合於自己應用情境的方案,後面的工作主要就是實施準備了。
在實施資料切分方案之前,有些可能存在的問題我們還是需要做一些分析的。一般來說,我們可能遇到的問題主要會有以下幾點:
1、引入分散式交易的問題
一旦資料進行切分被分別存放在多個MySQLServer中之後,不管我們的切分規則設計的多麼的完美(實際上並不存在完美的切分規則),都可能造成之前的某些事務所涉及到的資料已經不在同一個MySQLServer中了。
在這樣的情境下,如果我們的應用程式仍然按照老的解決方案,那麼勢必需要引入分散式交易來解決。而在MySQL各個版本中,只有從MySQL5.0開始以後的各個版本才開始對分散式交易提供支援,而且目前僅有Innodb提供分散式交易支援。不僅如此,即使我們剛好使用了支援分散式交易的MySQL版本,同時也是使用的Innodb儲存引擎,分散式交易本身對於系統資源的消耗就是很大的,效能本身也並不是太高。而且引入分散式交易本身在異常處理方面就會帶來較多比較難控制的因素。
怎麼辦?其實我們可以可以通過一個變通的方法來解決這種問題,首先需要考慮的一件事情就是:是否資料庫是唯一一個能夠解決事務的地方呢?其實並不是這樣 的,我們完全可以結合資料庫以及應用程式兩者來共同解決。各個資料庫解決自己身上的事務,然後通過應用程式來控制多個資料庫上面的事務。
也就是說,只要我們願意,完全可以將一個跨多個資料庫的分散式交易分拆成多個僅處於單個資料庫上面的小事務,並通過應用程式來總控各個小事務。當然,這樣作的要求就是我們的俄應用程式必須要有足夠的健壯性,當然也會給應用程式帶來一些技術難度。
2、跨節點Join的問題
上面介紹了可能引入分散式交易的問題,現在我們再看看需要跨節點Join的問題。資料切分之後,可能會造成有些老的Join語句無法繼續使用,因為Join使用的資料來源可能被切分到多個MySQLServer中了。
怎麼辦?這個問題從MySQL資料庫角度來看,如果非得在資料庫端來直接解決的話,恐怕只能通過MySQL一種特殊的儲存引擎Federated來解決了。Federated儲存引擎是MySQL解決類似於Oracle的DBLink之類問題的解決方案。和OracleDBLink的主要區別在於Federated會儲存一份遠端表結構的定義資訊在本地。咋一看,Federated確實是解決跨節點Join非常好的解決方案。但是我們還應該清楚一點,那就似乎如果遠端的表結構發生了變更,本地的表定義資訊是不會跟著發生相應變化的。如果在更新遠端表結構的時候並沒有更新本地的Federated表定義資訊,就很可能造成Query運行出錯,無法得到正確的結果。
對待這類問題,我還是推薦通過應用程式來進行處理,先在驅動表所在的MySQLServer中取出相應的驅動結果集,然後根據驅動結果集再到被驅動表所在的MySQLServer中取出相應的資料。可能很多讀者朋友會認為這樣做對效能會產生一定的影響,是的,確實是會對效能有一定的負面影響,但是除了此法,基本上沒有太多其他更好的解決辦法了。而且,由於資料庫通過較好的擴充之後,每台MySQLServer的負載就可以得到較好的控制,單純針對單條Query來說,其回應時間可能比不切分之前要提高一些,所以效能方面所帶來的負面影響也並不是太大。更何況,類似於這種需要跨節點Join的需求也並不是太多,相對於總體效能而言,可能也只是很小一部分而已。所以為了整體效能的考慮,偶爾犧牲那麼一點點,其實是值得的,畢竟系統最佳化本身就是存在很多取捨和平衡的過程。
3、跨節點合并排序分頁問題
一旦進行了資料的水平切分之後,可能就並不僅僅只有跨節點Join無法正常運行,有些排序分頁的Query語句的資料來源可能也會被切分到多個節點,這樣造成的直接後果就是這些排序分頁Query無法繼續正常運行。其實這和跨節點Join是一個道理,資料來源存在於多個節點上,要通過一個Query來解決,就和跨節點Join是一樣的操作。同樣Federated也可以部分解決,當然存在的風險也一樣。
還是同樣的問題,怎麼辦?我同樣仍然繼續建議通過應用程式來解決。
如何解決?解決的思路大體上和跨節點Join的解決類似,但是有一點和跨節點Join不太一樣,Join很多時候都有一個驅動與被驅動的關係,所以Join本 身涉及到的多個表之間的資料讀取一般都會存在一個循序關聯性。但是排序分頁就不太一樣了,排序分頁的資料來源基本上可以說是一個表(或者一個結果集),本身並 不存在一個循序關聯性,所以在從多個資料來源取資料的過程是完全可以並行的。這樣,排序分頁資料的取數效率我們可以做的比跨庫Join更高,所以帶來的效能損失相對的要更小,在有些情況下可能比在原來未進行資料切分的資料庫中效率更高了。當然,不論是跨節點Join還是跨節點排序分頁,都會使我們的應用伺服器消耗更多的資源,尤其是記憶體資源,因為我們在讀取存取以及合并結果集的這個過程需要比原來處理更多的資料。
分析到這裡,可能很多讀者朋友會發現,上面所有的這些問題,我給出的建議基本上都是通過應用程式來解決。大家可能心裡開始犯嘀咕了,是不是因為我是DBA,所以就很多事情都扔給應用架構師和開發人員了?
其實完全不是這樣,首先應用程式由於其特殊性,可以非常容易做到很好的擴充性,但是資料庫就不一樣,必須藉助很多其他的方式才能做到擴充,而且在這個擴充 過程中,很難避免帶來有些原來在集中式資料庫中可以解決但被切分開成一個資料庫叢集之後就成為一個難題的情況。要想讓系統整體得到最大限度的擴充,我們只 能讓應用程式做更多的事情,來解決資料庫叢集無法較好解決的問題。
七 MySQL基於Amoeba的水平和垂直分區樣本
詳細的配置描述請參考:http://pengranxiang.iteye.com/blog/1145342
MySQL Sharding詳解