您好,登錄后才能下訂單哦!
基本概念
直接介紹概念太枯燥了,還是拿個和背景篇相似的例子介紹
業務場景:客戶完成下單,快遞員接受并更新運單狀態,客戶可以隨時查看運單狀態的任務。一票快遞可能有多個子母件。同時,我們需要標記每個運單的狀態,運單狀態的解釋和含義保存在運單狀態字典表中。
因此,我們需要建立如下表:
我們現在按照業務將數據庫垂直拆分成運單庫(單表2000tps,6000W數據),快遞員庫(單表1500tps,100W數據),客戶庫(單表1500tps,1000W數據記錄);假設每個MySQL數據庫單表不能超過2000W數據,單表不能超過1000tps。那么運單庫則需要分成3片,客戶庫需要分成2片,統一由MyCat管理。如下圖所示:
1.邏輯庫
MyCat作為一個中間件,對應用應為無感知的。
應用訪問MyCat,根據之前所述,應用感知到后臺只是一個(或者多個,和訪問MySQL實例一樣)數據庫(假設只有一個數據庫,這個庫叫SF,里面有運單相關表,快遞員相關表和客戶相關表);這里MyCat的數據庫就是邏輯庫。訪問MyCat,結果應該如下面所示
雖然其中的表可能存在于不同的庫,但是表面上,他們屬于同一個MyCat實例中的同一個邏輯庫。所以,雖然上面的架構圖顯示他們不在同一個數據庫,但是在MyCat中,他們在同一個邏輯庫。
2.邏輯表
在邏輯庫下的表就是邏輯表。邏輯表可以分片,也可以不分片。
orders表明顯是要分片的表,但是在MyCat看來,他們雖然分布在不同的分片節點上(分布在不同的MySQL數據庫上),但仍視為是同一個邏輯表,在同一個邏輯庫里。
2.1分片表
分片表,是指那些原有的很大數據的表,需要切分到多個數據庫的表,這樣,每個分片都有一部分數據,所有分片構成了完整的數據。分片表都有自己的分片規則,根據分片規則確定分片。
配置里面,如下配置:
<table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long">
</table>
意思就是用mod-long規則根據主鍵id將運單表orders分割到test1,test2這兩個數據庫(分片節點)上。
請求情況1:
select * from orders where id = 1;
對于分片表的查詢,如果按照分片列查詢,則請求只會被發送到一個分片上。
請求情況2:
select * from orders where id < 100 and id > 0;
對于分片表的查詢,如果按照分片列范圍(在字段類型支持范圍的情況下)查詢,則請求會根據分片規則計算兩個邊界值,然后將請求發送到對應結果的分片上,并合并每個分片的結果。
請求情況3:
select * from orders where initialpoint = 'Beijing';
像這種根據非分片列查詢的情況,請求會被發送到所有分片上,并合并每個分片的結果。
請求情況4:
請求為更新類型的sql語句,與查詢的三種情況相同處理。
2.2 非分片表
一個數據庫中并不是所有的表都很大,某些表是可以不用進行切分的,非分片是相對分片表來說的,就是那些不需要進行數據切分的表。
例如:
<table name="courier" primaryKey="id" dataNode="test3">
</table>
意思就是快遞員表不用分片,保存在test3這個分片節點上。
對于非分片表的操作和對普通數據庫的一樣,因為不涉及到分布式數據庫。
2.3 ER表
關系型數據庫是基于實體關系模型(Entity-Relationship Model)之上,通過其描述了真實世界中事物與關系,Mycat中的ER表即是來源于此。根據這一思路,提出了基于E-R關系的數據分片策略,子表的記錄與所關聯的父表記錄存放在同一個數據分片上,即子表依賴于父表,通過表分組(Table Group)保證數據Join不會跨庫操作。
表分組(Table Group)是解決跨分片數據join的一種很好的思路,也是數據切分規劃的重要一條規則。
如下:
<!-- 運單表,對主鍵id對2取模 -->
<table name="orders" primaryKey="id" dataNode="test$1-2" rule="mod-long">
<!-- 運單子母件表,運單表的子表,order_id與orders的id列對應 -->
<childTable name="orders_cargo" joinKey="order_id" parentKey="id">
</childTable>
</table>
運單表為分片表,運單表和運單子母件表為一對多關系,可以做成父子表。
對于子表的sql請求,都是通過joinKey對應到父表對應字段后,按照之前分片表的規則進行處理。
2.4 全局表
一個真實的業務系統中,往往存在大量的類似字典表的表,這些表基本上很少變動,字典表具有以下幾個特性:
變動不頻繁
數據量總體變化不大
數據規模不大,很少有超過數十萬條記錄。
對于這類的表,在分片的情況下,當業務表因為規模而進行分片以后,業務表與這些附屬的字典表之間的關聯,就成了比較棘手的問題,所以Mycat中通過數據冗余來解決這類表的join,即所有的分片都有一份數據的拷貝,所有將字典表或者符合字典表特性的一些表定義為全局表。
數據冗余是解決跨分片數據join的一種很好的思路,也是數據切分規劃的另外一條重要規則
比如:
<!-- 運單狀態信息表,公共表,放在和運單表同樣的分片上 -->
<table name="order_status_interception" primaryKey="id" type="global" dataNode="test$1-2">
</table>
運單狀態信息字典表,只是注釋每種運單狀態,就是典型的字典表,與分片表orders為多對一的關系。
對于全局表,所有的查詢請求,只會發送到其中一個全局表分片上執行,所有的更新請求,會在每個全局表分片上執行。
2.5 如何決定?
根據之前的描述,我們可以推斷出,對于分片表的修改和查詢,如果是按照分片字段進行查找的話,則請求會被轉發到一個分片上。如果不是按照分片字段的話,就會把請求發到每一個分片上進行查找。所以,分片字段的選擇比較重要!對于全局表,相當于在每個分片上有一份相同的復制,修改請求會在每一個分片上執行,但是查詢只會落到一個分片上。所以,全局表盡量是不會改變的而且是需要和分片表做Join操作的,如果經常改變或者不需要做join,最好還是做成非分片表。
先拋出了這幾種邏輯表的概念,大家先有個印象。現在我們結合具體實際討論如何決定表的類型。
首先,orders表可定是分片表。orders_cargo表是子母件表,一個order可能有多個子母件,所以,最好把orders_cargo作為orders的子表。
這種情況下,orders與orders_cargo按照對應鍵(就是子表按照哪個鍵與主表的哪個鍵對應進行分片。比如orders_cargo就是order_id與orders的id對應。這是以order_id與orders的id進行join結果就是對的)join結果也是正確的。
一對n場景架構
像這種簡單的從屬關系一對n的表,我們處理起來很簡單,一般將它們按照需要做join的鍵設為父子表即可。
但是下面的場景很麻煩,比如快遞員與運單就是多對多的關系,客戶對于運單也是多對多的關系(一個收方,一個寄方)。我們既有快遞員需要查看自己的所有運單的場景和客戶查看自己所有運單的場景。相對的,我們也有查看一個運單涉及到的快遞員還有客戶的場景。
customer表(客戶表)以及courier表(快遞員表)因為與分片表orders之間不做join操作,所以不用作為公共表。
首先,關系表可以作為公共表,這樣的話,涉及到與分片表的join操作沒有限制,因為在每個分片,公共表都是完整的。但是,關系表的更新很頻繁,我們可能不能忍受每更新一次關系表就跑到每個分片上都更新一次(性能,可靠性考慮)。
那么作為運單的子表呢?那么查找一個運單涉及到的快遞員還有客戶就比較簡單。因為根據運單號(也就是分片id)查詢,MyCat就會根據分片規則給他定位到具體分片,而不是去按分片搜索。
這里寫圖片描述
但是相應的,快遞員查看自己所有運單的場景就比較慢,因為請求是發送到每一個分片上查找。
這里寫圖片描述
作為快遞員的子表也有同樣的缺陷。
還有一種方法,就是這種關系表同時作為運單和快遞員的子表。但是這樣,目前需要應用自己去做雙寫。MyCat目前還沒實現這種。當然,我覺得這是一個我們自己可以根據需要改進的地方。MyCat中間件根據關系冗余表關系進行雙寫
另外,究竟取哪種方法,都是從業務出發去考慮的。在這里,如果從快遞員出發去查找以及從運單出發去查找的業務壓力差不多大的話,那么最好就采用關系表同時作為運單和客戶的子表這種方法。然后將快遞員和運單的業務獨立,每個業務應用都去維護自己的關系表,同時通過消息隊列來保持關系表之間的一致性。這樣也不失為一種方法。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。