當你在MySQL裏敲下CREATE INDEX時,有沒有想過:同樣是“索引”,為什麼有的能防重複,有的能搜文章,有的還能找附近的咖啡店?
MySQL的索引體系裏,NORMAL、UNIQUE、FULLTEXT、SPATIAL這四個“頂流”,看似都是“加速查詢”的工具,實則是完全不同的“專項武器”。選錯索引,可能讓你的查詢從“毫秒級”變成“分鐘級”,甚至埋下數據混亂的坑——今天就來拆解這四類索引的底層邏輯、適用場景和避坑指南。
一、先搞懂:索引的本質是“數據的快捷方式”
在聊具體類型前,先統一認知:**索引不是“銀彈”,而是“為特定場景設計的查詢優化結構”**。
想象你是圖書館管理員:
- 想快速找到“書名包含‘MySQL’”的書 → 需要“關鍵詞索引”(對應FULLTEXT);
- 想確保“每本書的ISBN不重複” → 需要“唯一標識索引”(對應UNIQUE);
- 想快速找到“分類號是TP311”的書 → 需要“普通分類索引”(對應NORMAL);
- 想快速找到“位於3樓東側區域”的書 → 需要“空間區域索引”(對應SPATIAL)。
MySQL的四類索引,就是為了滿足這些不同的“查找需求”而生——它們的底層數據結構可能都是B+樹(除了FULLTEXT的倒排索引),但功能約束、適用場景天差地別。
二、NORMAL:最“普通”,卻是使用率最高的“基礎款”
什麼是NORMAL索引?
NORMAL是MySQL中默認的索引類型(如果不指定INDEX TYPE,創建的就是NORMAL索引),它的核心作用只有一個:加速查詢。
它像圖書館的“分類號索引卡”——你告訴管理員“我要TP311類的書”,管理員不用翻遍所有書架,直接從分類索引裏找到對應的書架位置。
核心特性:
- 無約束:索引列允許重複值、允許NULL值(甚至可以全是NULL);
- 適用所有數據類型:數值(INT)、字符串(VARCHAR)、日期(DATETIME)都能建;
- 底層結構:默認用B+樹(InnoDB引擎),支持範圍查詢、排序優化。
典型場景:
- 高頻出現在
WHERE條件裏的列:比如用户表的age、訂單表的order_time; - 需要排序/分組的列:比如
ORDER BY create_time、GROUP BY category_id。
創建示例:
-- 給用户表的age列建普通索引
CREATE INDEX idx_user_age ON user(age);
-- 給訂單表的order_time列建普通索引(優化排序)
CREATE INDEX idx_order_time ON order_info(order_time);
避坑點:
不要給“低區分度”的列建NORMAL索引!比如“性別”列(只有男/女/未知三個值),用索引查詢的成本可能比全表掃描還高——MySQL優化器會直接忽略這樣的索引。
三、UNIQUE:“帶約束的NORMAL”,既加速又防重複
什麼是UNIQUE索引?
UNIQUE索引是在NORMAL索引的基礎上,增加了“唯一性約束”——它不僅能加速查詢,還能保證索引列的所有值都是“唯一不重複”的(允許一個NULL值,因為NULL不與任何值相等)。
它像圖書館的“ISBN索引卡”——每本書的ISBN唯一,既可以通過ISBN快速找到書,也能避免圖書館出現兩本ISBN相同的書。
核心特性:
- 唯一性約束:插入/更新數據時,若索引列值重複,會直接報錯(
Duplicate entry); - 功能疊加:擁有NORMAL索引的所有查詢優化能力;
- 允許一個NULL:比如“用户身份證號”列,未填寫的用户可以存NULL,但不能有兩個相同的身份證號。
典型場景:
- 需要唯一標識的列:比如用户表的
phone(手機號)、email(郵箱); - 業務唯一鍵:比如訂單表的
order_sn(訂單編號)。
創建示例:
-- 給用户表的phone列建唯一索引(保證手機號不重複)
CREATE UNIQUE INDEX idx_user_phone ON user(phone);
-- 給訂單表的order_sn列建唯一索引(保證訂單編號唯一)
CREATE UNIQUE INDEX idx_order_sn ON order_info(order_sn);
避坑點:
- 不要用UNIQUE替代主鍵:主鍵是“唯一且非空”的,UNIQUE允許NULL;
- 多列UNIQUE的“坑”:比如
(a,b)的UNIQUE索引,允許(1,NULL)和(1,NULL)同時存在(因為NULL不相等)。
四、FULLTEXT:文本搜索的“專屬武器”,秒殺LIKE %關鍵詞%
什麼是FULLTEXT索引?
FULLTEXT是專門為“大文本內容搜索”設計的索引——它基於“倒排索引”實現,可以快速匹配文本中的關鍵詞,而不是像LIKE '%xxx%'那樣全表掃描。
它像圖書館的“關鍵詞索引本”——你輸入“數據庫優化”,就能直接找到所有包含這些關鍵詞的書籍,而不用一本本翻書的內容。
核心特性:
- 僅支持文本類型:只能給CHAR、VARCHAR、TEXT列建;
- 關鍵詞搜索:通過
MATCH() AGAINST()語法查詢,支持“布爾模式”“自然語言模式”; - 不支持所有引擎:InnoDB(MySQL 5.6+)和MyISAM支持,其他引擎(如Memory)不支持。
典型場景:
- 文章內容表的
content列; - 商品表的
description(商品描述)列; - 評論表的
comment_content列。
創建&查詢示例:
-- 給文章表的content列建全文索引
CREATE FULLTEXT INDEX idx_article_content ON article(content);
-- 自然語言模式:搜索包含“MySQL索引”的文章
SELECT * FROM article
WHERE MATCH(content) AGAINST('MySQL索引');
-- 布爾模式:搜索包含“MySQL”但不包含“Oracle”的文章
SELECT * FROM article
WHERE MATCH(content) AGAINST('+MySQL -Oracle' IN BOOLEAN MODE);
避坑點:
- 不要用FULLTEXT替代NORMAL:它只適合大文本,對短字符串(如手機號)完全沒用;
- 默認忽略“停止詞”:比如“的、了、a、the”這些高頻詞會被忽略,需要自定義停止詞表;
- 最小長度限制:默認情況下,長度<4的詞(如“SQL”)不會被索引,需要修改
ft_min_word_len參數。
五、SPATIAL:地理空間的“定位神器”,找附近的店就靠它
什麼是SPATIAL索引?
SPATIAL是專門為“地理空間數據”設計的索引——它基於R樹(一種空間數據結構)實現,可以快速查詢“點在區域內”“兩個區域是否相交”等空間關係。
它像地圖APP的“位置索引”——你點擊“附近500米的咖啡店”,APP能快速篩選出這個區域內的商家,而不用遍歷所有商家的經緯度。
核心特性:
- 僅支持空間數據類型:只能給GEOMETRY、POINT、LINESTRING、POLYGON等空間類型列建;
- 空間關係查詢:通過
ST_Contains()、ST_Distance()等函數實現位置查詢; - 引擎限制:InnoDB(MySQL 5.7+)和MyISAM支持。
典型場景:
- 商家表的
location(經緯度,用POINT類型存儲); - 城市區域表的
area(行政區域,用POLYGON類型存儲)。
創建&查詢示例:
-- 1. 先創建帶空間類型的表
CREATE TABLE shop (
id INT PRIMARY KEY,
name VARCHAR(50),
location POINT NOT NULL, -- 經緯度:POINT(經度 緯度)
SPATIAL INDEX idx_shop_location(location) -- 建空間索引
);
-- 2. 插入商家數據(比如:經度116.4,緯度39.9的咖啡店)
INSERT INTO shop VALUES (1, '星巴克', ST_GeomFromText('POINT(116.4 39.9)'));
-- 3. 查詢“位於北京市朝陽區內”的商家(假設朝陽區的區域是polygon)
SELECT s.name
FROM shop s, area a
WHERE a.name = '朝陽區'
AND ST_Contains(a.polygon, s.location);
避坑點:
- 必須用空間函數查詢:直接用
WHERE location = ...不會走SPATIAL索引; - 數據類型要匹配:比如POINT存儲經緯度,不能存成普通的VARCHAR。
六、終極選型指南:四選一,到底怎麼選?
用一張表總結選型決策邏輯:
| 需求場景 | 優先選的索引類型 |
|---|---|
| 只是想加速普通查詢(WHERE/ORDER BY) | NORMAL |
| 需要保證列值唯一(如手機號、訂單號) | UNIQUE |
| 需要搜索大文本內容(如文章、商品描述) | FULLTEXT |
| 需要處理地理空間數據(如經緯度、區域) | SPATIAL |
索引是“工具”,不是“越多越好”
很多開發者的誤區是“給所有列建索引”——但索引是有成本的:
- 寫操作(INSERT/UPDATE/DELETE)會變慢(需要維護索引);
- 佔用額外的磁盤空間。
正確的做法是:基於業務查詢場景,只給“高頻查詢、高區分度”的列建合適的索引。
比如:
- 用户表的
phone列:建UNIQUE(既加速查詢,又防重複); - 文章表的
content列:建FULLTEXT(優化文本搜索); - 商家表的
location列:建SPATIAL(支持附近商家查詢); - 訂單表的
order_time列:建NORMAL(優化按時間排序)。