MySQL 主鍵不推薦使用 UUID 的深層原因及解決方案
1.存儲(chǔ)空間問(wèn)題
存儲(chǔ)大小對(duì)比
| 主鍵類型 | 存儲(chǔ)大小 | 示例值 |
|---|---|---|
| BIGINT(自增) | 8字節(jié) | 1, 2, 3... |
| INT(自增) | 4字節(jié) | 1, 2, 3... |
| UUID(字符串) | 36字符(288位) | uuid-xxxx-xxxx-xxxx |
| UUID(二進(jìn)制) | 16字節(jié) | 二進(jìn)制格式 |
-- UUID 的兩種存儲(chǔ)方式
CREATE TABLE users_uuid_str (
id CHAR(36) PRIMARY KEY DEFAULT UUID(), -- 36字節(jié)
name VARCHAR(50)
);
CREATE TABLE users_uuid_bin (
id BINARY(16) PRIMARY KEY, -- 16字節(jié),但仍然有其他問(wèn)題
name VARCHAR(50)
);2.索引性能問(wèn)題(最核心問(wèn)題)
InnoDB 聚簇索引特性
-- InnoDB 表結(jié)構(gòu)示例 -- 數(shù)據(jù)實(shí)際按主鍵順序存儲(chǔ)在磁盤上 -- 自增ID:數(shù)據(jù)物理存儲(chǔ)是連續(xù)的 -- UUID:數(shù)據(jù)物理存儲(chǔ)是隨機(jī)的
性能影響對(duì)比
-- 場(chǎng)景:插入100萬(wàn)條數(shù)據(jù)
-- 使用自增ID
INSERT INTO table (name) VALUES ('name'); -- 直接追加到B+樹末尾
-- 使用UUID
INSERT INTO table (id, name) VALUES (UUID(), 'name');
-- 需要:1. 在B+樹中尋找插入位置 2. 可能導(dǎo)致頁(yè)分裂 3. 碎片化3.頁(yè)分裂與碎片化
頁(yè)分裂過(guò)程
原始頁(yè)(已滿):[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 新插入U(xiǎn)UID:需要插入到 5 和 6 之間 結(jié)果: 頁(yè)1:[1, 2, 3, 4, 5] 頁(yè)2:[uuid_value, 6, 7, 8, 9, 10] 問(wèn)題: 1. 數(shù)據(jù)不再連續(xù) 2. 磁盤空間利用率下降 3. 查詢需要更多磁盤I/O
4.緩存效率問(wèn)題
InnoDB Buffer Pool 工作原理
-- 自增ID:連續(xù)的數(shù)據(jù)更容易一起被緩存 -- 讀取用戶1-100的數(shù)據(jù)可能只需要1-2次磁盤I/O -- UUID:數(shù)據(jù)分散在不同頁(yè)中 -- 讀取100個(gè)用戶數(shù)據(jù)可能需要100次磁盤I/O
5.具體性能測(cè)試對(duì)比
測(cè)試數(shù)據(jù)
-- 創(chuàng)建測(cè)試表
CREATE TABLE test_autoinc (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
data VARCHAR(100)
) ENGINE=InnoDB;
CREATE TABLE test_uuid (
id CHAR(36) PRIMARY KEY DEFAULT UUID(),
data VARCHAR(100)
) ENGINE=InnoDB;
-- 插入性能對(duì)比(100萬(wàn)行)
-- 自增ID:約 30-40秒
-- UUID:約 90-120秒(慢2-3倍)
-- 查詢性能對(duì)比(范圍查詢)
SELECT * FROM test_autoinc WHERE id BETWEEN 100000 AND 200000;
-- 使用聚簇索引,高效
SELECT * FROM test_uuid WHERE id > 'xxxx';
-- 索引效率低,需要更多隨機(jī)I/O6.實(shí)際場(chǎng)景分析
適合使用UUID的場(chǎng)景
-- 分布式系統(tǒng),需要離線生成ID -- 數(shù)據(jù)需要合并的場(chǎng)景 -- 安全要求高,不希望暴露數(shù)據(jù)規(guī)模 -- 示例:移動(dòng)設(shè)備離線數(shù)據(jù)同步
不適合使用UUID的場(chǎng)景
-- 高并發(fā)寫入的OLTP系統(tǒng) -- 需要頻繁范圍查詢的業(yè)務(wù) -- 數(shù)據(jù)量大的表(>1000萬(wàn)行) -- 示例:電商訂單、用戶表、日志表
7.優(yōu)化方案
方案1:組合使用
-- 使用自增ID作為主鍵,UUID作為業(yè)務(wù)ID
CREATE TABLE users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, -- 用于索引和關(guān)聯(lián)
uuid CHAR(36) UNIQUE NOT NULL DEFAULT UUID(), -- 對(duì)外暴露
name VARCHAR(50),
INDEX idx_uuid(uuid)
);方案2:有序UUID
-- 使用時(shí)間有序的UUID變體
-- MySQL 8.0+ 的 UUID_TO_BIN 函數(shù)
CREATE TABLE users (
id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), 1)), -- 有序
name VARCHAR(50)
);
-- 參數(shù)1:將時(shí)間部分移到前面,提高順序性方案3:雪花算法(Snowflake)
# 分布式ID生成算法(64位) # 結(jié)構(gòu):時(shí)間戳(41位) + 機(jī)器ID(10位) + 序列號(hào)(12位) # 優(yōu)點(diǎn):有序、分布式、高性能
8.MySQL 8.0 的改進(jìn)
-- 生成有序UUID SELECT UUID_TO_BIN(UUID(), 1); -- 有序 SELECT UUID_TO_BIN(UUID(), 0); -- 無(wú)序 -- 反向轉(zhuǎn)換 SELECT BIN_TO_UUID(binary_uuid, 1);
9.監(jiān)控指標(biāo)
-- 查看碎片化程度
SELECT
table_name,
data_length,
index_length,
data_free,
ROUND(data_free/(data_length+index_length)*100, 2) as frag_percent
FROM information_schema.tables
WHERE table_schema = DATABASE();
-- 監(jiān)控插入性能
SHOW ENGINE INNODB STATUS;10.決策指南
何時(shí)可以使用UUID?
- ? 數(shù)據(jù)量?。?lt;100萬(wàn)行)
- ? 插入頻率低
- ? 分布式系統(tǒng)必須使用
- ? 數(shù)據(jù)合并需求
- ? 安全要求高
應(yīng)該避免使用UUID?
- ? 高并發(fā)寫入系統(tǒng)
- ? 大數(shù)據(jù)量表
- ? 頻繁范圍查詢
- ? 性能敏感系統(tǒng)
- ? 磁盤空間有限
總結(jié)
在大多數(shù)OLTP場(chǎng)景中,自增整數(shù)主鍵是最優(yōu)選擇。UUID主要問(wèn)題是破壞InnoDB聚簇索引的順序性,導(dǎo)致頁(yè)分裂、碎片化、緩存效率低下等問(wèn)題。如果必須使用UUID,應(yīng)優(yōu)先考慮有序UUID或組合方案,并監(jiān)控性能影響。
到此這篇關(guān)于MySQL 主鍵不推薦使用 UUID 的深層原因的文章就介紹到這了,更多相關(guān)mysql主鍵不推薦使用uuid內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解MySQL查看執(zhí)行慢的SQL語(yǔ)句(慢查詢)
查看執(zhí)行慢的SQL語(yǔ)句,需要先開啟慢查詢?nèi)罩?,MySQL的慢查詢?nèi)罩?,記錄在MySQL中響應(yīng)時(shí)間超過(guò)閥值的語(yǔ)句(具體指運(yùn)行時(shí)間超過(guò)long_query_time值的SQL,本文給大家介紹MySQL查看執(zhí)行慢的SQL語(yǔ)句,感興趣的朋友跟隨小編一起看看吧2024-03-03
mysql獲取group by的總記錄行數(shù)另類方法
mysql獲取group by內(nèi)部可以獲取到某字段的記錄分組統(tǒng)計(jì)總數(shù),而無(wú)法統(tǒng)計(jì)出分組的記錄數(shù),下面有個(gè)可行的方法,大家可以看看2014-10-10
MySQL數(shù)據(jù)庫(kù)遷移后無(wú)法啟動(dòng)的問(wèn)題解決
本文主要介紹了MySQL數(shù)據(jù)庫(kù)遷移后無(wú)法啟動(dòng)的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2025-06-06
MySQL查看主從狀態(tài)的命令實(shí)現(xiàn)
本文主要介紹了MySQL查看主從狀態(tài)的命令實(shí)現(xiàn),我們可以使用SHOW SLAVE STATUS命令來(lái)查看主從狀態(tài),本文就來(lái)詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下2023-10-10
Windows系統(tǒng)下MySQL忘記root密碼的2種解決辦法
這篇文章主要介紹了Windows系統(tǒng)下MySQL忘記root密碼的2種解決辦法,一種是通過(guò)啟動(dòng)MySQL時(shí)跳過(guò)權(quán)限表驗(yàn)證,然后重置密碼,另一種是創(chuàng)建一個(gè)包含新密碼的文本文件,并通過(guò)MySQL的--init-file選項(xiàng)來(lái)應(yīng)用該文件中的密碼設(shè)置,需要的朋友可以參考下2024-11-11
MySQL數(shù)據(jù)庫(kù)防止人為誤操作的實(shí)例講解
這篇文章主要介紹了MySQL數(shù)據(jù)庫(kù)防止人為誤操作的方法,需要的朋友可以參考下2014-06-06

