天天看點

hive導資料到mysql 自增主鍵出錯_MySQL主鍵設計

hive導資料到mysql 自增主鍵出錯_MySQL主鍵設計

今日格言:讓一切回歸原點,回歸最初的為什麼。

本篇講解 Mysql 的

主鍵

問題,從

為什麼

的角度來了解 Mysql 主鍵相關的知識,并拓展到主鍵的生成方案問題。再也不怕被問到 Mysql 時隻知道 CRUD 了。

一、為什麼需要主鍵

  1. 資料記錄需具有 唯一性 (第一範式)
  2. 資料需要關聯 join
  3. 資料庫底層索引用于檢索資料所需

以下廢話連篇,可以直接跳過到下一節。

資訊

是用來消除随機不定性的東西”(香農)。人通過獲得、識别自然界和社會的不同資訊來差別不同僚物,得以認識和改造世界。

資料

是反映客觀事物屬性的記錄,是資訊的具體表現形式。資料經過加工處理之後,就成為資訊;而資訊需要經過數字化轉變成資料才能存儲和傳輸。

資料庫

就是用于存儲資料記錄的。既已如此,

記錄

便是具有确定性(相對)的資訊,其确定性即唯一性。我們得出第一條原因:

1.資料記錄需具有唯一性

世界是由客觀存在及其關系組成的。

資料

是數字化和模型化的存在關系。資料除了本身的描述價值外,其價值還在于其互相關聯性。為實作關聯的準确性,資料需要有對外互相關聯的辨別。是以展現在資料存儲上,

主鍵

的第二作用,也是存在的第二因素即:

2.資料需要關聯 資料

用于描述客觀實在的,本身沒有意義。隻有在根據主觀需求組織之後,通過一定方式滿足人認識事物的過程才具有了意義。是以資料需要被檢索,被組織。則主鍵第三個作用:

3.資料庫底層索引用于檢索資料所需

二、為什麼主鍵不宜過長

這個問題的點在

上。那

有什麼優勢?(嘿嘿嘿,内涵)—— 短不占空間。但這麼點磁盤空間相對整個資料量來說微不足道,而且我們一般不怎麼用到主鍵列。那麼原因應該在

上,而且和原始資料關系不大。以此自然得出和

索引

相關,而且和索引讀取相關。那麼為什麼長主鍵在

索引

中會影響性能?

hive導資料到mysql 自增主鍵出錯_MySQL主鍵設計

上面是 Innodb 的索引資料結構。左邊是

聚簇索引

,通過主鍵定位資料記錄。右邊是

二級索引

,對列資料做索引,通過列資料查找資料主鍵。如果通過二級索引查詢資料,流程如圖上所示,先從二級索引樹上搜尋到

主鍵

,然後在聚簇索引上通過主鍵搜尋到資料行。其中二級索引的葉子節點是直接存儲的主鍵值,而不是主鍵指針。是以如果主鍵太長,一個二級索引樹所能存儲的索引記錄就會變少,這樣在有限的

索引緩沖

中,需要讀取磁盤的次數就會變多,是以性能就會下降。

三、為什麼建議使用自增 ID

hive導資料到mysql 自增主鍵出錯_MySQL主鍵設計

InnoDB 使用

聚簇索引

,如上圖所示,資料記錄本身被存于主索引(一顆 B+Tree)的葉子節點上。這就要求同一個葉子節點内(大小為一個記憶體頁或磁盤頁)的各條資料記錄

按主鍵順序存放

,是以每當有一條新的記錄插入時,MySQL 會根據其主鍵将其插入适當的節點和位置,如果頁面達到裝載因子(InnoDB 預設為 15/16),則開辟一個新的頁(節點)。

如果表使用自增主鍵,那麼每次插入新的記錄,記錄就會

順序添加

到目前索引節點的後續位置,當一頁寫滿,就會自動開辟一個新的頁。這樣就會形成一個

緊湊

的索引結構,近似順序填滿。由于每次插入時也不需要移動已有資料,是以效率很高,也不會增加很多開銷在維護索引上,如下圖左側所示。否則由于每次插入主鍵的值近似于随機,是以每次新記錄都要被插到現有索引頁的中間某個位置,MySQL 不得不為了将新記錄插到合适位置而

移動資料

,如下圖右側所示,這樣就造成了一定的開銷。由于此,Mysql 為維護索引可能需要頻繁的重新整理緩沖,增加了方法磁盤 IO 的次數,而且時常需要對索引結構進行重組織。

hive導資料到mysql 自增主鍵出錯_MySQL主鍵設計

四、業務 Key VS 邏輯 Key

業務 Key

,即使用具有業務意義的 id 作為 Key,比如使用訂單流水号作為訂單表的主鍵 Key。

邏輯 Key

,即無關業務的 Key,按某種規則生成 Key,如自增 Key。

業務 Key 的優點

  • Key 具有業務意義,在查詢時可以直接作為搜尋關鍵字使用
  • 不需要額外的列和索引空間
  • 可以減少一些 join 操作。

業務 Key 的缺點

  • 當業務發生變化時,有時需要變更主鍵
  • 涉及多列 Key 時比較難操作
  • 業務 Key 往往比較長,所占空間更大,導緻更大的磁盤 IO
  • 在 Key 确定前不能持久化資料,有時我們沒有在确定資料 Key 時,就想先添加一條記錄,之後再更新業務 Key
  • 設計一個兼具易用和性能的 Key 生成方案比較難

邏輯 Key 的優點

  • 不會因為業務的變動而需要修改 Key 邏輯
  • 操作簡單,且易于管理
  • 邏輯 Key 往往更小,性能更優
  • 邏輯 Key 更容易保證唯一性
  • 更易于優化

邏輯 Key 缺點

  • 查詢主鍵列和主鍵索引需要額外的磁盤空間
  • 在插入資料和更新資料時需要額外的 IO
  • 更多的 join 可能
  • 如果沒有唯一性政策限制,容易出現重複的 Key
  • 測試環境和正式環境 Key 不一緻,不利于排查問題
  • Key 的值沒有和資料關聯,不符合三範式
  • 不能用于搜尋關鍵字
  • 依賴不同資料庫系統的具體實作,不利于底層資料庫的替換

五、主鍵生成

一般情況下,我們都使用 Mysql 的自增 ID,來作為表的

主鍵

,這樣簡單,而且從上面講到的來看,性能也是最好的。但是在分庫分表的情況情況下,自增 ID 則不能滿足需求。我們可以來看看不同資料庫生成 ID 的方式,也看一些分布式 ID 生成方案。利于我們思考甚至實作自己的分布式 ID 生成服務。

資料庫的實作

Mysql 自增

Mysql 在記憶體中維護一個

自增計數器

,每次通路 auto-increment 計數器的時候, InnoDB 都會加上一個名為

AUTO-INC 鎖

直到該語句結束(注意鎖隻持有到語句結束,不是事務結束)。AUTO-INC 鎖是一個特殊的表級别的鎖,用來提升包含 auto_increment 列的并發插入性。

在分布式的情況下,其實可以獨立一個服務和資料庫來做 id 生成,依舊依賴 Mysql 的表 id 自增能力來為第三方服務統一生成 id。為性能考慮可以不同業務使用不同的表。

Mongodb ObjectId

Mongodb 為防止主鍵沖突,設計了一個 ObjectId 作為主鍵 id。它由一個 12 位元組的十六進制數字組成,其中包含以下幾部分:

  1. Time:時間戳。4 位元組。秒級。
  2. Machine:機器辨別。3 位元組。一般是機器主機名的散列值,這樣就確定了不同主機生成不同的機器 hash 值,確定在分布式中不造成沖突,同一台機器的值相同。
  3. PID:程序 ID。2 位元組。上面的 Machine 是為了確定在不同機器産生的 objectId 不沖突,而 pid 就是為了在同一台機器不同的 mongodb 程序産生的 objectId 不沖突。
  4. INC:自增計數器。3 位元組。前面的九個位元組保證了一秒内不同機器不同程序生成的 objectId 不沖突,自增計數器,用來確定在同一秒内産生的 objectId 也不會發現沖突,允許 256 的 3 次方等于 16777216 條記錄的唯一性。

Cassandra TimeUUID

Cassandra 使用下面規則生成一個唯一的 id:

time + MAC + sequence

方案

  1. Zookeeper 自增:通過 zk 的自增機制實作。
  2. Redis 自增:通過 Redis 的自增機制實作。
  3. UUID:使用 UUID 字元串作為 Key。
  4. snowflake 算法:和 Mongodb 的實作類似,

    1位符号位 + 41位時間戳(毫秒級)+ 10位資料機器位 + 12位毫秒内的序列

開源實作

  1. 百度 UidGenerator:基于 snowflake 算法。
  2. 美團 Leaf:同時實作了基于 Mysql 自增(優化)和 snowflake 算法的機制。

繼續閱讀