日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

[转]数据库分库分表

發布時間:2024/1/17 数据库 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转]数据库分库分表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉載至:http://blog.csdn.net/xxdddail/article/details/55195091

一、 基本思想

Sharding的基本思想就要把一個數據庫切分成多個部分放到不同的數據庫(server)上,從而緩解單一數據庫的性能問題。對于海量數據的數據庫,如果是因為表多而數據多,這時候適合使用垂直切分,即把關系緊密(比如同一模塊)的表切分出來放在一個服務器上。如果表并不多,但每張表的數據非常多,這時候適合水平切分,即把表的數據按某種規則(比如按ID散列)切分到多個數據庫(server)上。根據實際情況做出選擇,也可能會綜合使用垂直與水平切分

1、 垂直切分

數據的垂直切分,也可以稱之為縱向切分。將數據庫想象成為由很多個一大塊一大塊的數據塊(表)組成,我們垂直的將這些數據塊切開,然后將他們分散到多臺數據庫主機上面,這樣的切分方法就是一個垂直(縱向)的數據切分。

系統功能可以基本分為以下四個功能模塊:用戶、群組消息、相冊以及事件,分別對應為如下這些表:

1. 用戶模塊表 useruser_profileuser_groupuser_photo_album

2. 群組討論表 groupsgroup_messagegroup_message_contenttop_message

3. 相冊相關表 photophoto_albumphoto_album_relationphoto_comment

4. 事件信息表 event

模塊之間的關系:

1. 群組討論模塊和用戶模塊之間主要存在通過用戶或者是群組關系來進行關聯。一般關聯的時候都會是通過用戶id或者nick_name以及groupid來進行關聯,,通過模塊之間的接口實現不會帶來太多麻煩。

2. 相冊模塊僅僅與用戶模塊存在通過用戶的關聯。這兩個模塊之間的關聯基本就有通過用戶id關聯的內容,簡單清晰,接口明確。

3. 事件模塊與各個模塊可能都有關聯,但是都只關注其各個模塊中對象的信息ID,同樣可以做到很容易分拆。

所以,我們第一步可以將數據庫按照功能模塊相關的表進行一次垂直拆分,每個模塊 涉及的表單獨到一個數據庫中,模塊與模塊之間的表關聯都在應用系統端通過接口來處理。 如下圖所示:

?

通過這樣的垂直切分之后,之前只能通過一個數據庫來提供的服務,就被分拆成四個數據庫來提供服務,服務能力自然是增加幾倍了。

垂直切分的優點

u 數據庫的拆分簡單明了,拆分規則明確

u 應用程序模塊清晰明確,整合容易

u 數據維護方便易行,容易定位

垂直切分的缺點

u 部分表關聯無法在數據庫級別完成,需要在程序中完成

u 對于訪問極其頻繁且數據量超大的表仍然存在性能平靜,不一定能滿足要求

u 事務處理相對更為復雜

u 切分達到一定程度之后,擴展性會遇到限制

u 過度切分可能會帶來系統過渡復雜而難以維護

2、 水平切分

數據的水平切分,一般來說,簡單的水平切分主要是將某個訪問極其平凡的表再按照某個字段的某種規則來分散到多個表之中,每個表中包含一部分數據。簡單來說,我們可以將數據的水平切分理解為是按照數據行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其他的數據庫中。

對于我們的示例數據庫來說,大部分的表都可以根據用戶ID來進行水平的切分,不同用戶相關的數據進行切分之后存放在不同的數據庫中。如將所有用戶ID通過2取模,然后分別存放于兩個不同的數據庫中,每個和用戶ID關聯上的表都可以這樣切分。這樣,基本上每個用戶相關的數據,都在同一個數據庫中,即使是需要關聯,也可以非常簡單的關聯上。

我們可以通過下圖來更為直觀的展示水平切分相關信息:


水平切分的優點

u 表關聯基本能夠在數據庫端全部完成

u 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題

u 應用程序端整體架構改動相對較少

u 事務處理相對簡單

u 只要切分規則能夠定義好,基本上較難遇到擴展性限制

水平切分的缺點

u 切分規則相對更為復雜,很難抽象出一個能夠滿足整個數據庫的切分規則

u 后期數據的維護難度有所增加,人為手工定位數據更困難

u 應用系統各模塊耦合度較高,可能會對后面數據的遷移拆分造成一定的困難

3、 垂直與水平聯合切分

一般來說,我們數據庫中的所有表很難通過某一個(或少數幾個)字段全部關聯起來,所以很難簡單的僅僅通過數據的水平切分來解決所有問題。而垂直切分也只能解決部分問題,對于那些負載非常高的系統,即使僅僅只是單個表都無法通過單臺數據庫主機來承擔其負載,我們必須結合水平垂直兩種切分方式同時使用,充分利用兩者的優點,避開其缺點

每一個應用系統的負載都是一步一步增長上來的,在開始遇到性能瓶頸的時候,大多數架構師和DBA都會選擇先進行數據的垂直拆分,然而,隨著業務的不斷擴張,系統負載的持續增長,在系統穩定一段時期之后,經過了垂直拆分之后的數據庫集群可能又再一次不堪重負,遇到了性能瓶頸。

這時候我們就必須要通過數據的水平切分的優勢,來解決這里所遇到的問題。對于我們的示例數據庫,假設在最開始,我們進行了數據的垂直切分,然而隨著業務的不斷增長,數據庫系統遇到了瓶頸,我們選擇重構數據庫集群的架構。如何重構?考慮到之前已經做好了數據的垂直切分,而且模塊結構清晰明確。而業務增長的勢頭越來越猛,即使現在進一步再次拆分模塊,也堅持不了太久。我們選擇了在垂直切分的基礎上再進行水平拆分。

在經歷過垂直拆分后的各個數據庫集群中的每一個都只有一個功能模塊,而每個功能模切中的所有表基本上都會與某個字段進行關聯。如用戶模塊全部都可以通過用戶ID進行切分,群組討論模塊則都通過群組ID 來切分,相冊模塊則根據相冊 ID 分,最后的事件通知信息表考慮到數據的時限性(僅僅只會訪問最近某個事件段的信息),則考慮按時間來切分。

下圖展示了切分后的整個架構:

?

在應對不同的應用場景的時候,也需要充分考慮到這兩種切分方法各自的局限,以及各自的優勢,在不同的時期(負載壓力)使用不同的結合方式。

聯合切分的優點

u 可以充分利用垂直切分和水平切分各自的優勢而避免各自的缺陷

u 讓系統擴展性得到最大化提升

聯合切分的缺點

u 數據庫系統架構比較復雜,維護難度更大

u 應用程序架構也相對更復雜

?

二、 拆分實施策略和示例演示

第一部分:實施策略

?

1. 準備階段

對數據庫進行分庫分表(Sharding)前,需要充分了解系統業務邏輯和數據庫schema.繪制一張數據庫ER圖,以圖為基礎劃分shard,直觀易行,可以確保清醒思路。

2. 分析階段

1. 垂直切分

垂直切分的依據原則是:將業務緊密,表間關聯密切的表劃分在一起,例如同一模塊的表。結合已經準備好的數據庫ER圖或領域模型圖,仿照活動圖中的泳道概念,一個泳道代表一個shard,把所有表格劃分到不同的泳道中。下面的分析示例會展示這種做法。這種方式多個數據庫之間的表結構不同。

2. 水平切分

垂直切分后,需要對shard內表格的數據量和增速進一步分析,以確定是否需要進行水平切分。這些數據庫中的表結構完全相同。

??? 2.1 若劃分到一起的表格數據增長緩慢,在產品上線后可遇見的足夠長的時期內均可以由單一數據庫承載,則不需要進行水平切分,所有表格駐留同一shard,所有表間關聯關系會得到最大限度的保留,同時保證了書寫SQL的自由度,不易受joingroup byorder by等子句限制。

2.2 若劃分到一起的表格數據量巨大,增速迅猛,需要進一步進行水平分割。進一步的水平分割就這樣進行:

2.2.1結合業務邏輯和表間關系,將當前shard劃分成多個更小的shard,通常情況下,這些更小的shard每一個都只包含一個主表(將以該表ID進行散列的表)和多個與其關聯或間接關聯的次表。這種一個shard一張主表多張次表的狀況是水平切分的必然結果。這樣切分下來,shard數量就會迅速增多。如果每一個shard代表一個獨立的數據庫,那么管理和維護數據庫將會非常麻煩,而且這些小shard往往只有兩三張表,為此而建立一個新庫,利用率并不高,因此,在水平切分完成后可再進行一次反向的Merge”,即:將業務上相近,并且具有相近數據增長速率(主表數據量在同一數量級上)的兩個或多個shard放到同一個數據庫上,在邏輯上它們依然是獨立的shard,有各自的主表,并依據各自主表的ID進行散列,不同的只是它們的散列取模(即節點數量)必需是一致的。這樣,每個數據庫結點上的表格數量就相對平均了。

2.2.2 所有表格均劃分到合適的shard之后,所有跨越shard的表間關聯都必須打斷,在書寫sql時,跨shardjoingroup byorder by都將被禁止,需要在應用程序層面協調解決這些問題。

3. 實施階段

??? 如果項目在開發伊始就決定進行分庫分表,則嚴格按照分析設計方案推進即可。如果是在中期架構演進中實施,除搭建實現sharding邏輯的基礎設施外,還需要對原有SQL逐一過濾分析,修改那些因為sharding而受到影響的sql

?

第二部分:示例演示

??? 以下使用jpetstore(寵物店的電子商務系統)來演示如何進行分庫分表(sharding)在分析階段的工作。jpetstore來自原ibatis官方的一個Demo版本,SVN地址為:

http://mybatis.googlecode.com/svn/tags/java_release_2.3.4-726/jpetstore-5


??? 由于系統較簡單,我們很容易從模型上看出,其主要由三個模塊組成:用戶,產品和訂單。那么垂直切分的方案也就出來了。接下來看水平切分,如果我們從一個實際的寵物店出發考慮,可能出現數據激增的單表應該是AccountOrder,因此這兩張表需要進行水平切分。對于Product模塊來說,如果是一個實際的系統,ProductItem的數量都不會很大,因此只做垂直切分就足夠了,也就是(ProductCategoryItemIventorySupplier)五張表在一個數據庫結點上(沒有水平切分,不會存在兩個以上的數據庫結點)。但是作為一個演示,我們假設產品模塊也有大量的數據需要我們做水平切分,那么分析來看,這個模塊要拆分出兩個shard:一個是(Product(主),Category),另一個是(Item(主),IventorySupplier),同時,我們認為:這兩個shard在數據增速上應該是相近的,且在業務上也很緊密,那么我們可以把這兩個shard放在同一個數據庫節點上,ItemProduct數據在散列時取一樣的模。根據前文介紹的圖紙繪制方法,我們得到下面這張sharding示意圖:


對于這張圖再說明幾點:

1. 使用泳道表示物理shard(一個數據庫結點)

2. 若垂直切分出的shard進行了進一步的水平切分,但公用一個物理shard的話,則用虛線框住,表示其在邏輯上是一個獨立的shard

3. 深色實體表示主表

4. X表示需要打斷的表間關聯

三、 全局主鍵生成策略

??? 一旦數據庫被切分到多個物理結點上,我們將不能再依賴數據庫自身的主鍵生成機制。一方面,某個分區數據庫自生成的ID無法保證在全局上是唯一的;另一方面,應用程序在插入數據之前需要先獲得ID,以便進行SQL路由。

??? flickr開發團隊在2010年撰文介紹了flickr使用的一種主鍵生成測策略,同時表示該方案在flickr上的實際運行效果也非常令人滿意,它與一般Sequence表方案有些類似,但卻很好地解決了性能瓶頸和單點問題,是一種非常可靠而高效的全局主鍵生成方案。

?

flickr這一方案的整體思想是:建立兩臺以上的數據庫ID生成服務器,每個服務器都有一張記錄各表當前IDSequence表,但是SequenceID增長的步長是服務器的數量,起始值依次錯開,這樣相當于把ID的生成散列到了每個服務器節點上。例如:如果我們設置兩臺數據庫ID生成服務器,那么就讓一臺的Sequence表的ID起始值為1,每次增長步長為2,另一臺的Sequence表的ID起始值為2,每次增長步長也為2,那么結果就是奇數的ID都將從第一臺服務器上生成,偶數的ID都從第二臺服務器上生成,這樣就將生成ID的壓力均勻分散到兩臺服務器上,同時配合應用程序的控制,當一個服務器失效后,系統能自動切換到另一個服務器上獲取ID,從而保證了系統的容錯。

關于這個方案,有幾點細節這里再說明一下:

1. flickr的數據庫ID生成服務器是專用服務器,服務器上只有一個數據庫,數據庫中表都是用于生成Sequence的,這也是因為auto-increment-offsetauto-increment-increment這兩個數據庫變量是數據庫實例級別的變量。

2. flickr的方案中表格中的stub字段只是一個char(1) NOT NULL存根字段,并非表名,因此,一般來說,一個Sequence表只有一條紀錄,可以同時為多張表生成ID,如果需要表的ID是有連續的,需要為該表單獨建立Sequence表。

3. 方案使用了MySQLLAST_INSERT_ID()函數,這也決定了Sequence表只能有一條記錄。

4. 使用REPLACE INTO插入數據,這是很討巧的作法,主要是希望利用MySQL自身的機制生成ID,不僅是因為這樣簡單,更是因為我們需要ID按照我們設定的方式(初值和步長)來生成。

5. SELECT LAST_INSERT_ID()必須要于REPLACE INTO語句在同一個數據庫連接下才能得到剛剛插入的新ID,否則返回的值總是0

6. 該方案中Sequence表使用的是MyISAM引擎,以獲取更高的性能,注意:MyISAM引擎使用的是表級別的鎖,MyISAM對表的讀寫是串行的,因此不必擔心在并發時兩次讀取會得到同一個ID(另外,應該程序也不需要同步,每個請求的線程都會得到一個新的connection,不存在需要同步的共享資源)。經過實際對比測試,使用一樣的Sequence表進行ID生成,MyISAM引擎要比InnoDB表現高出很多!

7. 可使用純JDBC實現對Sequence表的操作,以便獲得更高的效率,實驗表明,即使只使用spring JDBC性能也不及純JDBC來得快!

?

實現該方案,應用程序同樣需要做一些處理,主要是兩方面的工作:

1. 自動均衡數據庫ID生成服務器的訪問

2. 確保在某個數據庫ID生成服務器失效的情況下,能將請求轉發到其他服務器上執行。

四、 sharding實現層面

??? 通過前面的章節,我們已經很清楚了通過數據庫的數據切分可以極大的提高系統的擴展性。但是,數據庫中的數據在經過垂直和(或)水平切分被存放在不同的數據庫主機之后,應用系統面臨的最大問題就是如何來讓這些數據源得到較好的整合。

在應用服務器與數據庫之間通過代理實現

在應用服務器與數據庫之間加入一個代理,應用程序向數據發出的數據請求會先通過代理,代理會根據配置的路由規則,對SQL進行解析后路由到目標shard,因為這種方案對應用程序完全透明,通用性好,所以成為了很多sharding產品的選擇。在這方面較為知名的產品是mysql官方的代理工具:Mysql Proxy和一款國人開發的產品:amoebamysql proxy本身并沒有實現任何sharding邏輯,它只是作為一種面向mysql數據庫的代理,給開發人員提供了一個嵌入sharding邏輯的場所,它使用lua作為編程語言,這對很多團隊來說是需要考慮的一個問題。amoeba則是專門實現讀寫分離與sharding的代理產品,它使用非常簡單,不使用任何編程語言,只需要通過xml進行配置。不過amoeba不支持事務(從應用程序發出的包含事務信息的請求到達amoeba時,事務信息會被抹去,因此,即使是單點數據訪問也不會有事務存在)一直是個硬傷。當然,這要看產品的定位和設計理念,我們只能說對于那些對事務要求非常高的系統,amoeba是不適合的。

Amoeba For MySQL主要是專門針對MySQL數據庫的解決方案,前端應用程序請求的協議以及后端連接的數據源數據庫都必須是MySQL。對于客戶端的任何應用程序來說, amoeba For MySQL和一個MySQL沒有什么區別,任何使用MySQL協議的客戶端請求, 都可以被 Amoeba For MySQL解析并進行相應的處理,下圖可以告訴我們Amoeba For MySQL的架構信息:

?

Amoeba使用指南:

http://docs.hexnova.com/amoeba/index.html

其他的一些實現層可以參考:

http://blog.csdn.NET/bluishglc/article/details/7766508

?

五、 多數據源的事務處理

?

?

?

六、 一種支持自由規劃無須數據遷移和修改路由代碼的Sharding擴容方案

Sharding擴容——系統維護不能承受之重

?

任何Sharding系統,在上線運行一段時間后,數據就會積累到當前節點規模所能承載的上限,此時就需要對數據庫進行擴容了,也就是增加新的物理結點來分攤數據。如果系統使用的是基于ID進行散列的路由方式,那么團隊需要根據新的節點規模重新計算所有數據應處的目標Shard,并將其遷移過去,這對團隊來說無疑是一個巨大的維護負擔;而如果系統是按增量區間進行路由(如每1千萬條數據或是每一個月的數據存放在一個節點上 ),雖然可以避免數據的遷移,卻有可能帶來熱點問題,也就是近期系統的讀寫都集中在最新創建的節點上(很多系統都有此類特點:新生數據的讀寫頻率明顯高于舊有數據),從而影響了系統性能。面對這種兩難的處境,Sharding擴容顯得異常困難。

?

一般來說,理想的擴容方案應該努力滿足以下幾個要求:

1.? 最好不遷移數據 (無論如何,數據遷移都是一個讓團隊壓力山大的問題)

2. 允許根據硬件資源自由規劃擴容規模和節點存儲負載

3. 能均勻的分布數據讀寫,避免熱點問題

4. 保證對已經達到存儲上限的節點不再寫入數據

目前,能夠避免數據遷移的優秀方案并不多,相對可行的有兩種,一種是維護一張記錄數據ID和目標Shard對應關系的映射表,寫入時,數據都寫入新擴容的Shard,同時將ID和目標節點寫入映射表,讀取時,先查映射表,找到目標Shard后再執行查詢。該方案簡單有效,但是讀寫數據都需要訪問兩次數據庫,且映射表本身也極易成為性能瓶頸。為此系統不得不引入分布式緩存來緩存映射表數據,但是這樣也無法避免在寫入時訪問兩次數據庫,同時大量映射數據對緩存資源的消耗以及專門為此而引入分布式緩存的代價都是需要權衡的問題。另一種方案來自淘寶綜合業務平臺團隊,它利用對2的倍數取余具有向前兼容的特性(如對4取余得1的數對2取余也是1)來分配數據,避免了行級別的數據遷移,但是依然需要進行表級別的遷移,同時對擴容規模和分表數量都有限制。總得來說,這些方案都不是十分的理想,多多少少都存在一些缺點,這也從一個側面反映出了Sharding擴容的難度。

?

取長補短,兼容并包——一種理想的Sharding擴容方案

?

如前文所述,Sharding擴容與系統采用的路由規則密切相關:基于散列的路由能均勻地分布數據,但卻需要數據遷移,同時也無法避免對達到上限的節點不再寫入新數據;基于增量區間的路由天然不存在數據遷移和向某一節點無上限寫入數據的問題,但卻存在熱點困擾。我們設計方案的初衷就是希望能結合兩種路由規則的優勢,摒棄各自的劣勢,創造出一種接近理想狀態的擴容方式,而這種方式簡單概括起來就是:全局按增量區間分布數據,使用增量擴容,無數據遷移,局部使用散列方式分散數據讀寫,解決熱點問題,同時對Sharding拓撲結構進行建模,使用一致的路由算法,擴容時只需追加節點數據,不再修改散列邏輯代碼。

?

原理

?

首先,作為方案的基石,為了能使系統感知到Shard并基于Shard的分布進行路由計算,我們需要建立一個可以描述Sharding拓撲結構的編程模型。按照一般的切分原則,一個單一的數據庫會首先進行垂直切分,垂直切分只是將關系密切的表劃分在一起,我們把這樣分出的一組表稱為一個Partition。 接下來,如果Partition里的表數據量很大且增速迅猛,就再進行水平切分,水平切分會將一張表的數據按增量區間或散列方式分散到多個Shard上存儲。在我們的方案里,我們使用增量區間與散列相結合的方式,全局上,數據按增量區間分布,但是每個增量區間并不是按照某個Shard的存儲規模劃分的,而是根據一組Shard的存儲總量來確定的,我們把這樣的一組Shard稱為一個ShardGroup,局部上,也就是一個ShardGroup內,記錄會再按散列方式均勻分布到組內各Shard上。這樣,一條數據的路由會先根據其ID所處的區間確定ShardGroup,然后再通過散列命中ShardGroup內的某個目標Shard。在每次擴容時,我們會引入一組新的Shard,組成一個新的ShardGroup,為其分配增量區間并標記為可寫入,同時將原有ShardGroup標記為不可寫入,于是新生數據就會寫入新的ShardGroup,舊有數據不需要遷移。同時,在ShardGroup內部各Shard之間使用散列方式分布數據讀寫,進而又避免了熱點問題。最后,在Shard內部,當單表數據達到一定上限時,表的讀寫性能就開始大幅下滑,但是整個數據庫并沒有達到存儲和負載的上限,為了充分發揮服務器的性能,我們通常會新建多張結構一樣的表,并在新表上繼續寫入數據,我們把這樣的表稱為分段表Fragment Table)。不過,引入分段表后所有的SQL在執行前都需要根據ID將其中的表名替換成真正的分段表名,這無疑增加了實現Sharding的難度,如果系統再使用了某種ORM框架,那么替換起來可能會更加困難。目前很多數據庫提供一種與分段表類似的分區機制,但沒有分段表的副作用,團隊可以根據系統的實現情況在分段表和分區機制中靈活選擇。總之,基于上述切分原理,我們將得到如下Sharding拓撲結構的領域模型:


在這個模型中,有幾個細節需要注意:ShardGroupwritable屬性用于標識該ShardGroup是否可以寫入數據,一個Partition在任何時候只能有一個ShardGroup是可寫的,這個ShardGroup往往是最近一次擴容引入的;startIdendId屬性用于標識該ShardGroupID增量區間;ShardhashValue屬性用于標識該Shard節點接受哪些散列值的數據;FragmentTablestartIdendId是用于標識該分段表儲存數據的ID區間。

確立上述模型后,我們需要通過配置文件或是在數據庫中建立與之對應的表來存儲節點元數據,這樣,整個存儲系統的拓撲結構就可以被持久化起來,系統啟動時就能從配置文件或數據庫中加載出當前的Sharding拓撲結構進行路由計算了(如果結點規模并不大可以使用配置文件,如果節點規模非常大,需要建立相關表結構存儲這些結點元數據。從最新的Oracle發布的《面向大規模可伸縮網站基礎設施的MySQL參考架構》白皮書一文的超大型系統架構參考章節給出的架構圖中我們可以看到一種名為:Shard Catalog的專用服務器,這個其實是保存結點配置信息的數據庫),擴容時只需要向對應的文件或表中加入相關的節點信息重啟系統即可,不需要修改任何路由邏輯代碼。

示例

讓我們通過示例來了解這套方案是如何工作的。

階段一:初始上線

假設某系統初始上線,規劃為某表提供4000W條記錄的存儲能力,若單表存儲上限為1000W條,單庫存儲上限為2000W條,共需2Shard,每個Shard包含兩個分段表,ShardGroup增量區間為0-4000W,按2取余分散到2Shard上,具體規劃方案如下:

上面說單表的存儲上線為1000W條,但是為什么圖中Table_0的范圍是0-2000W?)

與之相適應,Sharding拓撲結構的元數據如下:

?

階段二:系統擴容

?

經過一段時間的運行,當原表總數據逼近4000W條上限時,系統就需要擴容了。為了演示方案的靈活性,我們假設現在有三臺服務器Shard2Shard3Shard4,其性能和存儲能力表現依次為Shard2<Shard3<Shard4,我們安排Shard2儲存1000W條記錄,Shard3儲存2000W條記錄,Shard4儲存3000W條記錄,這樣,該表的總存儲能力將由擴容前的4000W條提升到10000W條,以下是詳細的規劃方案:

?

相應拓撲結構表數據下:

?

?

從這個擴容案例中我們可以看出該方案允許根據硬件情況進行靈活規劃,對擴容規模和節點數量沒有硬性規定,是一種非常自由的擴容方案。

?

增強

?

接下來讓我們討論一個高級話題:對再生存儲空間的利用。對于大多數系統來說,歷史數據較為穩定,被更新或是刪除的概率并不高,反映到數據庫上就是歷史Shard的數據量基本保持恒定,但也不排除某些系統其數據有同等的刪除概率,甚至是越老的數據被刪除的可能性越大,這樣反映到數據庫上就是歷史Shard隨著時間的推移,數據量會持續下降,在經歷了一段時間后,節點就會騰出很大一部分存儲空間,我們把這樣的存儲空間叫再生存儲空間,如何有效利用再生存儲空間是這些系統在設計擴容方案時需要特別考慮的。回到我們的方案,實際上我們只需要在現有基礎上進行一個簡單的升級就可以實現對再生存儲空間的利用,升級的關鍵就是將過去ShardGroupFragmentTable的單一的ID區間提升為多重ID區間。為此我們把ShardGroupFragmentTableID區間屬性抽離出來,分別用ShardGroupIntervalFragmentTableIdInterval表示,并和它們保持一對多關系。


讓我們還是通過一個示例來了解升級后的方案是如何工作的。

?

階段三:不擴容,重復利用再生存儲空間

?

假設系統又經過一段時間的運行之后,二次擴容的6000W條存儲空間即將耗盡,但是由于系統自身的特點,早期的很多數據被刪除,Shard0Shard1又各自騰出了一半的存儲空間,于是ShardGroup0總計有2000W條的存儲空間可以重新利用。為此,我們重新將ShardGroup0標記為writable=true,并給它追加一段ID區間:10000W-12000W,進而得到如下規劃方案:

7. 重復利用2000W再生存儲空間的規劃方案

相應拓撲結構的元數據如下:

小結

這套方案綜合利用了增量區間和散列兩種路由方式的優勢,避免了數據遷移和熱點問題,同時,它對Sharding拓撲結構建模,使用了一致的路由算法,從而避免了擴容時修改路由代碼,是一種理想的Sharding擴容方案。

?

?

?

轉載:

http://blog.csdn.Net/bluishglc/article/details/6161475

轉載:《MySQL性能調優與架構設計》簡朝陽

http://www.2cto.com/database/201410/341071.html

一、 基本思想

Sharding的基本思想就要把一個數據庫切分成多個部分放到不同的數據庫(server)上,從而緩解單一數據庫的性能問題。對于海量數據的數據庫,如果是因為表多而數據多,這時候適合使用垂直切分,即把關系緊密(比如同一模塊)的表切分出來放在一個服務器上。如果表并不多,但每張表的數據非常多,這時候適合水平切分,即把表的數據按某種規則(比如按ID散列)切分到多個數據庫(server)上。根據實際情況做出選擇,也可能會綜合使用垂直與水平切分

1、 垂直切分

數據的垂直切分,也可以稱之為縱向切分。將數據庫想象成為由很多個一大塊一大塊的數據塊(表)組成,我們垂直的將這些數據塊切開,然后將他們分散到多臺數據庫主機上面,這樣的切分方法就是一個垂直(縱向)的數據切分。

系統功能可以基本分為以下四個功能模塊:用戶、群組消息、相冊以及事件,分別對應為如下這些表:

1. 用戶模塊表 useruser_profileuser_groupuser_photo_album

2. 群組討論表 groupsgroup_messagegroup_message_contenttop_message

3. 相冊相關表 photophoto_albumphoto_album_relationphoto_comment

4. 事件信息表 event

模塊之間的關系:

1. 群組討論模塊和用戶模塊之間主要存在通過用戶或者是群組關系來進行關聯。一般關聯的時候都會是通過用戶id或者nick_name以及groupid來進行關聯,,通過模塊之間的接口實現不會帶來太多麻煩。

2. 相冊模塊僅僅與用戶模塊存在通過用戶的關聯。這兩個模塊之間的關聯基本就有通過用戶id關聯的內容,簡單清晰,接口明確。

3. 事件模塊與各個模塊可能都有關聯,但是都只關注其各個模塊中對象的信息ID,同樣可以做到很容易分拆。

所以,我們第一步可以將數據庫按照功能模塊相關的表進行一次垂直拆分,每個模塊 涉及的表單獨到一個數據庫中,模塊與模塊之間的表關聯都在應用系統端通過接口來處理。 如下圖所示:

?

通過這樣的垂直切分之后,之前只能通過一個數據庫來提供的服務,就被分拆成四個數據庫來提供服務,服務能力自然是增加幾倍了。

垂直切分的優點

u 數據庫的拆分簡單明了,拆分規則明確

u 應用程序模塊清晰明確,整合容易

u 數據維護方便易行,容易定位

垂直切分的缺點

u 部分表關聯無法在數據庫級別完成,需要在程序中完成

u 對于訪問極其頻繁且數據量超大的表仍然存在性能平靜,不一定能滿足要求

u 事務處理相對更為復雜

u 切分達到一定程度之后,擴展性會遇到限制

u 過度切分可能會帶來系統過渡復雜而難以維護

2、 水平切分

數據的水平切分,一般來說,簡單的水平切分主要是將某個訪問極其平凡的表再按照某個字段的某種規則來分散到多個表之中,每個表中包含一部分數據。簡單來說,我們可以將數據的水平切分理解為是按照數據行的切分,就是將表中的某些行切分到一個數據庫,而另外的某些行又切分到其他的數據庫中。

對于我們的示例數據庫來說,大部分的表都可以根據用戶ID來進行水平的切分,不同用戶相關的數據進行切分之后存放在不同的數據庫中。如將所有用戶ID通過2取模,然后分別存放于兩個不同的數據庫中,每個和用戶ID關聯上的表都可以這樣切分。這樣,基本上每個用戶相關的數據,都在同一個數據庫中,即使是需要關聯,也可以非常簡單的關聯上。

我們可以通過下圖來更為直觀的展示水平切分相關信息:


水平切分的優點

u 表關聯基本能夠在數據庫端全部完成

u 不會存在某些超大型數據量和高負載的表遇到瓶頸的問題

u 應用程序端整體架構改動相對較少

u 事務處理相對簡單

u 只要切分規則能夠定義好,基本上較難遇到擴展性限制

水平切分的缺點

u 切分規則相對更為復雜,很難抽象出一個能夠滿足整個數據庫的切分規則

u 后期數據的維護難度有所增加,人為手工定位數據更困難

u 應用系統各模塊耦合度較高,可能會對后面數據的遷移拆分造成一定的困難

3、 垂直與水平聯合切分

一般來說,我們數據庫中的所有表很難通過某一個(或少數幾個)字段全部關聯起來,所以很難簡單的僅僅通過數據的水平切分來解決所有問題。而垂直切分也只能解決部分問題,對于那些負載非常高的系統,即使僅僅只是單個表都無法通過單臺數據庫主機來承擔其負載,我們必須結合水平垂直兩種切分方式同時使用,充分利用兩者的優點,避開其缺點

每一個應用系統的負載都是一步一步增長上來的,在開始遇到性能瓶頸的時候,大多數架構師和DBA都會選擇先進行數據的垂直拆分,然而,隨著業務的不斷擴張,系統負載的持續增長,在系統穩定一段時期之后,經過了垂直拆分之后的數據庫集群可能又再一次不堪重負,遇到了性能瓶頸。

這時候我們就必須要通過數據的水平切分的優勢,來解決這里所遇到的問題。對于我們的示例數據庫,假設在最開始,我們進行了數據的垂直切分,然而隨著業務的不斷增長,數據庫系統遇到了瓶頸,我們選擇重構數據庫集群的架構。如何重構?考慮到之前已經做好了數據的垂直切分,而且模塊結構清晰明確。而業務增長的勢頭越來越猛,即使現在進一步再次拆分模塊,也堅持不了太久。我們選擇了在垂直切分的基礎上再進行水平拆分。

在經歷過垂直拆分后的各個數據庫集群中的每一個都只有一個功能模塊,而每個功能模切中的所有表基本上都會與某個字段進行關聯。如用戶模塊全部都可以通過用戶ID進行切分,群組討論模塊則都通過群組ID 來切分,相冊模塊則根據相冊 ID 分,最后的事件通知信息表考慮到數據的時限性(僅僅只會訪問最近某個事件段的信息),則考慮按時間來切分。

下圖展示了切分后的整個架構:

?

在應對不同的應用場景的時候,也需要充分考慮到這兩種切分方法各自的局限,以及各自的優勢,在不同的時期(負載壓力)使用不同的結合方式。

聯合切分的優點

u 可以充分利用垂直切分和水平切分各自的優勢而避免各自的缺陷

u 讓系統擴展性得到最大化提升

聯合切分的缺點

u 數據庫系統架構比較復雜,維護難度更大

u 應用程序架構也相對更復雜

?

二、 拆分實施策略和示例演示

第一部分:實施策略

?

1. 準備階段

對數據庫進行分庫分表(Sharding)前,需要充分了解系統業務邏輯和數據庫schema.繪制一張數據庫ER圖,以圖為基礎劃分shard,直觀易行,可以確保清醒思路。

2. 分析階段

1. 垂直切分

垂直切分的依據原則是:將業務緊密,表間關聯密切的表劃分在一起,例如同一模塊的表。結合已經準備好的數據庫ER圖或領域模型圖,仿照活動圖中的泳道概念,一個泳道代表一個shard,把所有表格劃分到不同的泳道中。下面的分析示例會展示這種做法。這種方式多個數據庫之間的表結構不同。

2. 水平切分

垂直切分后,需要對shard內表格的數據量和增速進一步分析,以確定是否需要進行水平切分。這些數據庫中的表結構完全相同。

??? 2.1 若劃分到一起的表格數據增長緩慢,在產品上線后可遇見的足夠長的時期內均可以由單一數據庫承載,則不需要進行水平切分,所有表格駐留同一shard,所有表間關聯關系會得到最大限度的保留,同時保證了書寫SQL的自由度,不易受joingroup byorder by等子句限制。

2.2 若劃分到一起的表格數據量巨大,增速迅猛,需要進一步進行水平分割。進一步的水平分割就這樣進行:

2.2.1結合業務邏輯和表間關系,將當前shard劃分成多個更小的shard,通常情況下,這些更小的shard每一個都只包含一個主表(將以該表ID進行散列的表)和多個與其關聯或間接關聯的次表。這種一個shard一張主表多張次表的狀況是水平切分的必然結果。這樣切分下來,shard數量就會迅速增多。如果每一個shard代表一個獨立的數據庫,那么管理和維護數據庫將會非常麻煩,而且這些小shard往往只有兩三張表,為此而建立一個新庫,利用率并不高,因此,在水平切分完成后可再進行一次反向的Merge”,即:將業務上相近,并且具有相近數據增長速率(主表數據量在同一數量級上)的兩個或多個shard放到同一個數據庫上,在邏輯上它們依然是獨立的shard,有各自的主表,并依據各自主表的ID進行散列,不同的只是它們的散列取模(即節點數量)必需是一致的。這樣,每個數據庫結點上的表格數量就相對平均了。

2.2.2 所有表格均劃分到合適的shard之后,所有跨越shard的表間關聯都必須打斷,在書寫sql時,跨shardjoingroup byorder by都將被禁止,需要在應用程序層面協調解決這些問題。

3. 實施階段

??? 如果項目在開發伊始就決定進行分庫分表,則嚴格按照分析設計方案推進即可。如果是在中期架構演進中實施,除搭建實現sharding邏輯的基礎設施外,還需要對原有SQL逐一過濾分析,修改那些因為sharding而受到影響的sql

?

第二部分:示例演示

??? 以下使用jpetstore(寵物店的電子商務系統)來演示如何進行分庫分表(sharding)在分析階段的工作。jpetstore來自原ibatis官方的一個Demo版本,SVN地址為:

http://mybatis.googlecode.com/svn/tags/java_release_2.3.4-726/jpetstore-5


??? 由于系統較簡單,我們很容易從模型上看出,其主要由三個模塊組成:用戶,產品和訂單。那么垂直切分的方案也就出來了。接下來看水平切分,如果我們從一個實際的寵物店出發考慮,可能出現數據激增的單表應該是AccountOrder,因此這兩張表需要進行水平切分。對于Product模塊來說,如果是一個實際的系統,ProductItem的數量都不會很大,因此只做垂直切分就足夠了,也就是(ProductCategoryItemIventorySupplier)五張表在一個數據庫結點上(沒有水平切分,不會存在兩個以上的數據庫結點)。但是作為一個演示,我們假設產品模塊也有大量的數據需要我們做水平切分,那么分析來看,這個模塊要拆分出兩個shard:一個是(Product(主),Category),另一個是(Item(主),IventorySupplier),同時,我們認為:這兩個shard在數據增速上應該是相近的,且在業務上也很緊密,那么我們可以把這兩個shard放在同一個數據庫節點上,ItemProduct數據在散列時取一樣的模。根據前文介紹的圖紙繪制方法,我們得到下面這張sharding示意圖:


對于這張圖再說明幾點:

1. 使用泳道表示物理shard(一個數據庫結點)

2. 若垂直切分出的shard進行了進一步的水平切分,但公用一個物理shard的話,則用虛線框住,表示其在邏輯上是一個獨立的shard

3. 深色實體表示主表

4. X表示需要打斷的表間關聯

三、 全局主鍵生成策略

??? 一旦數據庫被切分到多個物理結點上,我們將不能再依賴數據庫自身的主鍵生成機制。一方面,某個分區數據庫自生成的ID無法保證在全局上是唯一的;另一方面,應用程序在插入數據之前需要先獲得ID,以便進行SQL路由。

??? flickr開發團隊在2010年撰文介紹了flickr使用的一種主鍵生成測策略,同時表示該方案在flickr上的實際運行效果也非常令人滿意,它與一般Sequence表方案有些類似,但卻很好地解決了性能瓶頸和單點問題,是一種非常可靠而高效的全局主鍵生成方案。

?

flickr這一方案的整體思想是:建立兩臺以上的數據庫ID生成服務器,每個服務器都有一張記錄各表當前IDSequence表,但是SequenceID增長的步長是服務器的數量,起始值依次錯開,這樣相當于把ID的生成散列到了每個服務器節點上。例如:如果我們設置兩臺數據庫ID生成服務器,那么就讓一臺的Sequence表的ID起始值為1,每次增長步長為2,另一臺的Sequence表的ID起始值為2,每次增長步長也為2,那么結果就是奇數的ID都將從第一臺服務器上生成,偶數的ID都從第二臺服務器上生成,這樣就將生成ID的壓力均勻分散到兩臺服務器上,同時配合應用程序的控制,當一個服務器失效后,系統能自動切換到另一個服務器上獲取ID,從而保證了系統的容錯。

關于這個方案,有幾點細節這里再說明一下:

1. flickr的數據庫ID生成服務器是專用服務器,服務器上只有一個數據庫,數據庫中表都是用于生成Sequence的,這也是因為auto-increment-offsetauto-increment-increment這兩個數據庫變量是數據庫實例級別的變量。

2. flickr的方案中表格中的stub字段只是一個char(1) NOT NULL存根字段,并非表名,因此,一般來說,一個Sequence表只有一條紀錄,可以同時為多張表生成ID,如果需要表的ID是有連續的,需要為該表單獨建立Sequence表。

3. 方案使用了MySQLLAST_INSERT_ID()函數,這也決定了Sequence表只能有一條記錄。

4. 使用REPLACE INTO插入數據,這是很討巧的作法,主要是希望利用mysql自身的機制生成ID,不僅是因為這樣簡單,更是因為我們需要ID按照我們設定的方式(初值和步長)來生成。

5. SELECT LAST_INSERT_ID()必須要于REPLACE INTO語句在同一個數據庫連接下才能得到剛剛插入的新ID,否則返回的值總是0

6. 該方案中Sequence表使用的是MyISAM引擎,以獲取更高的性能,注意:MyISAM引擎使用的是表級別的鎖,MyISAM對表的讀寫是串行的,因此不必擔心在并發時兩次讀取會得到同一個ID(另外,應該程序也不需要同步,每個請求的線程都會得到一個新的connection,不存在需要同步的共享資源)。經過實際對比測試,使用一樣的Sequence表進行ID生成,MyISAM引擎要比InnoDB表現高出很多!

7. 可使用純JDBC實現對Sequence表的操作,以便獲得更高的效率,實驗表明,即使只使用spring JDBC性能也不及純JDBC來得快!

?

實現該方案,應用程序同樣需要做一些處理,主要是兩方面的工作:

1. 自動均衡數據庫ID生成服務器的訪問

2. 確保在某個數據庫ID生成服務器失效的情況下,能將請求轉發到其他服務器上執行。

四、 sharding實現層面

??? 通過前面的章節,我們已經很清楚了通過數據庫的數據切分可以極大的提高系統的擴展性。但是,數據庫中的數據在經過垂直和(或)水平切分被存放在不同的數據庫主機之后,應用系統面臨的最大問題就是如何來讓這些數據源得到較好的整合。

在應用服務器與數據庫之間通過代理實現

在應用服務器與數據庫之間加入一個代理,應用程序向數據發出的數據請求會先通過代理,代理會根據配置的路由規則,對SQL進行解析后路由到目標shard,因為這種方案對應用程序完全透明,通用性好,所以成為了很多sharding產品的選擇。在這方面較為知名的產品是mysql官方的代理工具:Mysql Proxy和一款國人開發的產品:amoebamysql proxy本身并沒有實現任何sharding邏輯,它只是作為一種面向mysql數據庫的代理,給開發人員提供了一個嵌入sharding邏輯的場所,它使用lua作為編程語言,這對很多團隊來說是需要考慮的一個問題。amoeba則是專門實現讀寫分離與sharding的代理產品,它使用非常簡單,不使用任何編程語言,只需要通過xml進行配置。不過amoeba不支持事務(從應用程序發出的包含事務信息的請求到達amoeba時,事務信息會被抹去,因此,即使是單點數據訪問也不會有事務存在)一直是個硬傷。當然,這要看產品的定位和設計理念,我們只能說對于那些對事務要求非常高的系統,amoeba是不適合的。

Amoeba For MySQL主要是專門針對MySQL數據庫的解決方案,前端應用程序請求的協議以及后端連接的數據源數據庫都必須是MySQL。對于客戶端的任何應用程序來說, amoeba For MySQL和一個MySQL沒有什么區別,任何使用MySQL協議的客戶端請求, 都可以被 Amoeba For MySQL解析并進行相應的處理,下圖可以告訴我們Amoeba For MySQL的架構信息:

?

Amoeba使用指南:

http://docs.hexnova.com/amoeba/index.html

其他的一些實現層可以參考:

http://blog.csdn.NET/bluishglc/article/details/7766508

?

五、 多數據源的事務處理

?

?

?

六、 一種支持自由規劃無須數據遷移和修改路由代碼的Sharding擴容方案

Sharding擴容——系統維護不能承受之重

?

任何Sharding系統,在上線運行一段時間后,數據就會積累到當前節點規模所能承載的上限,此時就需要對數據庫進行擴容了,也就是增加新的物理結點來分攤數據。如果系統使用的是基于ID進行散列的路由方式,那么團隊需要根據新的節點規模重新計算所有數據應處的目標Shard,并將其遷移過去,這對團隊來說無疑是一個巨大的維護負擔;而如果系統是按增量區間進行路由(如每1千萬條數據或是每一個月的數據存放在一個節點上 ),雖然可以避免數據的遷移,卻有可能帶來熱點問題,也就是近期系統的讀寫都集中在最新創建的節點上(很多系統都有此類特點:新生數據的讀寫頻率明顯高于舊有數據),從而影響了系統性能。面對這種兩難的處境,Sharding擴容顯得異常困難。

?

一般來說,理想的擴容方案應該努力滿足以下幾個要求:

1.? 最好不遷移數據 (無論如何,數據遷移都是一個讓團隊壓力山大的問題)

2. 允許根據硬件資源自由規劃擴容規模和節點存儲負載

3. 能均勻的分布數據讀寫,避免熱點問題

4. 保證對已經達到存儲上限的節點不再寫入數據

目前,能夠避免數據遷移的優秀方案并不多,相對可行的有兩種,一種是維護一張記錄數據ID和目標Shard對應關系的映射表,寫入時,數據都寫入新擴容的Shard,同時將ID和目標節點寫入映射表,讀取時,先查映射表,找到目標Shard后再執行查詢。該方案簡單有效,但是讀寫數據都需要訪問兩次數據庫,且映射表本身也極易成為性能瓶頸。為此系統不得不引入分布式緩存來緩存映射表數據,但是這樣也無法避免在寫入時訪問兩次數據庫,同時大量映射數據對緩存資源的消耗以及專門為此而引入分布式緩存的代價都是需要權衡的問題。另一種方案來自淘寶綜合業務平臺團隊,它利用對2的倍數取余具有向前兼容的特性(如對4取余得1的數對2取余也是1)來分配數據,避免了行級別的數據遷移,但是依然需要進行表級別的遷移,同時對擴容規模和分表數量都有限制。總得來說,這些方案都不是十分的理想,多多少少都存在一些缺點,這也從一個側面反映出了Sharding擴容的難度。

?

取長補短,兼容并包——一種理想的Sharding擴容方案

?

如前文所述,Sharding擴容與系統采用的路由規則密切相關:基于散列的路由能均勻地分布數據,但卻需要數據遷移,同時也無法避免對達到上限的節點不再寫入新數據;基于增量區間的路由天然不存在數據遷移和向某一節點無上限寫入數據的問題,但卻存在熱點困擾。我們設計方案的初衷就是希望能結合兩種路由規則的優勢,摒棄各自的劣勢,創造出一種接近理想狀態的擴容方式,而這種方式簡單概括起來就是:全局按增量區間分布數據,使用增量擴容,無數據遷移,局部使用散列方式分散數據讀寫,解決熱點問題,同時對Sharding拓撲結構進行建模,使用一致的路由算法,擴容時只需追加節點數據,不再修改散列邏輯代碼。

?

原理

?

首先,作為方案的基石,為了能使系統感知到Shard并基于Shard的分布進行路由計算,我們需要建立一個可以描述Sharding拓撲結構的編程模型。按照一般的切分原則,一個單一的數據庫會首先進行垂直切分,垂直切分只是將關系密切的表劃分在一起,我們把這樣分出的一組表稱為一個Partition。 接下來,如果Partition里的表數據量很大且增速迅猛,就再進行水平切分,水平切分會將一張表的數據按增量區間或散列方式分散到多個Shard上存儲。在我們的方案里,我們使用增量區間與散列相結合的方式,全局上,數據按增量區間分布,但是每個增量區間并不是按照某個Shard的存儲規模劃分的,而是根據一組Shard的存儲總量來確定的,我們把這樣的一組Shard稱為一個ShardGroup,局部上,也就是一個ShardGroup內,記錄會再按散列方式均勻分布到組內各Shard上。這樣,一條數據的路由會先根據其ID所處的區間確定ShardGroup,然后再通過散列命中ShardGroup內的某個目標Shard。在每次擴容時,我們會引入一組新的Shard,組成一個新的ShardGroup,為其分配增量區間并標記為可寫入,同時將原有ShardGroup標記為不可寫入,于是新生數據就會寫入新的ShardGroup,舊有數據不需要遷移。同時,在ShardGroup內部各Shard之間使用散列方式分布數據讀寫,進而又避免了熱點問題。最后,在Shard內部,當單表數據達到一定上限時,表的讀寫性能就開始大幅下滑,但是整個數據庫并沒有達到存儲和負載的上限,為了充分發揮服務器的性能,我們通常會新建多張結構一樣的表,并在新表上繼續寫入數據,我們把這樣的表稱為分段表Fragment Table)。不過,引入分段表后所有的SQL在執行前都需要根據ID將其中的表名替換成真正的分段表名,這無疑增加了實現Sharding的難度,如果系統再使用了某種ORM框架,那么替換起來可能會更加困難。目前很多數據庫提供一種與分段表類似的分區機制,但沒有分段表的副作用,團隊可以根據系統的實現情況在分段表和分區機制中靈活選擇。總之,基于上述切分原理,我們將得到如下Sharding拓撲結構的領域模型:


在這個模型中,有幾個細節需要注意:ShardGroupwritable屬性用于標識該ShardGroup是否可以寫入數據,一個Partition在任何時候只能有一個ShardGroup是可寫的,這個ShardGroup往往是最近一次擴容引入的;startIdendId屬性用于標識該ShardGroupID增量區間;ShardhashValue屬性用于標識該Shard節點接受哪些散列值的數據;FragmentTablestartIdendId是用于標識該分段表儲存數據的ID區間。

確立上述模型后,我們需要通過配置文件或是在數據庫中建立與之對應的表來存儲節點元數據,這樣,整個存儲系統的拓撲結構就可以被持久化起來,系統啟動時就能從配置文件或數據庫中加載出當前的Sharding拓撲結構進行路由計算了(如果結點規模并不大可以使用配置文件,如果節點規模非常大,需要建立相關表結構存儲這些結點元數據。從最新的Oracle發布的《面向大規模可伸縮網站基礎設施的MySQL參考架構》白皮書一文的超大型系統架構參考章節給出的架構圖中我們可以看到一種名為:Shard Catalog的專用服務器,這個其實是保存結點配置信息的數據庫),擴容時只需要向對應的文件或表中加入相關的節點信息重啟系統即可,不需要修改任何路由邏輯代碼。

示例

讓我們通過示例來了解這套方案是如何工作的。

階段一:初始上線

假設某系統初始上線,規劃為某表提供4000W條記錄的存儲能力,若單表存儲上限為1000W條,單庫存儲上限為2000W條,共需2Shard,每個Shard包含兩個分段表,ShardGroup增量區間為0-4000W,按2取余分散到2Shard上,具體規劃方案如下:

上面說單表的存儲上線為1000W條,但是為什么圖中Table_0的范圍是0-2000W?)

與之相適應,Sharding拓撲結構的元數據如下:

?

階段二:系統擴容

?

經過一段時間的運行,當原表總數據逼近4000W條上限時,系統就需要擴容了。為了演示方案的靈活性,我們假設現在有三臺服務器Shard2Shard3Shard4,其性能和存儲能力表現依次為Shard2<Shard3<Shard4,我們安排Shard2儲存1000W條記錄,Shard3儲存2000W條記錄,Shard4儲存3000W條記錄,這樣,該表的總存儲能力將由擴容前的4000W條提升到10000W條,以下是詳細的規劃方案:

?

相應拓撲結構表數據下:

?

?

從這個擴容案例中我們可以看出該方案允許根據硬件情況進行靈活規劃,對擴容規模和節點數量沒有硬性規定,是一種非常自由的擴容方案。

?

增強

?

接下來讓我們討論一個高級話題:對再生存儲空間的利用。對于大多數系統來說,歷史數據較為穩定,被更新或是刪除的概率并不高,反映到數據庫上就是歷史Shard的數據量基本保持恒定,但也不排除某些系統其數據有同等的刪除概率,甚至是越老的數據被刪除的可能性越大,這樣反映到數據庫上就是歷史Shard隨著時間的推移,數據量會持續下降,在經歷了一段時間后,節點就會騰出很大一部分存儲空間,我們把這樣的存儲空間叫再生存儲空間,如何有效利用再生存儲空間是這些系統在設計擴容方案時需要特別考慮的。回到我們的方案,實際上我們只需要在現有基礎上進行一個簡單的升級就可以實現對再生存儲空間的利用,升級的關鍵就是將過去ShardGroupFragmentTable的單一的ID區間提升為多重ID區間。為此我們把ShardGroupFragmentTableID區間屬性抽離出來,分別用ShardGroupIntervalFragmentTableIdInterval表示,并和它們保持一對多關系。


讓我們還是通過一個示例來了解升級后的方案是如何工作的。

?

階段三:不擴容,重復利用再生存儲空間

?

假設系統又經過一段時間的運行之后,二次擴容的6000W條存儲空間即將耗盡,但是由于系統自身的特點,早期的很多數據被刪除,Shard0Shard1又各自騰出了一半的存儲空間,于是ShardGroup0總計有2000W條的存儲空間可以重新利用。為此,我們重新將ShardGroup0標記為writable=true,并給它追加一段ID區間:10000W-12000W,進而得到如下規劃方案:

7. 重復利用2000W再生存儲空間的規劃方案

相應拓撲結構的元數據如下:

小結

這套方案綜合利用了增量區間和散列兩種路由方式的優勢,避免了數據遷移和熱點問題,同時,它對Sharding拓撲結構建模,使用了一致的路由算法,從而避免了擴容時修改路由代碼,是一種理想的Sharding擴容方案。

?

轉載于:https://www.cnblogs.com/wlzjdm/p/6702740.html

總結

以上是生活随笔為你收集整理的[转]数据库分库分表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产12页| 久久香蕉综合 | 黄色免费网站在线看 | 日本视频网 | 成人av网址大全 | 婷婷五月在线视频 | 久久精品亚洲一区 | 岛国av在线| 美女视频毛片 | 婚后打屁股高h1v1调教 | 99国产精品白浆在线观看免费 | 欧美一区高清 | 日本韩国欧美一区二区三区 | 福利在线免费视频 | 国产精品一区二区三区免费 | 成人毛片av | 日韩视频在线一区二区 | 东北少妇不带套对白 | 久久精品一区二区在线观看 | 国产成人专区 | 男女啪啪网站免费 | 91丝袜一区二区三区 | 国产黄色一级 | 中文字幕av片 | 中文字幕免费在线播放 | 全黄毛片 | 亚洲色图图片 | 中文字幕女同女同女同 | 婷婷第四色 | 最近高清中文在线字幕在线观看 | 久久视频在线观看免费 | 欧美一区在线观看视频 | 夜夜天堂| 国产农村妇女精品久久久 | 亚洲在线一区二区三区 | 亚洲一本在线 | 奇米在线| 亚洲欧美一区二区三区在线 | 久久国产精品电影 | 亚洲av成人无码一区二区三区在线观看 | 日本少妇吞精囗交 | 黄色不卡 | 久久爱99 | 山村大伦淫第1部分阅读小说 | 凹凸精品一区二区三区 | 香蕉a| 日韩精品免费一区二区三区竹菊 | 邻居校草天天肉我h1v1 | 黄色三级三级三级 | 九热精品 | 香蕉视频在线网站 | 日本免费黄色网 | 亚洲hh| 日韩 欧美 国产 综合 | 亚洲欧美日韩激情 | 国产精品1区2区3区4区 | 91调教视频| 国产在线啪 | 欧美九九视频 | 日本黄色片在线播放 | 国产自产视频 | 日韩电影一区二区 | 日本三级中国三级99人妇网站 | 丰满人妻一区二区 | a天堂资源在线 | 丰满岳乱妇国产精品一区 | 久久久精品视频在线观看 | 五月激情婷婷在线 | 看毛片网 | xxxxhd欧美| 伊人久久久久久久久久久久久 | 中文在线观看免费 | 欧美日韩色片 | 国产中文字幕在线视频 | 日韩欧美专区 | 91蝌蚪视频在线 | 先锋影音中文字幕 | 国产又粗又硬又黄的视频 | 91大神一区二区 | 国产一级高清视频 | 国产午夜精品一区 | 欧美日韩国产专区 | 国产欧美一区二区三区鸳鸯浴 | xxxx日本免费 | www.成人在线观看 | 久草av在线播放 | 好大好爽好舒服 | 亚洲成年人 | av一起看香蕉 | 国产精品成熟老女人 | 成年人黄色片 | 免费不卡的av | 中文字幕在线免费播放 | 日日夜夜av | 美日韩精品视频 | 欧美另类极品videosbest最新版本 | 久久亚洲影院 | 亚洲资源在线播放 | 黄色一级片免费在线观看 |