2013-03-01 88 views
21

我有這樣的一個表:的MySQL VARCHAR索引長度

CREATE TABLE `products` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `name` varchar(512) NOT NULL, 
    `description` text, 
    PRIMARY KEY (`id`), 
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8; 

,一個是這樣的:

CREATE TABLE `product_variants` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `product_id` int(11) unsigned NOT NULL, 
    `product_code` varchar(255) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `product_code` (`product_code`), 
    KEY `product_variant_product_fk` (`product_id`), 
    CONSTRAINT `product_variant_product_fk` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=1037 DEFAULT CHARSET=utf8; 

和SQL語句這樣

SELECT p.id AS id, p.name AS name, p.description AS description, pv.id AS product_variant_id, pv.product_code AS product_code 
FROM products p 
INNER JOIN product_variants pv ON pv.product_id = p.id 
ORDER BY p.name ASC 
LIMIT 300 OFFSET 0; 

這要是我解釋給我這個:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref  | rows | Extra   | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| 1 | SIMPLE  | p  | ALL | PRIMARY     | NULL      | NULL | NULL | 993658 | Using filesort | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | db.p.id |  1 |    | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
2 rows in set (0.00 sec) 

對於一百萬行,這很慢。我試着在 products.name添加索引有:

ALTER TABLE products ADD INDEX `product_name_idx` (name(512)); 

其中給出了這樣的:

mysql> show indexes from products; 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| Table | Non_unique | Key_name   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
| products |   0 | PRIMARY   |   1 | id    | A   |  993658 |  NULL | NULL |  | BTREE  |   |    | 
| products |   1 | product_manf_fk |   1 | manufacturer_id | A   |   18 |  NULL | NULL | YES | BTREE  |   |    | 
| products |   1 | product_name_idx |   1 | name   | A   |   201 |  255 | NULL |  | BTREE  |   |    | 
+----------+------------+------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 
3 rows in set (0.00 sec) 

我認爲Sub_part列顯示已在 索引的前綴(以字節爲單位),如this page所述。

當我再次解釋查詢,我得到:

+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref  | rows | Extra   | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
| 1 | SIMPLE  | p  | ALL | PRIMARY     | NULL      | NULL | NULL | 993658 | Using filesort | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | db.p.id |  1 |    | 
+----+-------------+-------+------+----------------------------+----------------------------+---------+---------+--------+----------------+ 
2 rows in set (0.00 sec) 

它看起來像新的索引不被使用。如 this page所述,如果前綴索引爲 ,索引將不會用於排序。事實上,如果我用截斷數據:

alter table products modify `name` varchar(255) not null; 

的解釋給出了:

+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 
| id | select_type | table | type | possible_keys    | key      | key_len | ref           | rows | Extra | 
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 
| 1 | SIMPLE  | p  | index | PRIMARY     | product_name_idx   | 767  | NULL           | 300 |  | 
| 1 | SIMPLE  | pv | ref | product_variant_product_fk | product_variant_product_fk | 4  | oh_2c98c233_69fe_4f06_ad0d_fe6f85a5beac.p.id | 1 |  | 
+----+-------------+-------+-------+----------------------------+----------------------------+---------+----------------------------------------------+------+-------+ 

我覺得背上這件事。不過,它在this page上說 InnoDB表最多可以有767字節的索引。如果長度爲 字節,爲什麼它拒絕超過255?如果它是 個字符,它如何決定每個UTF-8字符的長度?是 它只是假設3?

而且,現在用這個版本的MySQL:

mysql> select version(); 
+------------+ 
| version() | 
+------------+ 
| 5.5.27-log | 
+------------+ 
1 row in set (0.00 sec) 
+0

的MySQL 5.0.3一個varchar字段的最大長度是255,和65535在MySQL 5.0.3和更高版本之前。 – Cyclonecode 2013-03-01 12:07:22

+0

抱歉 - 應該說,我正在使用5.5.27-log – l0st3d 2013-03-01 12:20:14

回答

43

我必須修改我的答案是由於我的研究。我最初發布這個(引自本人):

我相信答案是,你可以不知道有多少字符將 在索引,因爲你無法知道有多少字節的字符 會(除非你做的東西排除多字節字符)。

而且我不知道,但它可能仍然是正確的,但不能在相當,我想。

下面是正確答案:

MySQL假設每utf8字符3個字節。 255個字符是您可以爲每列指定的最大索引大小,因爲256x3 = 768,這打破了767字節的限制。

如果不指定索引大小,MySQL會選擇最大大小(即每列255個)。 UNIQUE約束不能放在長度大於255的utf8列上,因爲唯一索引必須包含整個單元格值。但是可以使用常規索引 - 它只會索引前255個字符(或前767個字節)。這就是我仍然有些謎團的地方。

MySTERY: 爲了安全起見,我可以明白爲什麼MySQL爲每個字符假定3個字節,否則UNIQUE約束可能被破壞。但文檔似乎表明索引的大小實際上是以字節爲單位,而不是字符。因此,假設您在varchar(25 )列上放置了一個char(765字節)索引。如果您存儲的字符都是ASCII字符,即1字節字符,如A-Z,a-z,0-9,則可以將整列填入767字節索引。而且這似乎是實際發生的事情。

下面是從我關於字符,字節原始回答一些更多的信息,等等


根據wikipedia,UTF-8字符可以是1,2,3或4個字節長。 但是,根據this mysql documentation,最大字符大小爲3個字節,因此超過255個字符的任何列索引索引可能會達到該字節限制。但據我瞭解,它可能不會。如果大多數字符都在ASCII範圍內,那麼平均字符大小將接近1個字節。例如,如果您的平均字符大小爲1.3字節(大部分爲1字節,但大量爲2-3字節字符),那麼您可以指定索引767/1.3因此,如果您主要存儲1字節字符,你的實際字符限制更像是: 767/1.3 = 590.但事實證明,它不是它的工作方式。 255個字符是限制。

this MySQL documentation提到的,

前綴限制以字節爲單位,而在 前綴長度CREATE INDEX語句被解釋爲 非二進制數據類型(CHAR,VARCHAR,TEXT)的字符數。在爲使用多字節 字符集的列指定前綴長度時,請考慮到 。

看來,MySQL建議人們做一個計算/猜測,就像我剛纔所做的那樣,以確定您的密鑰大小的一個varchar列。但實際上,不能爲指定utf8列的大於255的索引。

最後,如果你回頭給我的第二個環節再次,也有這樣的:

當innodb_large_prefix配置選項的功能,這個 長度限制提高到3072個字節,對於使用InnoDB表 DYNAMIC和COMPRESSED行格式。

因此,如果你想要,可以得到更大的索引,稍微調整一下。只要確保行格式是DYNAMIC或COMPRESSED。在這種情況下,您可以指定一個1023或1024個字符的索引。


順便說一下,事實證明,您可以使用 the utf8mb4 character set來存儲4字節字符。 utf8字符集顯然只存儲 "plane 0" characters

編輯:

我只是試圖創建一個varchar(511)列有TINYINT(1)列一個綜合指數,並得到了錯誤信息,說最大索引大小是767個字節。這讓我相信MySQL假定utf8字符集列將包含每個字符3個字節(最大值),並允許您使用255個字符最大值。但也許這只是對於複合索引。我會更新我的答案,因爲我瞭解更多。但現在我將其作爲編輯離開。

0

限制對InnoDB表

警告

不要從MyISAM數據轉換MySQL的系統表中的MySQL數據庫InnoDB表。這是一個不受支持的操作。如果這樣做,MySQL將不會重新啓動,直到您從備份中還原舊的系統表或使用mysql_install_db程序重新生成它們。

警告

這是不配置InnoDB的使用數據文件或者NFS卷日誌文件是個好主意。否則,這些文件可能會被其他進程鎖定,並且不可供MySQL使用。

最大值和最小值

  1. 一個表可以包含最大1000個列。
  2. 一個表最多可以包含64個二級索引。
  3. 默認情況下,單列索引的索引鍵最多可以爲767個字節。相同的長度限制適用於任何索引鍵前綴。例如,假設使用UTF-8字符集並且每個字符的最大字節數爲3,那麼您可能會在TEXT或VARCHAR列中的列前綴索引超過255個字符的情況下達到此限制。當啓用innodb_large_prefix配置選項時,對於使用DYNAMIC和COMPRESSED行格式的InnoDB表,此長度限制將上升爲3072個字節。
  4. 如果指定的索引前綴長度大於允許的最大值,則會將該長度自動減少爲最大長度。在MySQL 5.6和更高版本中,指定大於最大長度的索引前綴長度會產生錯誤。

當啓用innodb_large_prefix時,試圖爲REDUNDANT或COMPACT表創建密鑰長度大於3072的索引前綴會導致ER_INDEX_COLUMN_TOO_LONG錯誤。

InnoDB內部最大密鑰長度爲3500字節,但MySQL本身將其限制爲3072字節。此限制適用於多列索引中組合索引鍵的長度。

除變長列(VARBINARY,VARCHAR,BLOB和TEXT)外,最大行長度略小於數據庫頁面的一半。也就是說,最大行長度大約是8000字節。 LONGBLOB和LONGTEXT列必須小於4GB,並且總行長度(包括BLOB和TEXT列)必須小於4GB。

參考: InnoDB Restrictions