萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 程式設計 >  >> 資料庫 >> MySQL 教學

MySQL InnoDB utf8mb4 VARCHAR 索引長度限制 767 Bytes 完整解析:單欄、聯合、前綴索引實戰

InnoDB utf8mb4 下單欄索引長度上限 767 Bytes (舊版) / 3072 Bytes (5.7.7+ innodb_large_prefix=ON),導致 VARCHAR(255) 無法直接建立索引。本文完整推導限制來源、實測單欄/聯合/前綴索引行為、innodb_large_prefix 參數影響,並給出設計規避指南。

限制來源:InnoDB 內部鍵長度上限

  • 內部上限:3500 Bytes
  • MySQL 層限制:3072 Bytes (非 64 位元舊版 1024 Bytes)
  • 單欄索引上限:767 Bytes (歷史原因,innodb_large_prefix=OFF 時)

utf8mb4 下 VARCHAR(N) 索引可行性計算

實測驗證 (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 超限

解決方案與最佳實踐

N資料位元組 (N×4)767 限制下3072 限制下 (large_prefix=ON)
191764✅ 可建索引✅
192768❌ 超過 767✅
2551020❌✅
7683072❌✅ 臨界值
7693076❌❌ 超過 3072

檢查現有表索引長度

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
方案適用場景優點缺點
啟用 innodb_large_prefix=ON + ROW_FORMAT=DYNAMIC/COMPRESSED5.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、效能更好遷移成本