# 1071 - Kunci yang ditentukan terlalu panjang; panjang kunci maks adalah 1000 byte

103

Saya tahu pertanyaan dengan judul ini telah dijawab sebelumnya, tetapi harap baca terus. Saya telah membaca semua pertanyaan / jawaban lain tentang kesalahan ini sebelum memposting.

Saya mendapatkan kesalahan di atas untuk kueri berikut:

CREATE TABLE IF NOT EXISTS `pds_core_menu_items` (
  `menu_id` varchar(32) NOT NULL,
  `parent_menu_id` int(32) unsigned DEFAULT NULL,
  `menu_name` varchar(255) DEFAULT NULL,
  `menu_link` varchar(255) DEFAULT NULL,
  `plugin` varchar(255) DEFAULT NULL,
  `menu_type` int(1) DEFAULT NULL,
  `extend` varchar(255) DEFAULT NULL,
  `new_window` int(1) DEFAULT NULL,
  `rank` int(100) DEFAULT NULL,
  `hide` int(1) DEFAULT NULL,
  `template_id` int(32) unsigned DEFAULT NULL,
  `alias` varchar(255) DEFAULT NULL,
  `layout` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`menu_id`),
  KEY `index` (`parent_menu_id`,`menu_link`,`plugin`,`alias`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Apakah ada yang tahu mengapa dan bagaimana cara memperbaikinya? Tangkapannya adalah - kueri yang sama ini berfungsi dengan sempurna di mesin lokal saya, dan bekerja dengan baik di host saya sebelumnya. Btw. Itu dari proyek yang matang - phpdevshell - jadi saya kira orang-orang ini tahu apa yang mereka lakukan, meskipun Anda tidak pernah tahu.

Setiap petunjuk dihargai.

Saya menggunakan phpMyAdmin.

CodeVirtuoso
sumber

Jawaban:

171

Seperti yang dikatakan @Devart, total panjang indeks Anda terlalu panjang.

Jawaban singkatnya adalah Anda tidak boleh mengindeks kolom VARCHAR yang begitu panjang, karena indeks akan menjadi sangat besar dan tidak efisien.

Praktik terbaiknya adalah menggunakan indeks awalan sehingga Anda hanya mengindeks substring kiri data. Sebagian besar data Anda akan jauh lebih pendek dari 255 karakter.

Anda dapat mendeklarasikan panjang prefiks per kolom saat Anda menentukan indeks. Sebagai contoh:

...
KEY `index` (`parent_menu_id`,`menu_link`(50),`plugin`(50),`alias`(50))
...

Tapi berapa panjang prefiks terbaik untuk kolom tertentu? Berikut metode untuk mengetahuinya:

SELECT
 ROUND(SUM(LENGTH(`menu_link`)<10)*100/COUNT(`menu_link`),2) AS pct_length_10,
 ROUND(SUM(LENGTH(`menu_link`)<20)*100/COUNT(`menu_link`),2) AS pct_length_20,
 ROUND(SUM(LENGTH(`menu_link`)<50)*100/COUNT(`menu_link`),2) AS pct_length_50,
 ROUND(SUM(LENGTH(`menu_link`)<100)*100/COUNT(`menu_link`),2) AS pct_length_100
FROM `pds_core_menu_items`;

Ini memberi tahu Anda proporsi baris yang memiliki tidak lebih dari panjang string tertentu di menu_linkkolom. Anda mungkin melihat keluaran seperti ini:

+---------------+---------------+---------------+----------------+
| pct_length_10 | pct_length_20 | pct_length_50 | pct_length_100 |
+---------------+---------------+---------------+----------------+
|         21.78 |         80.20 |        100.00 |         100.00 |
+---------------+---------------+---------------+----------------+

Ini memberi tahu Anda bahwa 80% string Anda kurang dari 20 karakter, dan semua string Anda kurang dari 50 karakter. Jadi tidak perlu mengindeks lebih dari panjang awalan 50, dan tentu saja tidak perlu mengindeks panjang penuh 255 karakter.

PS: Tipe INT(1)dan INT(32)data menunjukkan kesalahpahaman lain tentang MySQL. Argumen numerik tidak memiliki efek terkait penyimpanan atau rentang nilai yang diperbolehkan untuk kolom tersebut. INTselalu 4 byte, dan selalu mengizinkan nilai dari -2147483648 hingga 2147483647. Argumen numerik adalah tentang nilai padding selama tampilan, yang tidak berpengaruh kecuali jika Anda menggunakan ZEROFILLopsi.

Bill Karwin
sumber
17
Terima kasih banyak atas penjelasan detailnya. Selain memperbaiki masalah, saya juga belajar sesuatu yang berharga.
CodeVirtuoso
Sungguh, kueri yang sangat berguna untuk mengetahui panjang indeks harus disetel. Telah menggunakan ini beberapa kali untuk menentukan panjang terbaik untuk indeks. Terima kasih sudah berbagi!
Niraj Kumar
1
Ada bug halus dalam kueri praktis Anda untuk mengukur berapa lama string sebenarnya: Anda berasumsi bahwa semua string ada; Artinya, mereka semua ada di sana. Jika beberapa adalah null, itu akan membuang perhitungan Anda dan tidak melaporkan string pendek. Anda ingin menggunakan count ([field_name]) daripada count (*).
D Mac
Ini tidak akan menggunakan lebih dari indeks "awalan" pertama.
Rick James
28

Kesalahan ini berarti panjang indeks indexlebih dari 1000 byte. MySQL dan mesin penyimpanan mungkin memiliki batasan ini. Saya mendapatkan kesalahan serupa pada MySQL 5.5 - 'Kunci yang ditentukan terlalu panjang; panjang kunci maks adalah 3072 byte 'ketika menjalankan skrip ini:

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

UTF8 adalah multi-byte, dan panjang kunci dihitung dengan cara ini - 500 * 3 * 6 = 9000 byte.

Tapi perhatikan, kueri berikutnya berfungsi!

CREATE TABLE IF NOT EXISTS test_table1 (
  column1 varchar(500) NOT NULL,
  column2 varchar(500) NOT NULL,
  column3 varchar(500) NOT NULL,
  column4 varchar(500) NOT NULL,
  column5 varchar(500) NOT NULL,
  column6 varchar(500) NOT NULL,
  KEY `index` (column1, column2, column3, column4, column5, column6)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

... karena saya menggunakan CHARSET = latin1, dalam hal ini panjang kunci adalah 500 * 6 = 3000 byte.

Devart
sumber
7
Terima kasih atas balasannya, ini berfungsi, tetapi dengan mengorbankan charset utf8. Dapatkah pembatasan ini diatasi dengan cara apa pun (saya memiliki akses server penuh), apakah ada alasan yang baik?
CodeVirtuoso
4
Ini adalah nasihat yang buruk, harap pelajari apa itu charsets dan bagaimana hal ini akan menyebabkan masalah besar di masa mendatang. Database rangkaian karakter campuran tidak hanya menyebabkan masalah saat menggunakan gabungan atau sub-pilihan, tetapi juga menempatkan data Anda ke dalam format yang tidak dinormalisasi dan hampir mustahil untuk diperbaiki nanti.
Geoffrey
17

Saya mengalami masalah ini, dan diselesaikan dengan mengikuti:

Sebab

Ada bug yang diketahui pada MySQL terkait dengan MyISAM, kumpulan karakter UTF8, dan indeks yang dapat Anda periksa di sini.

Resolusi

  • Pastikan MySQL dikonfigurasi dengan mesin penyimpanan InnoDB.

  • Ubah mesin penyimpanan yang digunakan secara default sehingga tabel baru akan selalu dibuat dengan tepat:

    set GLOBAL storage_engine='InnoDb';

  • Untuk MySQL 5.6 dan yang lebih baru, gunakan yang berikut ini:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Dan terakhir, pastikan Anda mengikuti petunjuk yang diberikan di Migrasi ke MySQL .

Referensi

Vishrant
sumber
Dalam kebanyakan kasus, kami lupa mengkonfigurasi mesin penyimpanan sebagai 'InnoDB'. Jawaban berikut adalah yang paling sederhana dan mungkin akan menyelesaikan masalah bagi sebagian besar pengguna di sini. Terima kasih.
Frederiko Cesar
10

jalankan query ini sebelum membuat atau mengubah tabel.

SET @@global.innodb_large_prefix = 1;

ini akan mengatur panjang kunci maksimal menjadi 3072 byte

Raza Ahmed
sumber
3

Batas ukuran indeks ini tampaknya lebih besar pada versi 64 bit MySQL.

Saya mencapai batasan ini mencoba membuang database dev kami dan memuatnya pada virt VMWare lokal. Akhirnya saya menyadari bahwa server dev jarak jauh adalah 64 bit dan saya telah membuat virt. Saya baru saja membuat virt 64 bit dan saya dapat memuat database secara lokal.

Ryan Olson
sumber
2

Saya baru saja melewati kesalahan ini dengan hanya mengubah nilai "panjang" dalam database asli menjadi total sekitar "1000" dengan mengubah strukturnya, dan kemudian mengekspor yang sama, ke server. :)

Vishwadeep Kapoor
sumber
0

Saya menghadapi masalah yang sama, digunakan di bawah kueri untuk menyelesaikannya.

Saat membuat DB, Anda dapat menggunakan encoding utf-8

misalnya. create database my_db character set utf8 collate utf8mb4;

EDIT: (Mempertimbangkan saran dari komentar) Mengubah utf8_bin menjadi utf8mb4

Sudhir Dhumal
sumber
2
Ini mengarahkan saya ke arah yang benar. Bagi saya, saya harus mengubah pemeriksaan saya ke: utf8_general_ci
Ian Newland
2
Ini BUKAN arah yang benar, jangan lakukan ini! MySQL utf8BUKAN utf8, ini adalah format kepemilikan yang disadap yang seharusnya tidak pernah membuahkan hasil. utf8mb4benar utf8dan merupakan default yang disarankan untuk utf8dukungan yang tepat .
Geoffrey
utf8mb4adalah pengkodean yang benar untuk digunakan di mysql dan bukanutf8
alok
Itu hanya sebuah contoh - bagaimanapun saya telah memperbarui jawabannya.
Sudhir Dhumal