MySQL唯一索引和主鍵索引的區(qū)別有哪些(面試官常問(wèn))
面試考察點(diǎn)
- 索引類型理解:面試官不僅僅是想知道 "有什么區(qū)別",更是想考察你是否理解主鍵索引(聚簇索引)和唯一索引(二級(jí)索引)在存儲(chǔ)結(jié)構(gòu)上的根本差異。
- NULL 值處理:考察你是否清楚主鍵不允許 NULL,而唯一索引可以(但只能有一個(gè)),這是很多面試者容易忽略的細(xì)節(jié)。
- 性能優(yōu)化意識(shí):理解聚簇索引和非聚簇索引的查詢效率差異,能否在設(shè)計(jì)表結(jié)構(gòu)時(shí)做出正確選擇。
核心答案
主鍵索引:一種特殊的唯一索引,每張表只能有一個(gè),用于唯一標(biāo)識(shí)每一行記錄,InnoDB 中主鍵是 聚簇索引。
唯一索引:保證索引列的值唯一,一張表可以有多個(gè)唯一索引,InnoDB 中唯一索引是 二級(jí)索引(非聚簇索引) 。
核心區(qū)別對(duì)比:
| 對(duì)比維度 | 主鍵索引(PRIMARY) | 唯一索引(UNIQUE) |
|---|---|---|
| 數(shù)量限制 | 每表 只能有 1 個(gè) | 每表 可以有多個(gè) |
| NULL 值 | 不允許 NULL | 允許 NULL(但最多 1 個(gè)) |
| 存儲(chǔ)結(jié)構(gòu) | 聚簇索引,葉子節(jié)點(diǎn)存完整數(shù)據(jù) | 二級(jí)索引,葉子節(jié)點(diǎn)存主鍵值 |
| 索引回表 | 不需要回表 | 需要回表(查詢非索引列時(shí)) |
| 創(chuàng)建語(yǔ)法 | PRIMARY KEY (col) | UNIQUE KEY uk_name (col) |
| 是否必須 | 建議有,但不強(qiáng)制 | 按需創(chuàng)建 |
一句話總結(jié):主鍵是 唯一的聚簇索引,不允許 NULL;唯一索引是 可以有多個(gè)的非聚簇索引,允許 NULL。
深度解析
一、存儲(chǔ)結(jié)構(gòu)差異:聚簇索引 vs 二級(jí)索引
主鍵索引和唯一索引在 InnoDB 中的存儲(chǔ)結(jié)構(gòu)完全不同:

上圖對(duì)比了主鍵索引和唯一索引的存儲(chǔ)結(jié)構(gòu)。關(guān)鍵區(qū)別在于:
- 主鍵索引(聚簇索引) :葉子節(jié)點(diǎn)直接存儲(chǔ) 完整的行數(shù)據(jù),通過(guò)主鍵查詢可以直接獲取所有字段,無(wú)需回表
- 唯一索引(二級(jí)索引) :葉子節(jié)點(diǎn)只存儲(chǔ) 索引列的值 + 主鍵值,如果查詢其他字段,需要拿著主鍵值回表查詢聚簇索引
二、NULL 值處理差異
這是面試中的高頻考點(diǎn):
-- 創(chuàng)建測(cè)試表
CREATETABLEuser (
idBIGINT PRIMARY KEY, -- 主鍵,不允許 NULL
email VARCHAR(50) UNIQUE, -- 唯一索引,允許 NULL
phone VARCHAR(20) UNIQUE -- 唯一索引,允許 NULL
);
-- 主鍵測(cè)試:插入 NULL 會(huì)報(bào)錯(cuò)
INSERTINTOuser (id, email) VALUES (NULL, 'test@qq.com');
-- ? ERROR 1048: Column 'id' cannot be null
-- 唯一索引測(cè)試:允許 NULL,且 MySQL 中可以插入多個(gè) NULL
INSERTINTOuser (id, email) VALUES (1, NULL); -- ? 成功
INSERTINTOuser (id, email) VALUES (2, NULL); -- ? 成功(MySQL 認(rèn)為多個(gè) NULL 不重復(fù))
INSERTINTOuser (id, email) VALUES (3, 'a@qq.com'); -- ? 成功
INSERTINTOuser (id, email) VALUES (4, 'a@qq.com'); -- ? Duplicate entry
關(guān)鍵結(jié)論:
- 主鍵:絕對(duì)不允許 NULL,這是主鍵的基本約束
- 唯一索引:允許 NULL,且 MySQL 中可以插入多個(gè) NULL 值(因?yàn)?nbsp;
NULL != NULL)
三、查詢性能差異:回表問(wèn)題
通過(guò)主鍵查詢 vs 通過(guò)唯一索引查詢的性能差異:
-- 表結(jié)構(gòu)
CREATETABLEuser (
idBIGINT PRIMARY KEY,
email VARCHAR(50) UNIQUE,
nameVARCHAR(50),
age INT
);
-- 場(chǎng)景 1:通過(guò)主鍵查詢
SELECT * FROMuserWHEREid = 1;
-- ? 直接走聚簇索引,一次查詢即可獲取完整數(shù)據(jù)
-- 場(chǎng)景 2:通過(guò)唯一索引查詢所有字段
SELECT * FROMuserWHERE email = 'test@qq.com';
-- ?? 需要回表:先查唯一索引得到 id,再回表查聚簇索引
-- 場(chǎng)景 3:通過(guò)唯一索引查詢索引列(覆蓋索引)
SELECT email FROMuserWHERE email = 'test@qq.com';
-- ? 覆蓋索引,不需要回表
四、使用場(chǎng)景建議
| 場(chǎng)景 | 推薦索引類型 | 原因 |
|---|---|---|
| 標(biāo)識(shí)每一行記錄 | 主鍵索引 | 每表必須有唯一標(biāo)識(shí),推薦自增 BIGINT |
| 用戶郵箱不能重復(fù) | 唯一索引 | 業(yè)務(wù)唯一性約束,允許未設(shè)置郵箱(NULL) |
| 手機(jī)號(hào)唯一 | 唯一索引 | 允許用戶暫未綁定手機(jī)號(hào) |
| 身份證號(hào)唯一 | 唯一索引 | 允許 NULL(可能未錄入) |
| 聯(lián)合唯一(用戶 + 日期) | 聯(lián)合唯一索引 | UNIQUE KEY uk_user_date (user_id, date) |
最佳實(shí)踐:
-- 推薦:使用 BIGINT 自增主鍵
CREATETABLE orders (
idBIGINTUNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(32) UNIQUENOTNULL, -- 業(yè)務(wù)訂單號(hào),不允許 NULL
user_id BIGINTNOTNULL,
INDEX idx_user (user_id)
);
-- 不推薦:沒(méi)有主鍵的表
-- MySQL 會(huì)自動(dòng)選擇一個(gè)非空唯一索引作為聚簇索引
-- 如果沒(méi)有合適的,會(huì)生成一個(gè)隱藏的 6 字節(jié)主鍵五、如果沒(méi)有主鍵會(huì)怎樣?
InnoDB 要求每張表必須有聚簇索引:

面試高頻追問(wèn)
- 追問(wèn)一:為什么推薦使用自增主鍵而不是 UUID?
- 答:自增主鍵是順序插入的,B+ 樹(shù)葉子節(jié)點(diǎn)順序追加,不會(huì)產(chǎn)生頁(yè)分裂;UUID 是無(wú)序的,插入會(huì)導(dǎo)致頻繁頁(yè)分裂,影響性能。此外,UUID 占用空間大(36 字符 vs 8 字節(jié)),降低索引效率。
- 追問(wèn)二:一張表可以沒(méi)有主鍵嗎?
- 答:可以,但 InnoDB 會(huì)自動(dòng)選擇一個(gè)非空唯一索引作為聚簇索引;如果沒(méi)有合適的,會(huì)生成隱藏的 6 字節(jié)
ROW_ID。但強(qiáng)烈不建議這樣做,應(yīng)該顯式定義主鍵。
- 答:可以,但 InnoDB 會(huì)自動(dòng)選擇一個(gè)非空唯一索引作為聚簇索引;如果沒(méi)有合適的,會(huì)生成隱藏的 6 字節(jié)
- 追問(wèn)三:聯(lián)合主鍵和聯(lián)合唯一索引有什么區(qū)別?
- 答:本質(zhì)區(qū)別和單列一樣——聯(lián)合主鍵是聚簇索引,不允許任何列為 NULL;聯(lián)合唯一索引是二級(jí)索引,允許列為 NULL。
常見(jiàn)面試變體
- "主鍵索引和唯一索引有什么區(qū)別?"
- "聚簇索引和非聚簇索引的區(qū)別是什么?"
- "MySQL 查詢走唯一索引時(shí)為什么可能需要回表?"
- "唯一索引允許 NULL 嗎?主鍵呢?"
記憶口訣
主鍵 vs 唯一索引:
- 主鍵聚簇:葉子存完整數(shù)據(jù),查詢不回表
- 唯一二級(jí):葉子存主鍵值,查詢需回表
- NULL 有別:主鍵不允許,唯一索引可以
- 數(shù)量不同:主鍵唯一一個(gè),唯一索引多個(gè)
總結(jié)
主鍵索引是 聚簇索引,每表只能有一個(gè),不允許 NULL,葉子節(jié)點(diǎn)存儲(chǔ)完整行數(shù)據(jù);唯一索引是 二級(jí)索引,可以有多個(gè),允許 NULL(可多個(gè)),葉子節(jié)點(diǎn)存儲(chǔ)主鍵值。查詢時(shí)主鍵直接獲取數(shù)據(jù),唯一索引需要回表。推薦使用 BIGINT 自增主鍵,業(yè)務(wù)唯一約束用唯一索引。
到此這篇關(guān)于MySQL唯一索引和主鍵索引的區(qū)別有哪些的文章就介紹到這了,更多相關(guān)MySQL唯一索引和主鍵索引區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL數(shù)據(jù)庫(kù)觸發(fā)器從小白到精通
觸發(fā)器是SQLserver提供給程序員和數(shù)據(jù)分析員來(lái)保證數(shù)據(jù)完整性的一種方法,它是與表事件相關(guān)的特殊的存儲(chǔ)過(guò)程,它的執(zhí)行不是由程序調(diào)用,也不是手工啟動(dòng),而是由事件來(lái)觸發(fā),比如當(dāng)對(duì)一個(gè)表進(jìn)行操作時(shí)就會(huì)激活它執(zhí)行。觸發(fā)器經(jīng)常用于加強(qiáng)數(shù)據(jù)的完整性約束和業(yè)務(wù)規(guī)則等2022-03-03
解決mysql ERROR 1017:Can''t find file: ''/xxx.frm'' 錯(cuò)誤
如果重啟服務(wù)器前沒(méi)有關(guān)閉mysql,MySql的MyiSAM表很有可能會(huì)出現(xiàn) ERROR #1017 :Can't find file: '/xxx.frm' 的錯(cuò)誤2011-08-08
一文詳解MySQL在Docker中的部署與數(shù)據(jù)持久化指南
這篇文章主要為大家詳細(xì)介紹了在Docker中運(yùn)行MySQL時(shí)確保數(shù)據(jù)持久化的關(guān)鍵方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2026-02-02
mysql?中的備份恢復(fù),分區(qū)分表,主從復(fù)制,讀寫分離
這篇文章主要介紹了mysql?中的備份恢復(fù),分區(qū)分表,主從復(fù)制,讀寫分離,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容戒殺,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09

