| N | 資料位元組 (N×4) | 767 限制下 | 3072 限制下 (large_prefix=ON) |
| 191 | 764 | ✅ 可建索引 | ✅ |
| 192 | 768 | ❌ 超過 767 | ✅ |
| 255 | 1020 | ❌ | ✅ |
| 768 | 3072 | ❌ | ✅ 臨界值 |
| 769 | 3076 | ❌ | ❌ 超過 3072 |
實測驗證 (MySQL 5.6/5.7 預設 large_prefix=OFF)
CREATE TABLE temp_2 (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
pn VARCHAR(128) DEFAULT '',
first_path VARCHAR(256) DEFAULT '',
dir VARCHAR(255) DEFAULT '',
a TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 單欄索引
ALTER TABLE temp_2 ADD KEY (dir); -- VARCHAR(255) 765 Bytes ✅
ALTER TABLE temp_2 ADD KEY (first_path); -- VARCHAR(256) 768 Bytes ❌ Warning 1071
-- 實際建立為 KEY `first_path` (`first_path`(255)) 自動截斷前 255 字元
-- TEXT 欄位需指定前綴長度
ALTER TABLE temp_2 ADD KEY (a(255)); -- ❌ Warning 1071,實際建為 a(255) 但超過 767
-- 正確:a(191) 或 a(255) 配合 large_prefix=ON
聯合索引:767 限制針對「單欄」而非「總和」
-- pn(128)=512B + first_path(256)=1024B → 總 1536B > 767 但單欄 first_path 已超限
ALTER TABLE temp_2 ADD KEY (pn, first_path); -- ❌ Warning 1071
-- 實際建立:KEY `pn` (`pn`, `first_path`(255)) → first_path 截斷 255 字元
結論:聯合索引中每個欄位皆不能超過單欄限制 (767/3072),總和受 3072 限制。
聯合索引總長度 3072 實測
CREATE TABLE temp_3 (id BIGINT PRIMARY KEY, a TEXT, b TEXT, c TEXT, d TEXT, e TEXT) CHARSET=utf8;
-- 255×4=1020 × 3 = 3060 + 4×3=12 → 3072 臨界
ALTER TABLE temp_3 ADD KEY (a(255), b(255), c(255), d(255), e(4)); -- ✅ 成功
ALTER TABLE temp_3 ADD KEY (a(255), b(255), c(255), d(255), e(5)); -- ❌ 3072 超限
解決方案與最佳實踐
| 方案 | 適用場景 | 優點 | 缺點 |
啟用 innodb_large_prefix=ON + ROW_FORMAT=DYNAMIC/COMPRESSED | 5.7.7+、8.0 預設開啟 | 單欄支援 3072 Bytes,VARCHAR(768) 可直接索引 | 需檔案格式支援,舊版無效 |
前綴索引 col(191) | 舊版、不想改參數 | 相容所有版本 | 前綴衝突率高、無法覆蓋完整值 |
改用 VARCHAR(191) 或更短 | 新設計、業務允許 | 無截斷、索引最小 | 業務受限 |
改用 CHAR(32) 存雜湊值 (MD5/SHA256) | 長字串唯一查找 | 固定 32/64 字元、索引極小 | 需應用層計算雜湊、無法範圍查詢 |
| 升級 MySQL 8.0 | 長期規劃 | 預設 large_prefix=ON、預設 utf8mb4、效能更好 | 遷移成本 |
檢查現有表索引長度
SELECT
TABLE_NAME, INDEX_NAME, COLUMN_NAME,
CHARACTER_MAXIMUM_LENGTH,
CASE
WHEN CHARACTER_SET_NAME = 'utf8mb4' THEN CHARACTER_MAXIMUM_LENGTH * 4
WHEN CHARACTER_SET_NAME = 'utf8' THEN CHARACTER_MAXIMUM_LENGTH * 3
ELSE CHARACTER_MAXIMUM_LENGTH * 2
END AS index_bytes
FROM INFORMATION_SCHEMA.STATISTICS
WHERE TABLE_SCHEMA = 'your_db' AND INDEX_NAME != 'PRIMARY'
ORDER BY index_bytes DESC;
InnoDB utf8mb4 索引 767 Bytes 限制是歷史包袱。新專案請直接用 MySQL 8.0 (預設 large_prefix=ON) 或 5.7.7+ 開啟參數;舊系統若無法升級,請統一將可索引 VARCHAR 限制在 191 字元內,或改用雜湊值索引。聯合索引記住「單欄不超限、總和不超 3072」。
copyright ©
萬盛學電腦網 all rights reserved