許多開發者習慣「保守起見全用 VARCHAR(255)」,實際上長度定義直接影響索引大小、內存分配、排序緩衝、臨時表等核心性能指標。本文從索引 key_len 計算、ROW_FORMAT=FIXED 行存儲、排序/臨時表內存三個維度量化差異。
建立索引時若不指定前綴長度,索引長度默認等於列定義長度。
-- 測試表
CREATE TABLE abc (id INT, name VARCHAR(20)) ENGINE=InnoDB CHARSET=utf8;
ALTER TABLE abc ADD INDEX idx_name (name);
EXPLAIN SELECT name FROM abc; -- key_len = 20*3(utf8) + 2(變長) + 1(NULL) = 63
ALTER TABLE abc MODIFY name VARCHAR(255);
EXPLAIN SELECT name FROM abc; -- key_len = 255*3 + 2 + 1 = 768
| 列定義 | 公式 (utf8mb4=4字節/字符) | VARCHAR(20) | VARCHAR(255) |
|---|---|---|---|
| 變長 + 允許 NULL | L×4 + 1 + 2 | 83 | 1023 |
| 變長 + NOT NULL | L×4 + 2 | 82 | 1022 |
| 固定長 CHAR + 允許 NULL | L×4 + 1 | 81 | 1021 |
| 固定長 CHAR + NOT NULL | L×4 | 80 | 1020 |
結論:VARCHAR(255) 索引條目佔用 12 倍 內存,導致 Buffer Pool 容納的索引頁數銳減,磁碟 IO 增加。
CREATE TABLE t_fixed (c VARCHAR(20)) ENGINE=MyISAM ROW_FORMAT=FIXED;
CREATE TABLE t_fixed255 (c VARCHAR(255)) ENGINE=MyISAM ROW_FORMAT=FIXED;
-- 插入相同 'hello'
-- t_fixed 每行固定佔用 20*3 = 60 字節
-- t_fixed255 每行固定佔用 255*3 = 765 字節
雖然 InnoDB 預設 DYNAMIC/COMPRESSED 不受影響,但MyISAM 表若誤用 FIXED,空間浪費極大。
sort_buffer_size、join_buffer_size、tmp_table_size、max_heap_table_size 等參數按列定義最大長度預分配內存。| 指標 | VARCHAR(20) | VARCHAR(255) | 差異 |
|---|---|---|---|
| 索引大小 | ~80 MB | ~960 MB | 12x |
| 單次排序內存 (10萬行) | ~80 MB | ~960 MB | 12x |
| 內存臨時表行大小 | ~80 字節 | ~960 字節 | 12x |
| 磁碟臨時表觸發閾值 | 較高 | 極低 | 易觸發 |
ALTER TABLE ... MODIFY col VARCHAR(new_len)(需重建表,規劃窗口期)。「精確定義長度」是零成本、高收益的性能優化手段,建議納入代碼評審清單,從源頭杜絕過度定長。