# 1071 - Khóa được chỉ định quá dài; độ dài khóa tối đa là 1000 byte


102

Tôi biết các câu hỏi với tiêu đề này đã được trả lời trước đây, nhưng hãy đọc tiếp. Tôi đã đọc kỹ tất cả các câu hỏi / câu trả lời khác về lỗi này trước khi đăng.

Tôi gặp lỗi ở trên cho truy vấn sau:

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;

Có ai có ý tưởng tại sao và làm thế nào để sửa chữa nó? Vấn đề là - cùng một truy vấn này hoạt động hoàn hảo trên máy cục bộ của tôi và hoạt động tốt trên máy chủ trước đó của tôi. Btw. nó là từ một dự án trưởng thành - phpdevshell - vì vậy tôi đoán những người này biết họ đang làm gì, mặc dù bạn không bao giờ biết.

Bất kỳ manh mối nào được đánh giá cao.

Tôi đang sử dụng phpMyAdmin.

Câu trả lời:


170

Như @Devart nói, tổng độ dài chỉ mục của bạn quá dài.

Câu trả lời ngắn gọn là bạn không nên lập chỉ mục các cột VARCHAR dài như vậy, bởi vì chỉ mục sẽ rất cồng kềnh và không hiệu quả.

Cách tốt nhất là sử dụng các chỉ mục tiền tố để bạn chỉ lập chỉ mục một chuỗi con bên trái của dữ liệu. Hầu hết dữ liệu của bạn sẽ ngắn hơn 255 ký tự rất nhiều.

Bạn có thể khai báo độ dài tiền tố cho mỗi cột khi bạn xác định chỉ mục. Ví dụ:

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

Nhưng độ dài tiền tố tốt nhất cho một cột nhất định là bao nhiêu? Đây là một phương pháp để tìm ra:

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`;

Nó cho bạn biết tỷ lệ các hàng không có nhiều hơn một độ dài chuỗi nhất định trong menu_linkcột. Bạn có thể thấy đầu ra như thế này:

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

Điều này cho bạn biết rằng 80% chuỗi của bạn có ít hơn 20 ký tự và tất cả các chuỗi của bạn có ít hơn 50 ký tự. Vì vậy, không cần phải lập chỉ mục nhiều hơn độ dài tiền tố là 50 và chắc chắn không cần lập chỉ mục với độ dài đầy đủ là 255 ký tự.

Tái bút: Kiểu dữ liệu INT(1)INT(32)chỉ ra một sự hiểu lầm khác về MySQL. Đối số số không có tác dụng liên quan đến lưu trữ hoặc phạm vi giá trị được phép cho cột. INTluôn là 4 byte và nó luôn cho phép các giá trị từ -2147483648 đến 2147483647. Đối số số là về giá trị đệm trong khi hiển thị, không có tác dụng trừ khi bạn sử dụng ZEROFILLtùy chọn.


17
Cảm ơn rất nhiều cho lời giải thích chi tiết. Ngoài việc sửa chữa một vấn đề, tôi cũng học được một số điều có giá trị.
CodeVirtuoso

Thực sự, truy vấn rất hữu ích để tìm ra độ dài mà chỉ mục nên được đặt. Đã sử dụng điều này một số lần để xác định độ dài tốt nhất cho một chỉ mục. Cảm ơn bạn đã chia sẻ!
Niraj Kumar

1
Có một lỗi nhỏ trong truy vấn tiện dụng của bạn để đo độ dài thực sự của các chuỗi: bạn đang giả định rằng tất cả các chuỗi đều có mặt; nghĩa là tất cả chúng đều ở đó. Nếu một số là rỗng, nó sẽ loại bỏ các tính toán của bạn và báo cáo thiếu các chuỗi ngắn. Bạn muốn sử dụng count ([field_name]) thay vì count (*).
D Mac

Nó sẽ không sử dụng nhiều hơn chỉ mục "tiền tố" đầu tiên.
Rick James

28

Lỗi này có nghĩa là độ dài của chỉ mục indexhơn 1000 byte. MySQL và các công cụ lưu trữ có thể có hạn chế này. Tôi đã gặp lỗi tương tự trên MySQL 5.5 - 'Khóa được chỉ định quá dài; độ dài khóa tối đa là 3072 byte 'khi chạy tập lệnh này:

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 là nhiều byte và độ dài khóa được tính theo cách này - 500 * 3 * 6 = 9000 byte.

Nhưng lưu ý, truy vấn tiếp theo hoạt động!

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;

... bởi vì tôi đã sử dụng CHARSET = latin1, trong trường hợp này độ dài khóa là 500 * 6 = 3000 byte.


7
Cảm ơn bạn đã trả lời, điều này hoạt động, nhưng với chi phí từ bỏ bộ ký tự utf8. Hạn chế này bằng cách nào đó có thể được khắc phục (tôi có toàn quyền truy cập máy chủ), nó có lý do chính đáng không?
CodeVirtuoso

4
Đây là một lời khuyên khủng khiếp, hãy tìm hiểu bảng mã là gì và điều này sẽ gây ra các vấn đề lớn trong tương lai như thế nào. Cơ sở dữ liệu bộ ký tự hỗn hợp không chỉ gây ra sự cố khi sử dụng các phép nối hoặc phép chọn con, nó còn đặt dữ liệu của bạn ở định dạng không chuẩn hóa và gần như không thể sửa được sau này.
Geoffrey

16

Tôi gặp sự cố này và đã giải quyết bằng cách sau:

Nguyên nhân

Có một lỗi đã biết với MySQL liên quan đến MyISAM, bộ ký tự UTF8 và các chỉ mục mà bạn có thể kiểm tra tại đây.

Độ phân giải

  • Đảm bảo MySQL được định cấu hình với công cụ lưu trữ InnoDB.

  • Thay đổi công cụ lưu trữ được sử dụng theo mặc định để các bảng mới sẽ luôn được tạo một cách thích hợp:

    set GLOBAL storage_engine='InnoDb';

  • Đối với MySQL 5.6 trở lên, hãy sử dụng như sau:

    SET GLOBAL default_storage_engine = 'InnoDB';

  • Và cuối cùng hãy đảm bảo rằng bạn đang làm theo các hướng dẫn được cung cấp trong Di chuyển sang MySQL .

Tài liệu tham khảo


Trong hầu hết các trường hợp, chúng tôi quên định cấu hình công cụ lưu trữ là 'InnoDB'. Câu trả lời sau đây là câu trả lời đơn giản nhất và có lẽ sẽ giải quyết được vấn đề cho hầu hết người dùng ở đây. Cảm ơn.
Frederiko Cesar

10

chạy truy vấn này trước khi tạo hoặc thay đổi bảng.

SET @@global.innodb_large_prefix = 1;

điều này sẽ đặt độ dài khóa tối đa thành 3072 byte


3

Giới hạn kích thước chỉ mục này dường như lớn hơn trên các bản dựng 64 bit của MySQL.

Tôi đã gặp phải hạn chế này khi cố gắng kết xuất cơ sở dữ liệu nhà phát triển của chúng tôi và tải nó trên một VMWare Virt cục bộ. Cuối cùng, tôi nhận ra rằng máy chủ nhà phát triển từ xa là 64 bit và tôi đã tạo một phiên bản 32 bit. Tôi vừa tạo một bản 64 bit và tôi có thể tải cơ sở dữ liệu cục bộ.


2

Tôi vừa thực hiện bỏ qua lỗi này bằng cách chỉ thay đổi các giá trị của "độ dài" trong cơ sở dữ liệu gốc thành tổng số khoảng "1000" bằng cách thay đổi cấu trúc của nó, và sau đó xuất giống nhau sang máy chủ. :)


0

Tôi đã gặp phải vấn đề tương tự, được sử dụng truy vấn bên dưới để giải quyết nó.

Trong khi tạo DB, bạn có thể sử dụng mã hóa utf-8

ví dụ. create database my_db character set utf8 collate utf8mb4;

CHỈNH SỬA: (Xem xét đề xuất từ ​​các bình luận) Đã thay đổi utf8_bin thành utf8mb4


2
Điều này đã chỉ cho tôi đi đúng hướng. Đối với tôi, tôi đã phải thay đổi đối chiếu của tôi để: utf8_general_ci
Ian Newland

2
Đây KHÔNG phải là hướng đi đúng, đừng làm điều này! MySQL utf8thì KHÔNG utf8, nó là một định dạng độc quyền bị lỗi mà lẽ ra không bao giờ có kết quả. utf8mb4là đúng utf8và là mặc định được khuyến nghị để được utf8hỗ trợ thích hợp .
Geoffrey

utf8mb4là mã hóa chính xác để sử dụng trên mysql chứ không phảiutf8
alok

Đó chỉ là một ví dụ - dù sao thì tôi cũng đã cập nhật câu trả lời.
Sudhir Dhumal
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.