本篇講解 Mysql 的主鍵問題,從為什么的角度來了解 Mysql 主鍵相關(guān)的知識,并拓展到主鍵的生成方案問題。再也不怕被問到 Mysql 時只知道 CRUD 了。
一、為什么需要主鍵
數(shù)據(jù)記錄需具有唯一性(第一范式)
數(shù)據(jù)需要關(guān)聯(lián) join
數(shù)據(jù)庫底層索引用于檢索數(shù)據(jù)所需
以下廢話連篇,可以直接跳過到下一節(jié)。
“信息是用來消除隨機不定性的東西”(香農(nóng))。人通過獲得、識別自然界和社會的不同信息來區(qū)別不同事物,得以認(rèn)識和改造世界。數(shù)據(jù)是反映客觀事物屬性的記錄,是信息的具體表現(xiàn)形式。數(shù)據(jù)經(jīng)過加工處理之后,就成為信息;而信息需要經(jīng)過數(shù)字化轉(zhuǎn)變成數(shù)據(jù)才能存儲和傳輸。數(shù)據(jù)庫就是用于存儲數(shù)據(jù)記錄的。既已如此,記錄便是具有確定性(相對)的信息,其確定性即唯一性。我們得出第一條原因:
1.數(shù)據(jù)記錄需具有唯一性
世界是由客觀存在及其關(guān)系組成的。數(shù)據(jù)是數(shù)字化和模型化的存在關(guān)系。數(shù)據(jù)除了本身的描述價值外,其價值還在于其相互關(guān)聯(lián)性。為實現(xiàn)關(guān)聯(lián)的準(zhǔn)確性,數(shù)據(jù)需要有對外相互關(guān)聯(lián)的標(biāo)識。所以體現(xiàn)在數(shù)據(jù)存儲上,主鍵的第二作用,也是存在的第二因素即:
2.數(shù)據(jù)需要關(guān)聯(lián)
數(shù)據(jù)用于描述客觀實在的,本身沒有意義。只有在根據(jù)主觀需求組織之后,通過一定方式滿足人認(rèn)識事物的過程才具有了意義。所以數(shù)據(jù)需要被檢索,被組織。則主鍵第三個作用:
3.數(shù)據(jù)庫底層索引用于檢索數(shù)據(jù)所需
二、為什么主鍵不宜過長
這個問題的點在長上。那短比長有什么優(yōu)勢?(嘿嘿嘿,內(nèi)涵)—— 短不占空間。但這么點磁盤空間相對整個數(shù)據(jù)量來說微不足道,而且我們一般不怎么用到主鍵列。那么原因應(yīng)該在快上,而且和原始數(shù)據(jù)關(guān)系不大。以此自然得出和索引相關(guān),而且和索引讀取相關(guān)。那么為什么長主鍵在索引中會影響性能?
上面是 Innodb 的索引數(shù)據(jù)結(jié)構(gòu)。左邊是聚簇索引,通過主鍵定位數(shù)據(jù)記錄。右邊是二級索引,對列數(shù)據(jù)做索引,通過列數(shù)據(jù)查找數(shù)據(jù)主鍵。如果通過二級索引查詢數(shù)據(jù),流程如圖上所示,先從二級索引樹上搜索到主鍵,然后在聚簇索引上通過主鍵搜索到數(shù)據(jù)行。其中二級索引的葉子節(jié)點是直接存儲的主鍵值,而不是主鍵指針。所以如果主鍵太長,一個二級索引樹所能存儲的索引記錄就會變少,這樣在有限的索引緩沖中,需要讀取磁盤的次數(shù)就會變多,所以性能就會下降。
三、為什么建議使用自增 ID
InnoDB 使用聚簇索引,如上圖所示,數(shù)據(jù)記錄本身被存于主索引(一顆 B+Tree)的葉子節(jié)點上。這就要求同一個葉子節(jié)點內(nèi)(大小為一個內(nèi)存頁或磁盤頁)的各條數(shù)據(jù)記錄按主鍵順序存放,因此每當(dāng)有一條新的記錄插入時,MySQL 會根據(jù)其主鍵將其插入適當(dāng)?shù)墓?jié)點和位置,如果頁面達到裝載因子(InnoDB 默認(rèn)為 15/16),則開辟一個新的頁(節(jié)點)。
如果表使用自增主鍵,那么每次插入新的記錄,記錄就會順序添加到當(dāng)前索引節(jié)點的后續(xù)位置,當(dāng)一頁寫滿,就會自動開辟一個新的頁。這樣就會形成一個緊湊的索引結(jié)構(gòu),近似順序填滿。由于每次插入時也不需要移動已有數(shù)據(jù),因此效率很高,也不會增加很多開銷在維護索引上,如下圖左側(cè)所示。否則由于每次插入主鍵的值近似于隨機,因此每次新記錄都要被插到現(xiàn)有索引頁的中間某個位置,MySQL 不得不為了將新記錄插到合適位置而移動數(shù)據(jù),如下圖右側(cè)所示,這樣就造成了一定的開銷。由于此,Mysql 為維護索引可能需要頻繁的刷新緩沖,增加了方法磁盤 IO 的次數(shù),而且時常需要對索引結(jié)構(gòu)進行重組織。
四、業(yè)務(wù) Key VS 邏輯 Key
業(yè)務(wù) Key,即使用具有業(yè)務(wù)意義的 id 作為 Key,比如使用訂單流水號作為訂單表的主鍵 Key。邏輯 Key,即無關(guān)業(yè)務(wù)的 Key,按某種規(guī)則生成 Key,如自增 Key。
業(yè)務(wù) Key 的優(yōu)點
Key 具有業(yè)務(wù)意義,在查詢時可以直接作為搜索關(guān)鍵字使用
不需要額外的列和索引空間
可以減少一些 join 操作。
業(yè)務(wù) Key 的缺點
當(dāng)業(yè)務(wù)發(fā)生變化時,有時需要變更主鍵
涉及多列 Key 時比較難操作
業(yè)務(wù) Key 往往比較長,所占空間更大,導(dǎo)致更大的磁盤 IO
在 Key 確定前不能持久化數(shù)據(jù),有時我們沒有在確定數(shù)據(jù) Key 時,就想先添加一條記錄,之后再更新業(yè)務(wù) Key
設(shè)計一個兼具易用和性能的 Key 生成方案比較難
邏輯 Key 的優(yōu)點
不會因為業(yè)務(wù)的變動而需要修改 Key 邏輯
操作簡單,且易于管理
邏輯 Key 往往更小,性能更優(yōu)
邏輯 Key 更容易保證唯一性
更易于優(yōu)化
邏輯 Key 缺點
查詢主鍵列和主鍵索引需要額外的磁盤空間
在插入數(shù)據(jù)和更新數(shù)據(jù)時需要額外的 IO