Bali, Indonesia
Insights

Kenapa Muncul Error Key Length Saat Migrasi Database ke utf8mb4

October 17, 2026
0:00
Pernahkah kamu berniat memperbarui character set database dari utf8 lawas ke utf8mb4 agar aplikasi bisa menyimpan karakter emoji atau aksara khusus, tetapi proses migrasi langsung terhenti oleh pesan error? Salah satu pesan yang paling sering muncul berbunyi Specified key was too long; max key length is 767 bytes (atau batas lain tergantung versi MySQL dan MariaDB yang berjalan). Masalah ini kerap membuat bingung karena struktur kolom tabel sama sekali tidak diubah ukurannya, melainkan hanya jenis encoding karakternya saja. Di lingkungan hosting dan administrasi server, kendala ini bukan tanda bahwa database rusak atau disk penuh. Error ini murni benturan antara alokasi memori fisik per karakter pada storage engine InnoDB dengan batasan panjang index yang sudah ditentukan oleh format baris tabel. Mari kita bedah pertanyaan-pertanyaan yang sering diajukan seputar error ini agar proses migrasi database kamu berjalan mulus.
Kunci persoalannya terletak pada perbedaan definisi "karakter" dan "byte". Pada MySQL dan MariaDB versi terdahulu, set karakter bernama utf8 sebenarnya adalah alias untuk utf8mb3. Encoding ini hanya mengalokasikan maksimal 3 byte per karakter. Sementara itu, standar Unicode modern membutuhkan hingga 4 byte untuk menampung karakter tertentu seperti emoji atau simbol matematika khusus. Karakter 4 byte inilah yang didukung penuh oleh utf8mb4 (UTF-8 multi-byte 4). Saat kamu membuat index pada sebuah kolom teks, InnoDB tidak menghitung batas maksimum index berdasarkan jumlah karakter yang saat ini tersimpan di dalamnya. InnoDB mencadangkan kapasitas index berdasarkan skenario terburuk (worst-case scenario), yaitu mengalikan jumlah panjang karakter deklarasi kolom dengan kebutuhan byte maksimum dari set karakter yang aktif. Ketika kolom bertipe VARCHAR(255) masih menggunakan utf8mb3, InnoDB mengalokasikan memori index sebesar 255 dikali 3 byte, yang menghasilkan 765 byte. Angka 765 byte ini pas di bawah batas historis index InnoDB lawas, yakni 767 byte. Namun begitu tabel kamu ubah ke utf8mb4, perhitungannya melonjak menjadi 255 dikali 4 byte, yaitu 1020 byte. Karena 1020 byte melebihi batas 767 byte, mesin database menolak perubahan tersebut dan langsung menghentikan proses alter table.
Angka 767 byte berasal dari arsitektur tabel lama InnoDB yang menggunakan format file Antelope dengan format baris (row format) bertipe COMPACT atau REDUNDANT. Pada format klasik ini, ukuran satu halaman (page size) InnoDB standarnya adalah 16 KB. Untuk memastikan bahwa setidaknya dua baris data penuh dan informasi kontrol internal muat di dalam satu halaman B-tree index, para perancang awal InnoDB menetapkan batas ketat pada ukuran prefix index kolom tunggal. Batas tersebut dipatok pada sepersepuluh ukuran payload halaman, yang secara matematis jatuh pada 767 byte. Aturan ini sudah ada jauh sebelum era emoji dan adopsi luas 4-byte UTF-8 di web modern. Selama bertahun-tahun, banyak aplikasi web dan kerangka kerja (framework) PHP memakai panjang default VARCHAR(255) untuk kolom string seperti username, email, slug URL, atau token autentikasi. Kombinasi VARCHAR(255) dan utf8mb3 (765 byte) berjalan mulus tanpa masalah selama bertahun-tahun, sehingga ketika migrasi ke utf8mb4 dilakukan di server dengan format lawas, batas fisik 767 byte langsung terlanggar. Perlu diingat bahwa pada server modern (seperti MySQL 5.7 ke atas atau MariaDB 10.2 ke atas), batas default index ini sebenarnya sudah dinaikkan menjadi 3072 byte. Namun, database yang berasal dari hasil dump impor server lama atau tabel yang dibuat pada aplikasi lama sering kali masih membawa atribut format baris lama yang membatasi index hingga 767 byte saja.
Jika kamu pernah mengamati skrip migrasi CMS seperti WordPress atau skema tabel di berbagai proyek open-source lawas, kamu mungkin memperhatikan angka 191 yang terasa ganjil untuk ukuran panjang kolom. Mengapa bukan 200 atau 190? Angka 191 adalah solusi matematis praktis untuk mengakali batas 767 byte tanpa harus mengubah konfigurasi server atau format tabel secara global. Jika kita membagi batas maksimum 767 byte dengan 4 byte (kapasitas per karakter pada utf8mb4), kita mendapatkan angka 191,75 karakter. Karena panjang kolom database harus berupa bilangan bulat, angka tersebut dibulatkan ke bawah menjadi 191 karakter. Dengan mendefinisikan kolom sebagai VARCHAR(191), alokasi maksimum index menjadi 191 dikali 4 byte, yaitu 764 byte. Angka 764 byte berada tepat di bawah batas aman 767 byte, dengan sisa 3 byte cadangan. Oleh sebab itu, banyak pengembang memilih menurunkan kolom index yang semula VARCHAR(255) menjadi VARCHAR(191) saat mempersiapkan dukungan emoji, sehingga tabel tetap kompatibel di semua server hosting tanpa mempedulikan apakah server tujuan memakai format InnoDB lama atau baru.
Untuk mengatasi batasan format Antelope, MySQL dan InnoDB memperkenalkan arsitektur file baru bernama Barracuda yang mendukung row format DYNAMIC dan COMPRESSED. Perbedaan mendasar di antara keduanya ada pada cara InnoDB menangani kolom teks panjang di luar halaman data utama. Pada format COMPACT, InnoDB mencoba menyimpan sebagian prefix data teks langsung di halaman indeks B-tree lokal. Sementara pada format DYNAMIC, InnoDB dapat menyimpan seluruh payload kolom panjang secara off-page pada halaman overflow, hanya menyisakan pointer 20-byte di halaman utama. Efisiensi ini memungkinkan InnoDB melonggarkan batas panjang index secara drastis. Ketika sebuah tabel menggunakan ROW_FORMAT=DYNAMIC, batas maksimum index tunggal melonjak dari 767 byte menjadi 3072 byte. Dengan batas 3072 byte, kamu bisa membuat index pada kolom VARCHAR(768) penuh meski menggunakan utf8mb4 (768 dikali 4 byte = 3072 byte). Artinya, kolom standar VARCHAR(255) yang membutuhkan 1020 byte akan tersimpan dan terindeks dengan sangat leluasa tanpa memicu error apa pun.
Tergantung pada hak akses server yang kamu miliki, ada dua pendekatan umum yang bisa diambil: memperbarui format baris tabel di level database atau menyesuaikan panjang kolom index di level skema aplikasi. Jika kamu memiliki akses administratif penuh ke database dan server menjalankan versi MySQL atau MariaDB modern, pendekatan terbaik adalah mengubah format baris tabel menjadi DYNAMIC sebelum atau bersamaan dengan konversi set karakter. Dengan cara ini, kamu tidak perlu memotong panjang deklarasi kolom pada aplikasi.
SQL
-- Periksa struktur tabel dan ubah format baris beserta charset ke utf8mb4
ALTER TABLE nama_tabel 
  ROW_FORMAT = DYNAMIC,
  CONVERT TO CHARACTER SET utf8mb4 
  COLLATE utf8mb4_unicode_ci;
Namun, jika server MySQL kamu adalah instalasi lawas (seperti MySQL 5.6 yang belum mengaktifkan parameter global innodb_large_prefix dan innodb_file_format = Barracuda), atau jika kamu berada di lingkungan shared hosting di mana setelan global server dikunci, mengubah format baris terkadang tetap gagal. Pada situasi seperti ini, solusi paling aman adalah meninjau kembali kolom-kolom yang memiliki index unik (UNIQUE) atau index pencarian. Turunkan panjang kolom tersebut ke VARCHAR(191) sebelum menjalankan konversi charset. Memahami bahwa batasan index dihitung dari perkalian panjang karakter dan kapasitas maksimum byte encoding membantu kita melihat akar masalah dengan jernih. Begitu format baris tabel disesuaikan ke DYNAMIC atau panjang index dirasionalisasi ke batas aman, migrasi ke utf8mb4 dapat berjalan tuntas tanpa risiko kegagalan skema data.