天天看點

資料庫 資料切分

1  何謂資料切分

無論資料的 sharding 還是資料的切分,其實質都是一樣的。簡單來說,就是指通過某種特定的條件,将存放在同一個資料庫中的資料分散存放到多個資料庫(主機)上面,以達到分散單台裝置負載的效果。資料的切分同時還可以提高系統的總體可用性,因為單台裝置 crash 之後,隻有總體資料的某部分不可用,而不是所有的資料。

資料的切分(sharding)根據其切分規則的類型,可以分為兩種切分模式。一種是按照不同的表(或者 schema)來切分到不同的資料庫(主機)之上,這種切分可以稱之為資料的垂直(縱向)切分;另外一種則是根據表中資料的邏輯關系,将同一個表中的資料按照某種條件拆分到多台資料庫(主機)上,這種切分稱之為資料的水準(橫向)切分。

垂直切分的最大特點就是規則簡單,實施也更為友善,尤其适合各業務之間的耦合度非常低、互相影響很小、業務邏輯非常清晰的系統。在這種系統中,可以很容易做到将不同業務子產品所使用的表分拆到不同的資料庫中。根據不同的表來進行拆分,對應用程式的影響也更小,拆分規則也會比較簡單清晰。

水準切分與垂直切分相比,稍微複雜一些。因為要将同一個表中的不同資料拆分到不同的資料庫中,對于應用程式來說,拆分規則本身就較根據表名來拆分更為複雜,後期的資料維護也會更複雜。

當某個(或者某些)表的資料量和通路量特别大,通過垂直切分将其放在獨立的裝置上後仍然無法滿足性能要求時,就必須将垂直切分和水準切分相結合,先垂直切分,然後再水準切分,這樣才能解決這種超大型表的性能問題。

下面就針對垂直、水準及組合切分這三種資料切分方式的架構實作及切分後資料的整合進行相應的分析。

2  資料的垂直切分

我們先來看一下,資料的垂直切分到底是如何切分的。資料的垂直切分,也可以稱為縱向切分。将資料庫想象成由很多個一大塊一大塊的"資料塊"(表)組成,垂直地将這些"資料塊"切開,然後把它們分散到多台資料庫主機上面。這樣的切分方法就是垂直(縱向)的資料切分。

一個架構設計較好的應用系統,其總體功能肯定是由很多個功能子產品所組成的,而每一個功能子產品所需要的資料對應到資料庫中就是一個或多個表。而在架構設計中,各個功能子產品互相之間的互動點越統一、越少,系統的耦合度就越低,系統各個子產品的維護性及擴充性也就越好。這樣的系統,實作資料的垂直切分也就越容易。

功能子產品越清晰,耦合度越低,資料垂直切分的規則定義也就越容易。完全可以根據功能子產品來進行資料的切分,不同功能子產品的資料存放于不同的資料庫主機中,可以很容易就避免跨資料庫的 join 存在,同時系統架構也非常清晰。

當然,很難有系統能夠做到所有功能子產品使用的表完全獨立,根本不須要通路對方的表,或者須要将兩個子產品的表進行 join 操作。這種情況下,就必須根據實際的應用場景進行評估權衡。決定是遷就應用程式将需要 join 的表的相關子產品都存放在同一個資料庫中,還是讓應用程式做更多的事情--完全通過子產品接口取得不同資料庫中的資料,然後在程式中完成 join 操作。

一般來說,如果是一個負載相對不大,而且表關聯又非常頻繁的系統,那可能資料庫讓步,将幾個相關子產品合并在一起,減少應用程式工作的方案可以減少較多的工作量,是一個可行的方案。

當然,通過資料庫的讓步,讓多個子產品集中共用資料源,實際上也是間接默許了各子產品架構耦合度增大的發展,可能會惡化以後的架構。尤其是當發展到一定階段,發現資料庫實在無法承擔這些表所帶來的壓力,不得不面臨再次切分時,所帶來的架構改造成本可能遠遠大于最初就使用切分的架構設計。

是以,在資料庫進行垂直切分的時候,如何切分、切分到什麼樣的程度,是一個比較考驗人的難題。這隻能在實際的應用場景中通過平衡各方面的成本和收益,才能分析出一個真正适合自己的拆分方案。

比如在本書所使用的示例系統的 example 資料庫中,我們簡單分析一下,然後設計一個簡單的切分規則,進行一次垂直拆分。

系統功能基本可以分為4個功能子產品:使用者、群組消息、相冊以及事件,分别對應為如下這些表:

(1)使用者子產品表:user,user_profile,user_group,user_photo_album

(2)群組讨論表:groups,group_message,group_message_content,top_message

(3)相冊相關表:photo,photo_album,photo_album_relation,photo_comment

(4)事件資訊表:event

初略一看,沒有哪個子產品可以脫離其他子產品獨立存在,子產品與子產品之間都存在着關系,莫非無法切分?

當然不是,再稍微深入分析一下,可以發現,雖然各個子產品所使用的表之間都有關聯,但是關聯關系還算清晰,也比較簡單。

群組讨論子產品和使用者子產品之間主要存在通過使用者或群組關系來進行關聯。一般都會通過使用者的 id 或 nick_name 及 group 的 id 來進行關聯,通過子產品之間的接口實作不會帶來太多麻煩。

相冊子產品僅僅與使用者子產品存在使用者的關聯。這兩個子產品之間的關聯基本隻有通過使用者 id 關聯的内容,簡單清晰,接口明确。

事件子產品與各個子產品可能都有關聯,但是都隻關注其各個子產品中對象的id資訊,同樣比較容易分拆。

是以,第一步可以将資料庫按照功能子產品相關的表進行一次垂直拆分,每個子產品所涉及的表單獨分到一個資料庫中,子產品與子產品之間的表關聯在應用系統端都通過接口來處理。如資料垂直切分示意圖(圖14-1)所示:

資料庫 資料切分

(點選檢視大圖)圖14-1  垂直切分示意

通過這樣的垂直切分之後,之前隻能通過一個資料庫來提供的服務,就被分拆成4個資料庫來提供服務,服務能力自然是增加幾倍了。

垂直切分的優點:

資料庫的拆分簡單明了,拆分規則明确;

應用程式子產品清晰明确,整合容易;

資料維護友善易行,容易定位。

垂直切分的缺點:

部分表關聯無法在資料庫級别完成,要在程式中完成;

對于通路極其頻繁且資料量超大的表仍然存在性能瓶頸,不一定能滿足要求;

事務處理相對複雜;

切分達到一定程度之後,擴充性會受到限制;

過度切分可能會帶來系統過于複雜而難以維護。

針對于垂直切分可能遇到資料切分及事務問題,在資料庫層面實在是很難找到一個較好的處理方案。實際應用案例中,資料庫的垂直切分大多是與應用系統的子產品相對應的,同一個子產品的資料源存放于同一個資料庫中,可以解決子產品内部的資料關聯問題。而子產品與子產品之間,則通過應用程式以服務接口的方式來互相提供所需要的資料。雖然這樣做在資料庫的總體操作次數方面确實會有所增加,但是在系統整體擴充性及架構子產品化方面,都是有益的。可能某些操作的單次響應的時間會稍有增加,但是系統的整體性能很可能反而會有一定的提升。而擴充瓶頸問題,就隻能依靠下一節将要介紹的資料水準切分架構來解決了。

3  資料的水準切分

資料的垂直切分基本上可以簡單地了解為按照表或子產品來切分資料,而水準切分則不同。一般來說,簡單的水準切分主要是将某個通路極其平凡的表再按照某個字段的某種規則分散到多個表中,每個表包含一部分資料。

簡單來說,可以将資料的水準切分了解為按照資料行的切分,就是将表中的某些行切分到一個資料庫,而另外的某些行又切分到其他的資料庫中。當然,為了能夠比較容易地判定各行資料被切分到哪個資料庫中了,切分總是須要按照某種特定的規則來進行的:如根據某個數字類型字段基于特定數目取模,某個時間類型字段的範圍,或者某個字元類型字段的 hash 值。如果整個系統中大部分核心表都可以通過某個字段來進行關聯,那這個字段自然是一個進行水準分區的上上之選了,當然,非常特殊無法使用的情況除外。

一般來說,像現在非常火爆的 web 2.0 類型網站,基本上大部分資料都能夠通過會員使用者資訊關聯上,可能很多核心表都非常适合通過會員 id 來進行資料的水準切分。而像論壇社群讨論系統,就更容易切分了,可以按照論壇編号來進行水準切分。切分之後基本上不會出現各個庫之間的互動。

如果示例系統的所有資料都是和使用者關聯的,那麼就可以根據使用者來進行水準拆分,将不同使用者的資料切分到不同的資料庫中。當然,唯一差別是使用者子產品中的 groups 表和使用者沒有直接關系,是以 groups 不能根據使用者來進行水準拆分。對于這種特殊情況下的表,完全可以獨立出來,放在一個獨立的資料庫中。其實這個做法可以說是利用了前面一節所介紹的"資料的垂直切分"方法,将在下一節中更為詳細地介紹這種垂直切分與水準切分同時使用的聯合切分方法。

是以,對于示例資料庫來說,大部分的表都可以根據使用者 id 來進行水準切分。不同使用者相關的資料進行切分之後存放在不同的資料庫中。如将所有使用者 id 通過被2 取模然後分别存放于兩個不同的資料庫中。每個和使用者 id 關聯上的表都可以這樣切分。這樣,基本上每個使用者相關的資料,都在同一個資料庫中,即使須要關聯,也非常容易實作。

可以通過水準切分示意圖(圖14-2)更為直覺地展示水準切分相關資訊:

資料庫 資料切分

(點選檢視大圖)圖14-2

水準切分的優點:

表關聯基本能夠在資料庫端全部完成;

不會存在某些超大型資料量和高負載的表遇到瓶頸的問題;

應用程式端整體架構改動相對較少;

事務處理相對簡單;

隻要切分規則能夠定義好,基本上較難遇到擴充性限制。

水準切分的缺點:

切分規則相對複雜,很難抽象出一個能夠滿足整個資料庫的切分規則;

後期資料的維護難度有所增加,人為手工定位資料更困難;

應用系統各子產品耦合度較高,可能會對後面資料的遷移拆分造成一定的困難。