分庫分表之拆分鍵設計
衆所周知,在現實世界中,每一個資源都有其提供能力的最大上限,當單一資源達到最大上限後就得讓多個資源同時提供其能力來滿足使用方的需求。同理,在計算機世界中,單一數據庫資源不能滿足使用需求時,我們也會考慮使用多個數據庫同時提供服務來滿足需求。當使用了多個數據庫來提供服務時,最爲關鍵的點是如何讓每一個數據庫比較均勻的承擔壓力,而不至於其中的某些數據庫壓力過大,某些數據庫沒什麼壓力。這其中的關鍵點之一就是拆分鍵的設計。
一、 水平、垂直拆分
在關係數據庫中,當單個庫的負載、連接數、併發數等達到數據庫的最大上限時,就得考慮做數據庫和表的拆分。如一個簡單的電商數據庫,在業務初期,爲了快速驗證業務模式,把用戶、商品、訂單都放到一個數據庫中,隨着業務的發展及用戶量的增長,單數據庫逐漸不能支撐業務(MySQL 中單記錄容量超過 1K 時,單表數據量建議不超過一千萬條),這時就得考慮把數據庫和表做出拆分。
- 垂直拆分:簡單的說就是將數據庫及表由一個拆分爲多個,如我們這裏的電商數據庫,可以垂直拆分爲用戶數據庫、商品數據庫和訂單數據庫,訂單表可以垂直拆分爲訂單基本信息表,訂單收貨地址表、訂單商品表等,每一個表裏保存了一個訂單的一部分數據。
- 水平拆分:簡單地說就是將一個庫、一個表擴展爲多個庫,多個表,每一個拆分後的表中保存的依然是一個訂單的完整信息。如電商數據庫,我們按水平拆分數據庫和表後,每一個拆分後的數據庫表與現有未拆分前的都保持一致。
- 常用拆分方法:上述僅從理論上講解了可行的水平、垂直拆分方法,在實際的生產上,我們拆分一般是按照水平拆表、垂直拆庫這一原則進行,在業務比較複雜的場景下也會對錶進行垂直拆分。
二、 拆分鍵的選取
分庫分表的關鍵項之一是拆分鍵的選取,一般情況下,拆分鍵的選取遵循以什麼維度進行查詢就選取該維度爲拆分鍵。如:訂單表就以訂單號作爲拆分鍵,商品表就以商品編號作爲拆分鍵。拆分鍵選取後,對於一些非拆分鍵的單條件查詢,我們需要怎麼支持呢?在這裏提供 3 種方法供參考。
1、等值法:
對於非拆分鍵的單條件查詢,對這一個單條件的賦值,可以將其值與拆分鍵保持一致。比如在電商場景中,用戶下訂單後,需要通過物流給用戶把商品送到用戶手上。對於用戶來說僅能看到訂單信息,訂單上展示的物流信息用戶也是通過訂單號查詢而來;但對於物流系統來說,其系統裏的業務主鍵(拆分鍵)是運單號,此時,運單號如果和訂單號相同,即可完美解決這一問題。訂單表和運單表的基本數據模型如下:
訂單表:
運單表:
2、索引法:
對於常用的非拆分鍵,我們可以將其與拆分鍵之間建立一個索引關係,當按該條件進行查詢時,先查詢對應的拆分鍵,再通過拆分鍵查詢對應的數據信息。訂單表的索引法查詢表模型如下:
索引表:
3、基因法:
拆分鍵與非拆分鍵的單號生成規則中,存在相同規則的部分且該部分被用作拆分鍵來進行庫表的定位。比如:訂單號生成時,生成一個 Long 類型的單號,由於 Long 是 64 位的,我們可以用其低 4 位取模來定位該訂單存儲的數據庫及表,其他的表的拆分鍵也用 Long 類型的低 4 位取模來定位對應的數據庫及表。還是用訂單表和運單表的模型做解釋如下:
訂單表:
運單表:
當通過訂單表裏的訂單號查運單表時,可以直接用訂單號來查詢其對應的運單信息。
三、 拆分鍵的生成
拆分鍵選取後,接下來是拆分鍵的生成,拆分鍵的生成有多種方式,建議根據業務量及併發量的大小來確定拆分鍵生成的規則,在這裏介紹幾種常用的拆分鍵生成規則。
1、數據庫自增主鍵
在併發量不大的情況下,我們可以使用 MySQL 數據庫裏的自增主鍵來實現拆分鍵。
2、UUID
在 Java 裏,可以使用 Java 自帶的 UUID 工具類直接生成,UUID 的組成:UUID = 當前日期和時間 + 時鐘序列 + 全局唯一的 IEEE 機器識別號組成。其中,全局唯一的 IEEE 機器識別號一般是通過網卡的 MAC 地址獲得,沒有網卡時以其他的方式獲得。UUID 生成的編號不會重複,但不利於閱讀和理解。
3、雪花算法
雪花算法生成的 ID 是一個 64 位大小的整數,結構如下:
從其結構可以看出,第一位是符號位,在使用時一般不使用,後面的 41 位是時間位,是由時間戳來確定的,後面的 10 位是機器位,最後的 12 位是生成的 ID 序列,是每毫秒生成的 ID 數,即每毫秒可以生成 4096 個 ID。從該結構可以看出,10 位機器位決定了使用機器的上限,在某些業務場景下,需要所有的機器使用同一個業務空間,這可能導致機器超限;同時,每一個機器分配後如果機器宕機需要更換時,對 ID 的回收也需要有相應的策略;最爲關鍵的一點是機器的時間是動態調整的,有可能會出現時間回退幾毫秒的情況,如果這個時候獲取到這個時間,則會生成重複的 ID,導致數據重複。
四、 提升總結
單數據庫不能滿足業務場景的情況下,主要的思路還是要進行拆分,無論是 NoSQL 還是關係數據庫,隨着業務量的增長,都得需要把多個服務器資源組合成一個整體共同來支撐業務。數據庫拆分後,如果業務上有多個複雜查詢條件的需求,一般就得把數據同步到 NoSQL 數據庫裏,由 NoSQL 來提供支持。無論什麼時候,數據庫提供的主要能力是存儲能力,對於複雜的計算需求,一般是需要在業務邏輯裏實現。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/loKtCKy-FSqEFG2hb17E0Q