天天看點

分庫分表後全局ID生成方案(上)1 資料庫自增id2 UUID(Universally Unique Identifier,通用唯一辨別碼)3 系統時間

依據資料庫的第二範式,資料庫中每一個表中都需要有一個唯一的主鍵,其他資料元素和主鍵一一對應。

那麼關于主鍵的選擇就成為一個關鍵點了,一般有如下方案:

使用業務字段作為主鍵

比如說對于使用者表來說,可以使用手機号,email或者身份證号作為主鍵。對大部分場景,這并不适用,像評論表,你很難找到一個業務字段主鍵。而對于使用者表,考慮的是業務字段是否能夠唯一辨別人,一人可有多個email和手機号,一旦出現變更email或手機号,就需要變更所有引用的外鍵資訊,是以使用email或者手機作為主鍵不行。

身份證号碼确實是使用者唯一辨別,但由于過于私密,并非使用者系統的必須屬性,你的系統如果沒有要求做實名認證,肯定不會要求使用者填寫身份證号。

而且已有身份證号碼也會變化,比如1999年時身份證号從15位變為18位,但主鍵一變更,以該主鍵為外鍵的表也都要随之變更,影響很大。

使用生成的唯一ID作為主鍵

是以,更推薦使用生成的ID作為資料庫主鍵。不僅是因為其唯一性,且一旦生成就不會變更,可随意引用。

1 資料庫自增id

提供一個專門用于生成主鍵的庫,這樣服務每次接收請求都

  1. 先往單點庫的某表裡插入一條沒啥業務含義的資料
  2. 然後擷取一個資料庫自增id
  3. 取得id後,再寫入對應的分庫分表

優點

簡單,是個人都會

缺點

因為是單庫生成自增id,是以若是高并發場景,有性能瓶頸。

若硬是要改進,那就專門開個服務:

  • 該服務每次就拿到目前id最大值
  • 然後自己遞增幾個id,一次性傳回一批id
  • 然後再把目前最大id值修改成遞增幾個id之後的一個值

但無論怎麼說都隻是基于單庫。

适用場景

分庫分表原因其實就倆:

  1. 單庫的并發負載過高
  2. 單庫的資料量過大

除非并發不高,但資料量太大導緻的分庫分表擴容,可用該方案,因為可能每秒最高并發最多就幾百,那麼就走單獨的一個庫和表生成自增主鍵即可。

并發很低,幾百/s,但是資料量大,幾十億的資料,是以需要靠分庫分表來存放海量資料。

當資料庫分庫分表後,使用自增字段就無法保證 ID 的全局唯一性了嗎?

1.使用資料庫的自增,設定起始值和步長不一樣,不是一樣可以實作嗎?

2.預估每天的資料量,預先生成ID存入緩存(比如Redis)裡面,然後去取,這種方法也簡單?

但是這其實很難預估資料量,某一天有活動咋辦?不同的起始值也可,隻是增加人工成本,增加了庫表咋辦?忘了設定咋辦?

2 UUID(Universally Unique Identifier,通用唯一辨別碼)

2.1 優點

本地生成,不依賴任何第三方系統,是以在性能和可用性上都比較好。

2.2 缺點

2.2.1 無序

生成的ID做好具有單調遞增性,即有序。

為什麼ID要有序呢?

因為在系統設計時,ID可能成為排序字段。

比如實作評論系統,一般會設計兩個表:

  • 評論表

    存儲評論的詳細資訊,其中有ID字段,有評論的内容,還有評論人ID,被評論内容的ID等等,以ID字段作為分區鍵

  • 評論清單

    存儲着内容ID和評論ID的對應關系,以内容ID為分區鍵

擷取内容的評論清單時,需按照時間序倒排,因為ID時間上有序,是以可按評論ID倒序排列。

若評論ID不在時間上有序,就得在評論清單中再備援createTime列以排序,假設内容ID、評論ID和時間都8位元組,就要多出50%存儲空間存儲時間字段,浪費存儲空間。

ID有序會提升資料的寫性能

MySQL InnoDB主鍵也是一種索引。索引資料在B+樹中有序排列。當插入的下一條記錄ID遞增時,DV隻需将其追加到後面。

但若插入資料無序,則DB查找資料應該插入的位置,再挪動該資料後面的資料,造成多餘資料移動開銷。

導緻 B+ 樹索引寫時有着過多的随機寫操作,而機械磁盤:

  • 随機寫時,需先“尋道”找到要寫入位置,即讓磁頭找到對應磁道,很耗時
  • 順序寫就無需尋道,大大提升索引寫性能

寫時不能産生有順序的 append 操作,而需要 insert,将會讀取整個 B+ 樹節點到記憶體,在插入這條記錄後會将整個節點寫回磁盤,這種操作在記錄占用空間較大情況下,性能下降明顯

2.2.2 過長

由32個16進制數字組成的字元串,若作為DB主鍵使用,較耗費空間。

2.2.3 不具備業務含義

現實使用的ID中都包含有一些有意義資料,這些資料會出現在ID的固定位置。

如身份證:

  • 前6位地區編号
  • 7~14生日

    不同城市電話号碼的區号不同,前三位即可看出所屬營運商。

而若生成的ID可被反解,則從反解出的資訊中即可驗證ID,進而知道該ID生成時間、從哪個機房發号器生成、為哪個業務服務,這都有助問題排查。

Snowflake算法則可完美彌補UUID缺點。

随機生成檔案名、編号等,生成Request ID标記單次請求。

3 系統時間

擷取目前時間即可。但問題是高并發時,會有重複,這肯定不合适啊,而且還可能修改系統時間!

若用該方案,一般将目前時間跟很多其他的業務字段拼接起來,作為一個id。若業務上你可以接受,那也行。

你可以将别的業務字段值跟目前時間拼接起來,組成一個全局唯一的編号,比如訂單編号:

時間戳 + 使用者id + 業務含義編碼