有同學問到InnoDB的索引長度問題,簡單說幾個tips。
MySQL的每個單表中所創建的索引長度是有限制的,且對不同存儲引擎下的表有不同的限制。
- myisam表,單列索引,最大長度不能超過 1000 bytes,否則會報警,但是創建成功,最終創建的是前綴索引(取前333個字符)。
- myisam表,組合索引,索引長度和不能超過 1000 bytes,否則會報錯,創建失敗;
- innodb表,單列索引,超過 767 bytes的,給出warning,最終索引創建成功,取前綴索引(取前 255 字符)。
- innodb表,組合索引,各列長度不超過 767 bytes ,如果有超過 767 bytes 的,則給出報警,索引最后創建成功,但是對於超過 767 字節的列取前綴索引,與索引列順序無關,總和不得超過 3072 ,否則失敗,無法創建。
測試:
作者只對mysql innodb 引擎,utf8字符集定義的表做了實際的測試,myisam留給讀者
版本:


新建測試表:



組合索引中有大於 767 bytes的字段,產生告警


結合上邊兩個測試結果,可以看到組合索引長度之和大於 767 bytes並無影響,當有某個字段定義長度大於 767 bytes(1000*3)時,僅產生告警,但不影響創建,超長字段會取前 255 字符作為前綴索引,並且組合索引中字段出現的順序並無關系。


可以看到,由於每個字段占用255*3, 因此這個索引的大小是3825>3072,報錯。
為什么3072
InnoDB一個page的默認大小是 16 k。由於是Btree組織,要求葉子節點上一個page至少包含兩條記錄(否則就退化鏈表了)。所以一個記錄最多不能超過 8 k。又由於InnoDB的聚簇索引結構,一個二級索引要包含主鍵索引,因此每個單個索引不能超過 4 k(極端情況,pk和某個二級索引都達到這個限制)。由於需要預留和輔助空間,扣掉后不能超過 3500 ,取個“整數”就是(1024*3)。
單列索引限制
默認情況下,InnoDB 引擎單一字段索引的長度最大為 767 字節,同樣的,前綴索引也有同樣的限制。當使用 UTF-8 字符集,每一個字符使用 3 字節來存儲,767=256*3-1,在 TEXT 或者 VARCHAR 類型的字段上建立一個超過 255 字符數的前綴索引時就會遇到問題。至於為什么字符長度限制在 256 內,我猜是為提高索引效率,應為varchar類型需要額外的字節保留其長度信息,256 就將其限定在一個字節了。但是在5.5以后,開始支持4個字節的uutf8。255×4>767, 於是增加了一個參數叫做innodblargeprefix。這個參數默認值是OFF,當改為ON時,允許列索引最大達到 3072 字節。要求表的 row_format 需要使用 compressed 或者 dynamic。
主要字符集的計算方式:
- latin1 = 1 byte = 1 character
- uft8 = 3 byte = 1 character
- gbk = 2 byte = 1 character
使用前綴索引帶來的風險:
INNODB的索引會限制單獨Key的最大長度為 767 字節,超過這個長度必須建立小於等於 767 字節的前綴索引。 此外,BLOB和TEXT類型的列只能創建前綴索引。 前綴索引能提高索引建立速度和檢索速度,但是下面情況是無法使用前綴索引的:
- 索引覆蓋掃描
- 通過索引的排序(order by, group by)
還是在上面的測試表上:


author:bill
2015年 12月 08日
