UUID 與順序 ID 作為主鍵
一、概述
在本教程中,我們將探討 UUID 和順序 ID 作為主鍵之間的區別。
在設計數據庫時,選擇合適的主鍵格式對於系統的性能、可伸縮性和數據完整性至關重要。
數據庫中的表必須有一個唯一且不可為空的主鍵列。這樣,主鍵值可以唯一標識每一行。
選擇主鍵的主要決定之一是使用 UUID 還是順序 ID。雖然這兩種方法各有利弊,但最佳選擇取決於具體用例和系統目標。
2.UUID
UUID( Universally Unique IDentifier
)由RFC 4122標准定義,代表一個 128 位的值。
現在大部分關係型數據庫都支持UUID類型:
- Oracle – RAW(16) 類型
- SQL Server – NEWID() 函數
- PostgreSQL – UUID 類型
- MySQL – BINARY(16) 類型或 UUID() 函數
但是,如果數據庫不支持這種類型,我們應該將其定義為 BINARY(16) 類型。使用不同的類型(即 CHAR(32) 也可以,但它會佔用額外且不必要的空間來存儲值)。
現在,讓我們來看看使用 UUID 作為主鍵的優點和缺點。
2.1.分佈式系統
在使用共享數據庫的分佈式系統時,UUID 可以派上用場。
將 UUID 作為主鍵的主要目的是能夠跨分佈式系統共享數據。
使用 UUID 作為主鍵,我們可以保證不會發生衝突,並且我們不必創建一個中央協調系統來管理主鍵的唯一性。
此外,它還降低了數據庫之間集成的複雜性。
分佈式數據庫和 NoSQL 數據庫依賴 UUID 作為鍵(即 MongoDB 或 CouchDB)而不是數值。
2.2.唯一性
UUID 是全局唯一的。換句話說,我們可以用跨表、數據庫和系統唯一的 ID 來標識數據庫中的每條記錄。後者在我們可以動態添加或刪除節點的分佈式系統中尤為重要,並且它們之間的協調可能具有挑戰性。碰撞僅在理論上是可能的。
此外,它們提供了額外的安全性,因為下一個值很難預測。因此,惡意用戶幾乎不可能猜出 ID。另一方面,這樣的用戶可以很容易地猜出連續 ID 的下一個值。
此外,它們不會暴露有關業務數據的信息,因此我們可以安全地將它們用作 URL 路徑的一部分。
2.3.創造價值
UUID 的另一個優點是它可以由應用程序或數據庫系統本身生成。
通常,當具有順序 ID 時,我們依靠數據庫系統來生成和增加主鍵值而不是我們。否則,跟踪我們應該使用的下一個值會很複雜。
但是,放棄生成順序主鍵的責任會帶來不利影響。只有執行完插入語句,我們才能得到新記錄的實際值。
相反,我們可以自己在代碼中生成UUID,而不用告訴數據庫代我們生成。由於 UUID 是唯一的並且不是連續的,我們不必擔心以前的值。
因此,我們可以立即得到主鍵值。我們不需要等到執行插入查詢。
2.4.內存使用情況
UUID 的長度為 128 位,是 BIGINT 類型大小的兩倍,是 INTEGER 類型大小的四倍。
在關係數據庫中,我們通常使用 BIGINT 來存儲數字標識符,而使用 UUID 可能不會有太大的區別,因為它只是大兩倍。
但是,擁有一個大的主鍵可能會導致性能問題,尤其是在選擇查詢和索引時。
2.5.可讀性
UUID 由 32 個十六進制數字組成,由四個破折號分隔,這使得它很難記住。
因此,UUID 的表示可能不被認為是用戶友好的,並且可能難以用語言表達。閱讀和記憶並不容易。
另一方面,順序 ID 易於閱讀和記憶。
但是,ID 值本身是否適合人類閱讀是值得懷疑的。
2.6.排序
另一個缺點是我們無法按自然順序對 UUID 值進行排序。
由於此限制,我們可能被迫使用另一列(例如創建時間戳)來獲取訂購的商品。因此,這可能會增加查詢的執行時間。
3.順序ID
序列是唯一的字母數字值,用於標識數據庫中的序列記錄。
此外,我們只能在序列中使用數字標識符,因為系統無法確定 UUID 類型的下一個序列值。
順序 ID 通常優於 UUID,因為它們需要的空間更少。
數據庫引擎為順序 ID 提供本機支持:
- PostgreSQL——串行
- SQL Server – 身份
- MySQL – 自動遞增
- SQLite – 自動增量
讓我們看看使用順序 ID 作為主鍵的優點和缺點。
3.1.可讀性
與 UUID 值不同,數字標識符更易於閱讀和記憶。
使用數字標識符,我們可以輕鬆跟踪記錄插入數據庫的順序。
此外,我們可以根據記錄的 ID 快速識別記錄之間的關係。
3.2.索引
我們經常在主鍵和外鍵上使用索引來加速選擇查詢和連接。
一些數據庫使用 B+Tree 結構進行索引,而其他數據庫,如 SQL 和 MySQL,則使用聚簇索引。
此外,UUID 值不能很好地建立索引。鍵越長,索引條目需要的內存就越多。
此外,UUID 的索引因子較低,因為它們的值是隨機的。每次我們修改表時,索引都必須更新。這會影響我們系統的性能並使用不必要的內存。
此外,我們還可以在連接表上索引外鍵,這在涉及 UUID 值時會帶來另一個性能損失。
3.3.批量操作
批處理操作是指將多個數據庫操作作為單個工作單元執行的過程。
批處理操作使用順序 ID 效果更好的原因之一是順序 ID 是按可預測的順序生成的。多個數據庫操作可以作為一個批處理來執行,從而優化系統的性能。
主鍵值以可預測的順序生成,新記錄添加到序列的末尾。這使得使用某些類型的查詢成為可能,例如範圍查詢,它要求記錄按其主鍵排序。
此外,順序 ID 通常小於 UUID,這可以通過減少所需的存儲量來提高系統性能。
但是,在分佈式系統中使用順序 ID 時,務必要考慮序列中可能存在的間隙。在這種情況下,將 UUID 作為主鍵是更好的選擇。
3.4.可預測
序列標識符遵循使它們可預測的特定結構。
這可能允許惡意用戶閱讀他們不應該閱讀的信息。
我們可能最終無意中暴露了一些私有數據和業務邏輯。例如,最後一個 ID 可以表示用戶生成的發票總數,這可以揭示收入信息。
3.5.並發
如前所述,應用程序生成順序 ID 會很複雜。要創建 ID,我們需要查詢數據庫以確定下一個可用值。
在分佈式系統中,多個系統將數據插入數據庫,這通常意味著我們最終可能會在數據中發生衝突。不同的系統很可能會產生相同的鍵值。
為了克服這個問題,我們需要創建單獨的服務來產生連續的價值。此外,相同的服務將成為單點故障。
3.6.大小限制
最後,順序 ID 限制了它們的大小。儘管數字標識符的最大數量很大,但可能會用完數字。
如果我們使用 INT 類型作為主鍵,最終,我們可以達到最大數量 (2,147,483,647),這將導致靜默溢出錯誤。因此,我們最終可能會以負值作為主鍵。
4. UUID和Sequential ID的區別
綜上所述,下表顯示了 UUID 和 Sequential ID 之間的區別。
UUID | 序號 |
---|---|
佔用 128 位 | 對於 BIGINT 類型需要 64 位,對於 INT 類型需要 32 位 |
碰撞僅在理論上是可能的 | 由於大小限制,可能會發生碰撞 |
在分佈式系統中運行良好 | 需要有一個協調組件來避免重複值 |
NoSQL 和分佈式數據庫的良好支持 | 不建議在 NoSQL 和分佈式數據庫中使用它 |
不可預測 | 可預測 |
難以記憶和發聲 | 易於閱讀和記憶 |
5.結論
在本教程中,我們了解了 UUID 和順序 ID 作為主鍵的區別。
總之,是使用 UUID 還是順序 ID 作為主鍵取決於系統的具體用例和目標
如果我們正在使用分佈式系統並且我們需要全局唯一性,那麼 UUID 可能是最佳選擇。另一方面,如果我們的內存使用量有限並且執行 SQL 查詢的性能至關重要,那麼順序 ID 可能最適合。
最終,應根據對系統要求和約束的仔細評估來選擇主鍵。