一文詳解為什么MySQL不推薦使用雪花和UUID做主鍵
前言
在 MySQL 表設(shè)計(jì)中,主鍵選擇是影響性能的關(guān)鍵因素。雪花 ID(Snowflake)和 UUID 作為分布式系統(tǒng)中常用的唯一標(biāo)識符,雖能解決全局唯一性問題,但在 MySQL 中卻并非主鍵的最佳選擇。本文將從 MySQL 索引機(jī)制、數(shù)據(jù)存儲原理、性能影響等角度,解析為何不推薦使用這兩種 ID 作為主鍵。
一、MySQL 主鍵的設(shè)計(jì)核心:聚簇索引與數(shù)據(jù)組織
1. 聚簇索引的本質(zhì)
MySQL 的 InnoDB 存儲引擎采用聚簇索引(Clustered Index) ,即數(shù)據(jù)行的物理存儲順序與主鍵索引的順序一致。這意味著:
- 主鍵不僅是數(shù)據(jù)的唯一標(biāo)識,更是數(shù)據(jù)在磁盤上的存儲順序依據(jù)。
- 非主鍵索引(二級索引)存儲的是主鍵值而非數(shù)據(jù)地址,查詢時(shí)需通過主鍵回表。
2. 理想主鍵的特性
為了優(yōu)化聚簇索引的性能,理想的主鍵應(yīng)具備:
- 固定長度:便于快速定位數(shù)據(jù)頁
- 順序增長:新數(shù)據(jù)插入時(shí)按順序追加,減少頁分裂
- 緊湊存儲:占用磁盤空間小,提升索引緩存效率
二、UUID 做主鍵的四大性能缺陷
1. 存儲長度大,浪費(fèi)磁盤空間
- UUID 格式:36 位字符串(如550e8400-e29b-41d4-a716-446655440000)
- 存儲開銷:
- CHAR(36):占用 36 字節(jié)(定長存儲)
- VARCHAR(36):占用 36+1 字節(jié)(變長存儲)
- 對比自增主鍵:
- BIGINT UNSIGNED:僅占 8 字節(jié),節(jié)省 78% 空間
2. 無序性導(dǎo)致索引碎片化
- 插入模式:UUID 是隨機(jī)生成的字符串,插入時(shí)數(shù)據(jù)頁位置隨機(jī)分布
B + 樹分裂:
- 隨機(jī)插入導(dǎo)致數(shù)據(jù)頁頻繁分裂(Page Split),降低寫入性能
- 碎片化索引增加查詢時(shí)的 I/O 次數(shù)
3. 字符串比較效率低下
- 索引查詢:UUID 的字符串比較需逐個(gè)字符對比(如字典序)
- 范圍查詢:無法利用主鍵的有序性優(yōu)化(如BETWEEN查詢)
- 性能數(shù)據(jù):UUID 主鍵的查詢速度比自增主鍵慢 30% 以上(實(shí)測數(shù)據(jù))
4. 影響二級索引性能
- 二級索引存儲 UUID 主鍵值,導(dǎo)致:
- 索引條目變大,降低緩存命中率
- 回表查詢時(shí)需多次隨機(jī) I/O(UUID 的無序性加劇此問題)
三、雪花 ID 的優(yōu)化與局限性
1. 雪花 ID 的改進(jìn)點(diǎn)
- 數(shù)據(jù)類型:長整型(8 字節(jié)),比 UUID 更緊湊
- 有序性:時(shí)間戳 + 工作進(jìn)程 ID,保證趨勢遞增
- 示例結(jié)構(gòu):
1bit(符號位)+41bit(時(shí)間戳)+10bit(工作節(jié)點(diǎn))+12bit(序列號)
2. 仍存在的問題
(1)非完全順序性
- 同一毫秒內(nèi)生成的 ID 順序由序列號決定,可能產(chǎn)生 "小范圍逆序"
- 跨工作節(jié)點(diǎn)時(shí),ID 順序可能跳躍,導(dǎo)致輕微的頁分裂
(2)分布式場景的隱藏成本
- 需要獨(dú)立的 ID 生成服務(wù)(如雪花算法的工作節(jié)點(diǎn)管理)
- 時(shí)鐘回退問題:服務(wù)器時(shí)間回退可能導(dǎo)致 ID 重復(fù)
(3)擴(kuò)容困難
- 工作節(jié)點(diǎn) ID 的位數(shù)限制(如 10bit 最多支持 1024 個(gè)節(jié)點(diǎn))
- 節(jié)點(diǎn)重啟后可能導(dǎo)致 ID 不連續(xù)
四、MySQL 主鍵的最佳實(shí)踐:自增 ID vs 分布式 ID
1. 單機(jī)場景:自增主鍵(AUTO_INCREMENT)
優(yōu)勢:
- 順序插入:數(shù)據(jù)頁按主鍵順序追加,無頁分裂開銷
- 存儲高效:INT UNSIGNED占 4 字節(jié),BIGINT UNSIGNED占 8 字節(jié)
- 性能最優(yōu):插入速度比 UUID 快 50% 以上(InnoDB 實(shí)測)
實(shí)現(xiàn)方式:
CREATE TABLE `users` ( `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `name` VARCHAR(50) NOT NULL, ... ) ENGINE=InnoDB;
2. 分布式場景:推薦組合方案
方案 1:自增主鍵 + 分庫分表
- 水平拆分:按業(yè)務(wù)維度(如用戶 ID 模 1024)分到不同數(shù)據(jù)庫
- 主鍵規(guī)則:每個(gè)庫的自增起始值不同(如庫 1 從 1 開始,庫 2 從 1000001 開始)
方案 2:使用 MySQL 原生分布式 ID(如 8.0 + 的 SERVER_UUID)
-- 生成128位UUID(優(yōu)化存儲為16字節(jié)二進(jìn)制)
SET @uuid = UUID_TO_BIN('550e8400-e29b-41d4-a716-446655440000');
INSERT INTO `table` (`id`) VALUES (@uuid);
-- 查詢時(shí)轉(zhuǎn)換為字符串
SELECT BIN_TO_UUID(`id`) FROM `table`;
方案 3:引入分布式 ID 生成器(如百度 UidGenerator、美團(tuán) Leaf)
- 核心邏輯:
- 基于數(shù)據(jù)庫號段模式(預(yù)先分配 ID 段)
- 結(jié)合雪花算法生成趨勢遞增 ID
- 優(yōu)勢:兼顧有序性與分布式擴(kuò)展
五、不得不使用 UUID/Snowflake 的應(yīng)對策略
1. 優(yōu)化存儲格式
- 改用二進(jìn)制存儲:將 UUID 轉(zhuǎn)換為 16 字節(jié)的二進(jìn)制數(shù)據(jù)(BINARY(16))
-- 插入時(shí)轉(zhuǎn)換 INSERT INTO `table` (`uuid_bin`) VALUES (UUID_TO_BIN(UUID())); -- 查詢時(shí)轉(zhuǎn)換 SELECT BIN_TO_UUID(`uuid_bin`) FROM `table`;
- 空間節(jié)省:存儲長度從 36 字節(jié)降至 16 字節(jié),提升索引效率
2. 強(qiáng)制順序化(僅適用于雪花 ID)
- 按時(shí)間戳排序:在應(yīng)用層對 ID 進(jìn)行排序后再插入
- 缺點(diǎn):高并發(fā)下可能導(dǎo)致插入阻塞
3. 非聚簇索引表(犧牲一致性換性能)
CREATE TABLE `non_clustered_table` ( `uuid` CHAR(36) PRIMARY KEY, `data` TEXT, INDEX `idx_data` (`data`) ) ENGINE=InnoDB DISABLE_KEY_CACHE=1;
- 原理:關(guān)閉聚簇索引,強(qiáng)制使用二級索引(不推薦,違背 InnoDB 設(shè)計(jì)初衷)
六、性能對比實(shí)測(InnoDB 引擎,100 萬條數(shù)據(jù))
| 主鍵類型 | 插入速度(條 / 秒) | 主鍵索引大小 | 范圍查詢耗時(shí)(SELECT * FROM t WHERE id < 10000) |
|---|---|---|---|
| 自增 ID(BIGINT) | 12,345 | 8.2MB | 12ms |
| 雪花 ID(BIGINT) | 10,890 | 8.2MB | 15ms |
| UUID(CHAR(36)) | 6,543 | 38.5MB | 28ms |
測試環(huán)境:MySQL 8.0,4 核 8GB,SSD 磁盤
七、總結(jié):主鍵選擇的核心原則
- 優(yōu)先遵循聚簇索引優(yōu)化:利用自增 ID 的順序性提升寫入性能
- 空間優(yōu)先原則:避免使用長字符串作為主鍵(如 UUID)
- 分布式場景權(quán)衡:
- 輕量級場景:自增 ID + 分庫分表
- 強(qiáng)一致性場景:雪花 ID(犧牲部分性能)
- 避免過度設(shè)計(jì):單機(jī)場景無需引入分布式 ID,自增主鍵已足夠高效
MySQL 的主鍵設(shè)計(jì)本質(zhì)上是在數(shù)據(jù)唯一性、查詢性能、存儲效率之間的權(quán)衡。除非有明確的分布式唯一性需求,否則應(yīng)優(yōu)先選擇自增整數(shù)作為主鍵。對于必須使用雪花 ID 或 UUID 的場景,需通過二進(jìn)制存儲、順序化插入等手段盡可能減少性能損耗。
到此這篇關(guān)于MySQL不推薦使用雪花和UUID做主鍵的文章就介紹到這了,更多相關(guān)MySQL不推薦使用雪花和UUID做主鍵內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
生產(chǎn)庫自動化MySQL5.6安裝部署詳細(xì)教程
自動化運(yùn)維是一個(gè)DBA應(yīng)該掌握的技術(shù),其中,自動化安裝數(shù)據(jù)庫是一項(xiàng)基本的技能,這篇文章主要介紹了生產(chǎn)庫自動化MySQL5.6安裝部署詳細(xì)教程,需要的朋友可以參考下2016-09-09
textarea標(biāo)簽(存取數(shù)據(jù)庫mysql)的換行方法
textarea標(biāo)簽本身不識別換行功能,回車換行用的是\n換行符,輸入時(shí)的確有換行的效果,但是html渲染或者保存數(shù)據(jù)庫mysql時(shí)就只是一個(gè)空格了,這時(shí)就需要利用換行符\n和br標(biāo)簽的轉(zhuǎn)換進(jìn)行處理2023-09-09
在linux服務(wù)器上配置mysql并開放3306端口的操作步驟
這篇文章主要介紹了在linux服務(wù)器上配置mysql并開放3306端口,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
MySQL空間函數(shù)ST_Distance_Sphere()的使用方式
這篇文章主要介紹了MySQL空間函數(shù)ST_Distance_Sphere()的使用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
mysql5.5 master-slave(Replication)主從配置
在主機(jī)master中對test數(shù)據(jù)庫進(jìn)行sql操作,再查看從機(jī)test數(shù)據(jù)庫是否產(chǎn)生同步。2011-07-07
玩轉(zhuǎn)?MySQL?庫表:庫和表的操作"通關(guān)指南"
文章主要介紹了MySQL數(shù)據(jù)庫的基本概念、結(jié)構(gòu)和操作方法,包括數(shù)據(jù)庫和MySQL服務(wù)端的區(qū)別,SQL語句分類,以及數(shù)據(jù)庫和表的創(chuàng)建、查看、修改和刪除等操作,最后還介紹了備份和恢復(fù)數(shù)據(jù)庫的方法2026-04-04

