當你在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_timeGROUP 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);

避坑點:

  1. 不要用UNIQUE替代主鍵:主鍵是“唯一且非空”的,UNIQUE允許NULL;
  2. 多列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);

避坑點:

  1. 不要用FULLTEXT替代NORMAL:它只適合大文本,對短字符串(如手機號)完全沒用;
  2. 默認忽略“停止詞”:比如“的、了、a、the”這些高頻詞會被忽略,需要自定義停止詞表;
  3. 最小長度限制:默認情況下,長度<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);

避坑點:

  1. 必須用空間函數查詢:直接用WHERE location = ...不會走SPATIAL索引;
  2. 數據類型要匹配:比如POINT存儲經緯度,不能存成普通的VARCHAR。

六、終極選型指南:四選一,到底怎麼選?

用一張表總結選型決策邏輯

需求場景 優先選的索引類型
只是想加速普通查詢(WHERE/ORDER BY) NORMAL
需要保證列值唯一(如手機號、訂單號) UNIQUE
需要搜索大文本內容(如文章、商品描述) FULLTEXT
需要處理地理空間數據(如經緯度、區域) SPATIAL

索引是“工具”,不是“越多越好”

很多開發者的誤區是“給所有列建索引”——但索引是有成本的:

  • 寫操作(INSERT/UPDATE/DELETE)會變慢(需要維護索引);
  • 佔用額外的磁盤空間。

正確的做法是:基於業務查詢場景,只給“高頻查詢、高區分度”的列建合適的索引

比如:

  • 用户表的phone列:建UNIQUE(既加速查詢,又防重複);
  • 文章表的content列:建FULLTEXT(優化文本搜索);
  • 商家表的location列:建SPATIAL(支持附近商家查詢);
  • 訂單表的order_time列:建NORMAL(優化按時間排序)。