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


562

Khi tôi thực hiện lệnh sau:

ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);

Tôi nhận được thông báo lỗi này:

#1071 - Specified key was too long; max key length is 767 bytes

Thông tin về cột1 và cột2:

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

Tôi nghĩ rằng varchar(20)chỉ cần 21 byte trong khi varchar(500)chỉ yêu cầu 501 byte. Vậy tổng số byte là 522, nhỏ hơn 767. Vậy tại sao tôi lại nhận được thông báo lỗi?

#1071 - Specified key was too long; max key length is 767 bytes

5
Bởi vì nó không phải là 520 byte, mà là 2080 byte, vượt xa 767 byte, bạn có thể thực hiện cột 1 varchar (20) và cột2 varchar (170). nếu bạn muốn một ký tự / byte tương đương, hãy sử dụng latin1
Rahly

3
Tôi nghĩ rằng tính toán của bạn là một chút sai ở đây. mysql sử dụng 1 hoặc 2 byte bổ sung để ghi lại độ dài giá trị: 1 byte nếu độ dài tối đa của cột là 255 byte hoặc ít hơn, 2 nếu nó dài hơn 255 byte. mã hóa utf8_general_ci cần 3 byte cho mỗi ký tự, vì vậy varchar (20) sử dụng 61 byte, varchar (500) sử dụng 1502 byte trong tổng số 1563 byte
thiếuovic10

3
mysql chọn maxlen, character_set_name từ information_schema.character_sets trong đó character_set_name trong ('latin1', 'utf8', 'utf8mb4'); maxlen | character_set_name ------ | ------------------- 1 | Latin1 ------ | ------------------- 3 | utf8 ------ | ------------------- 4 | utf8mb4
thiếuovic10

15
'nếu bạn muốn một ký tự / byte tương đương, hãy sử dụng latin1' Vui lòng không làm điều này . Latin1 thực sự, thực sự hút. Bạn sẽ phải hối hận vì điều này.
Stijn de Witt

Tham khảo stackoverflow.com/a/52778785/2137210 để biết giải pháp
Pratik

Câu trả lời:


490

767 byte là giới hạn tiền tố đã nêu cho các bảng InnoDB trong phiên bản MySQL 5.6 (và các phiên bản trước). Nó dài 1.000 byte cho các bảng MyISAM. Trong phiên bản MySQL 5.7 trở lên, giới hạn này đã được tăng lên 3072 byte.

Bạn cũng phải lưu ý rằng nếu bạn đặt chỉ mục trên trường char hoặc varchar lớn được mã hóa utf8mb4, bạn phải chia độ dài tiền tố chỉ mục tối đa là 767 byte (hoặc 3072 byte) cho 4 kết quả là 191. Điều này là do độ dài tối đa của một ký tự utf8mb4 là bốn byte. Đối với một ký tự utf8, nó sẽ là ba byte dẫn đến độ dài tiền tố chỉ mục tối đa là 254.

Một tùy chọn bạn có là chỉ đặt giới hạn thấp hơn trên các trường VARCHAR của bạn.

Một tùy chọn khác (theo phản hồi cho vấn đề này ) là lấy tập hợp con của cột thay vì toàn bộ số tiền, nghĩa là:

ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );

Tinh chỉnh khi bạn cần lấy chìa khóa để áp dụng, nhưng tôi tự hỏi liệu có đáng để xem lại mô hình dữ liệu của mình về thực thể này để xem liệu có những cải tiến nào cho phép bạn thực hiện các quy tắc kinh doanh dự định mà không đạt giới hạn MySQL không.


4
Để áp dụng bằng cách chỉ định một tập hợp con của cột chứ không phải toàn bộ số tiền. Một giải pháp tốt.
Steven

204
Điều này không giải thích tại sao các trường nằm dưới giới hạn độ dài vượt quá giới hạn độ dài ...
Cerin

6
Tôi đã thử chỉnh sửa thông tin @Cerin bị thiếu ở trên, điều này rõ ràng cũng bị người khác coi là mất tích, nhưng nó bị từ chối vì phù hợp hơn như một nhận xét. Đối với những người cố gắng hiểu tại sao 500 + 20> 767, hãy xem nhận xét của Stefan Endrullis về câu trả lời của Julien.
Letharion

8
Đây có thể là một vấn đề. Ví dụ: Tôi có tên trường (255) và thêm duy nhất vào trường này ở tên (191) khi tôi đang sử dụng utf8mb4. Nếu tôi có người dùng của mình, hãy thêm tên của họ bằng 'IJUE3ump5fiUuCi16jSofYS234MLschW4wsIktKiBrTPT Nó sẽ vượt qua xác nhận không bị mắc kẹt tại mục trùng lặp.
vee

28
Giới hạn chỉ mục là 767 byte , không phải ký tự. Và vì bộ utf8mb4ký tự của Mysql (mà phần còn lại của thế giới gọi là utf8) cần (nhiều nhất) 4 byte cho mỗi ký tự, bạn chỉ có thể lập chỉ mục tối đa VARCHAR(191). Bộ utf8ký tự của Mysql (mà phần còn lại của thế giới gọi là bị hỏng) cần tối đa 3 byte cho mỗi ký tự, vì vậy nếu bạn đang sử dụng (mà bạn không nên ), bạn có thể lập chỉ mục lên tớiVARCHAR(255)
Stijn de Witt

404

Nếu bất cứ ai gặp vấn đề với INNODB / Utf-8 đang cố gắng đặt UNIQUEchỉ mục trên một VARCHAR(256)trường, hãy chuyển nó sang VARCHAR(255). Có vẻ như 255 là giới hạn.


247
Số lượng ký tự được phép chỉ phụ thuộc vào bộ ký tự của bạn. UTF8 có thể sử dụng tối đa 3 byte cho mỗi ký tự, utf8mb4 tối đa 4 byte và latin1 chỉ 1 byte. Do đó, đối với utf8, độ dài khóa của bạn bị giới hạn ở 255 ký tự 3*255 = 765 < 767.
Stefan Endrullis

10
Điều này đã tiết kiệm cho tôi rất nhiều thất vọng - cảm ơn bạn. Nó phải là câu trả lời IMO được chấp nhận, xem xét người dùng đang sử dụng InnoDB bằng cách tuyên bố họ đạt giới hạn 767b.
TheCarver

8
NHƯ Stefan Endrullis đã nêu, nó phụ thuộc vào bộ ký tự. Nếu bạn sử dụng UTF8 sử ​​dụng 3 byte: 255x3 = 765 thấp hơn giới hạn 767, trong khi 256x3 = 768 thì cao hơn. Nhưng nếu bạn sử dụng UTF8mb4 thì 255 * 4 = 1020, vì vậy đây không phải là giải pháp thực sự
bernhardh

25
Câu trả lời này là chính xác. Tuy nhiên, nếu 255 thực sự làm việc cho bạn, điều đó có nghĩa là bạn đang sử dụng Mysql utf8, điều không may bị hỏng. Nó chỉ có thể mã hóa các ký tự trong mặt phẳng đa ngôn ngữ cơ bản. Bạn sẽ gặp vấn đề với các nhân vật nằm ngoài điều đó. Ví dụ, những ký tự Emoji mà họ đã thêm nằm ngoài nó tôi nghĩ. Vì vậy, thay vì chuyển sang VARCHAR(255), hãy chuyển sang VARCHAR(191) chuyển mã hóa sang utf8mb4(thực ra chỉ là utf8, nhưng MySql muốn giữ lại. Compat).
Stijn de Witt

1
Không hữu ích cho các ràng buộc duy nhất nhiều màu của tôi. Nó cũng sẽ không hoạt động đối với các ràng buộc duy nhất nhiều màu của OP. (sẽ cung cấp cho nó tổng kích thước 825 byte)
Barett

351

Khi bạn đạt đến giới hạn. Đặt như sau.

  • NỔI BẬT utf8 VARCHAR(255)
  • NỔI BẬT utf8mb4 VARCHAR(191)

34
Đây là câu trả lời tốt nhất. Đơn giản, đi thẳng vào vấn đề và cũng bao gồm utf8mb4giới hạn (là mã hóa được sử dụng nhiều nhất cho các cơ sở dữ liệu mới hơn, vì nó chấp nhận biểu tượng cảm xúc / vv).
Claudio Holanda

10
bởi vì 767/4 ~ = 191 và 767/3 ~ = 255
Kế toán م

11
đặt nó ở đâu và như thế nào?
Dinesh Nắng

1
Có, chỉ định ENGINE=InnoDB DEFAULT CHARSET=utf8ở cuối CREATE TABLEcâu lệnh tôi có thể có VARCHAR(255)khóa chính. Cảm ơn.
xonya

1
ai đó cho anh ta +1 để vượt quá giới hạn 191 :)
cagcak

147

MySQL giả định trường hợp xấu nhất cho số byte trên mỗi ký tự trong chuỗi. Đối với mã hóa 'utf8' của MySQL, đó là 3 byte cho mỗi ký tự vì mã hóa đó không cho phép các ký tự vượt quá U+FFFF. Đối với mã hóa 'utf8mb4' của MySQL, đó là 4 byte cho mỗi ký tự, vì đó là thứ mà MySQL gọi là UTF-8 thực tế.

Vì vậy, giả sử bạn đang sử dụng 'utf8', cột đầu tiên của bạn sẽ lấy 60 byte chỉ mục và 1500 thứ hai của bạn.


4
- Điều đó có lẽ có nghĩa là khi sử dụng utf8mb4, tôi cần đặt chúng thành (tối đa) 191 là 191 * 4 = 764 <767
Isaac

2
@Isaac Vâng, chính xác,
morganwahl

2
Tôi nghĩ rằng đây có thể là câu trả lời đúng, nhưng bạn có thể giải thích những gì người ta cần làm để khắc phục vấn đề như vậy không? Ít nhất là cho các noobs MySQL như tôi?
Adam Grant

Không có cách nào để vượt qua giới hạn chỉ số này. Câu hỏi ở đây là về những ràng buộc độc đáo; đối với những người đó, bạn có thể có một cột văn bản có độ dài không giới hạn và một cột khác nơi bạn lưu trữ một hàm băm (như MD5) của văn bản đó và sử dụng cột băm cho ràng buộc duy nhất của bạn. Bạn sẽ phải đảm bảo chương trình của bạn luôn cập nhật các bảng băm khi nó thay đổi văn bản, nhưng có nhiều cách khác nhau để xử lý vấn đề đó mà không gặp quá nhiều khó khăn. Thẳng thắn mà nói, MySQL nên tự thực hiện một việc như vậy để bạn không phải làm vậy; Tôi sẽ không ngạc nhiên nếu MariaDB có thứ gì đó giống như vậy.
morganwahl

Một chỉ số duy nhất trên một cột varchar rất dài là rất hiếm. Hãy suy nghĩ về lý do tại sao bạn cần nó bởi vì nó có thể là một vấn đề thiết kế. Nếu bạn chỉ muốn một chỉ mục trên đó để tìm kiếm, hãy xem xét một số trường 'từ khóa phù hợp với 191 ký tự hoặc chia văn bản thành mô tả ngắn và văn bản dài / hoàn chỉnh, v.v. Hoặc nếu bạn thực sự cần tìm kiếm toàn văn bản, hãy xem xét sử dụng phần mềm chuyên dụng cho nó, chẳng hạn như Apache Lucene.
Stijn de Witt

56

chạy truy vấn này trước khi truy vấn của bạn:

SET @@global.innodb_large_prefix = 1;

điều này sẽ tăng giới hạn đến 3072 bytes.


4
Có bất kỳ nhược điểm nào khi thay đổi thành innodb_large_prefix trên toàn cầu không? Và đó là DB "toàn cầu" hay tất cả các DB HOÀN TOÀN TOÀN CẦU?
SciPhi

5
Chỉ áp dụng khi sử dụng các định dạng hàng không chuẩn. Xem dev.mysql.com/doc/refman/5.5/vi/ . Cụ thể, 'Kích hoạt tùy chọn này để cho phép tiền tố khóa chỉ mục dài hơn 767 byte (tối đa 3072 byte), đối với các bảng InnoDB sử dụng các định dạng hàng NĂNG ĐỘNG và NĂNG LỰC.' Định dạng hàng mặc định không bị ảnh hưởng.
Chris Lear

5
Điều này làm việc tốt với tôi - chi tiết hơn và hướng dẫn có thể được tìm thấy ở đây: Mechanicalics.flite.com/blog/2014/07/29/
mẹo

1
Chúng ta có cần khởi động lại máy chủ mysql sau này không?
SimpleGuy

7
Chi tiết thiếu quan trọng từ câu trả lời này. innodb_file_formatphải BARRACUDAvà Ở cấp độ bảng bạn phải sử dụng ROW_FORMAT=DYNAMIChoặc ROW_FORMAT=COMPRESSED. Xem bình luận của @ cwd ở trên với hướng dẫn.
q0rban

46

Giải pháp cho khung làm việc

Theo tài liệu của Laravel 5.4. * ; Bạn phải đặt độ dài chuỗi mặc định bên trong bootphương thức của app/Providers/AppServiceProvider.phptệp như sau:

use Illuminate\Support\Facades\Schema;

public function boot() 
{
    Schema::defaultStringLength(191); 
}

Giải thích về sửa chữa này, được đưa ra bởi tài liệu của Laravel 5.4. * :

Laravel sử dụng bộ utf8mb4ký tự theo mặc định, bao gồm hỗ trợ lưu trữ "biểu tượng cảm xúc" trong cơ sở dữ liệu. Nếu bạn đang chạy phiên bản MySQL cũ hơn bản phát hành 5.7.7 hoặc MariaDB cũ hơn bản phát hành 10.2.2, bạn có thể cần phải định cấu hình thủ công độ dài chuỗi mặc định được tạo bởi di chuyển để MySQL tạo chỉ mục cho chúng. Bạn có thể cấu hình điều này bằng cách gọi Schema::defaultStringLengthphương thức trong của bạn AppServiceProvider.

Ngoài ra, bạn có thể kích hoạt innodb_large_prefixtùy chọn cho cơ sở dữ liệu của bạn. Tham khảo tài liệu của cơ sở dữ liệu của bạn để biết hướng dẫn về cách bật tùy chọn này.


10
@BojanPetkovic tốt Tôi vừa xuất phát từ một vấn đề của Laravel, và câu trả lời này thực sự đã giải quyết vấn đề của tôi.
mkmnstr

Câu trả lời này là tuyệt vời. Nhưng có ai biết, tại sao chính xác điều này hoạt động?
UeliDeSchwert

1
Tài liệu @Bulk Laravel đưa ra lời giải thích trong tiêu đề "Độ dài chỉ mục & MySQL / MariaDB" laravel.com/docs/5.4/migations#creating-indexes
Ali Shaukat

1
@BOB Tôi đã cập nhật câu trả lời. Tôi đã thêm lời giải thích được đưa ra bởi tài liệu laravel cho sửa chữa này.
Ali Shaukat

39

Bạn đang sử dụng mã hóa ký tự nào? Một số bộ ký tự (như UTF-16, et cetera) sử dụng nhiều hơn một byte cho mỗi ký tự.


8
Nếu là UTF8, một ký tự có thể sử dụng tối đa 4 byte, để cột 20 ký tự là 20 * 4 + 1byte và cột 500 char là 500 * 4 + 2byte
Thanatos

5
Đối với những gì nó có giá trị, tôi chỉ có cùng một vấn đề và chuyển từ utf8_general_ci sang utf8_unicode_ci đã giải quyết vấn đề cho tôi. Tôi không biết tại sao mặc dù :(
Andresch Serj

11
Đối với một VARCHAR(256)cột có UNIQUEchỉ mục, việc thay đổi đối chiếu không có tác dụng đối với tôi, giống như đối với @Andresch. Tuy nhiên, việc giảm độ dài từ 256 xuống 255 đã giải quyết được nó. Tôi không hiểu tại sao, vì 767 / tối đa 4 byte cho mỗi ký tự sẽ mang lại tối đa là 191?
Arjan

10
255*3 = 765; 256*3 = 768. Có vẻ như máy chủ của bạn đã giả định 3 byte cho mỗi ký tự, @Arjan
Amber

21
@Greg: Bạn đã đúng, nhưng điều này cần được xây dựng: Bản thân UTF-8 sử dụng 1 byte4 byte cho mỗi điểm mã. "Bộ ký tự" của MySQL (mã hóa thực sự) có một bộ ký tự gọi là "utf8" có thể mã hóa một số UTF-8 và sử dụng 1 byte3 byte cho mỗi điểm mã và không có khả năng mã hóa các điểm mã bên ngoài BMP. Nó cũng bao gồm một bộ ký tự khác gọi là "utf8mb4", sử dụng 1 byte4 byte cho mỗi điểm mã và có khả năng mã hóa tất cả các điểm mã Unicode. (utf8mb4 là UTF-8, utf8 là phiên bản kỳ lạ của UTF-8.)
Thanatos

28

Tôi nghĩ varchar (20) chỉ yêu cầu 21 byte trong khi varchar (500) chỉ yêu cầu 501 byte. Vậy tổng số byte là 522, nhỏ hơn 767. Vậy tại sao tôi lại nhận được thông báo lỗi?

UTF8 yêu cầu 3 byte cho mỗi ký tự để lưu trữ chuỗi, vì vậy trong trường hợp của bạn, 20 + 500 ký tự = 20 * 3 + 500 * 3 = 1560 byte nhiều hơn mức 767 byte cho phép .

Giới hạn cho UTF8 là 767/3 = 255 ký tự , đối với UTF8mb4 sử dụng 4 byte cho mỗi ký tự là 767/4 = 191 ký tự.


Có hai giải pháp cho vấn đề này nếu bạn cần sử dụng cột dài hơn giới hạn:

  1. Sử dụng mã hóa "rẻ hơn" (loại yêu cầu ít byte hơn cho mỗi ký tự)
    Trong trường hợp của tôi, tôi cần thêm Chỉ mục duy nhất trên cột chứa chuỗi bài viết SEO, vì tôi chỉ sử dụng các [A-z0-9\-]ký tự cho SEO, tôi latin1_general_cichỉ sử dụng một byte cho mỗi ký tự và vì vậy cột có thể có độ dài 767 byte.
  2. Tạo hàm băm từ cột của bạn và chỉ sử dụng chỉ mục duy nhất trên đó
    Tùy chọn khác đối với tôi là tạo một cột khác lưu trữ hàm băm của SEO, cột này sẽ có UNIQUEkhóa để đảm bảo giá trị SEO là duy nhất. Tôi cũng sẽ thêm KEYchỉ mục vào cột SEO ban đầu để tăng tốc độ tra cứu.

Điều này đã lừa Tôi đã có varchar (256) và tôi đã phải đổi nó thành varchar (250).
MaRmAR

25

Câu trả lời về lý do tại sao bạn nhận được thông báo lỗi đã được trả lời bởi nhiều người dùng ở đây. Câu trả lời của tôi là về cách sửa chữa và sử dụng nó như nó được.

Tham khảo từ liên kết này .

  1. Mở máy khách MySQL (hoặc máy khách MariaDB). Nó là một công cụ dòng lệnh.
  2. Nó sẽ hỏi mật khẩu của bạn, nhập mật khẩu chính xác của bạn.
  3. Chọn cơ sở dữ liệu của bạn bằng cách sử dụng lệnh này use my_database_name;

Cơ sở dữ liệu đã thay đổi

  1. set global innodb_large_prefix=on;

Truy vấn OK, 0 hàng bị ảnh hưởng (0,00 giây)

  1. set global innodb_file_format=Barracuda;

Truy vấn OK, 0 hàng bị ảnh hưởng (0,02 giây)

  1. Truy cập cơ sở dữ liệu của bạn trên phpMyAdmin hoặc một cái gì đó tương tự để quản lý dễ dàng. > Chọn cơ sở dữ liệu> Xem cấu trúc bảng > Chuyển đến tab Hoạt động . > Thay đổi ROW_FORMAT thành NĂNG ĐỘNG và lưu các thay đổi.
  2. Chuyển đến tab cấu trúc của bảng > Nhấp vào nút Unique .
  3. Làm xong. Bây giờ nó không có lỗi.

Vấn đề của sửa lỗi này là nếu bạn xuất db sang máy chủ khác (ví dụ từ localhost sang máy chủ thực) và bạn không thể sử dụng dòng lệnh MySQL trong máy chủ đó. Bạn không thể làm cho nó hoạt động ở đó.


nếu bạn vẫn gặp lỗi sau khi nhập truy vấn ở trên, hãy thử truy cập "phpmyadmin"> đặt "đối chiếu" theo sở thích của bạn (đối với tôi, tôi sử dụng "utf8_general_ci")> nhấp vào áp dụng (ngay cả khi đã là utf8)
Anthony Kal

Tôi không sử dụng một công cụ làm việc theo hướng dẫn của bạn, nhưng tôi vẫn bỏ phiếu để cố gắng giúp mọi người thực sự khắc phục vấn đề. Có những lời giải thích vô tận về những gì gây ra vấn đề, nhưng đáng chú ý rất ít về cách thực sự giải quyết nó.
Teekin

20
Specified key was too long; max key length is 767 bytes

Bạn nhận được thông báo đó vì 1 byte chỉ bằng 1 ký tự nếu bạn sử dụng bộ latin-1ký tự. Nếu bạn sử dụng utf8, mỗi ký tự sẽ được coi là 3 byte khi xác định cột khóa của bạn. Nếu bạn sử dụng utf8mb4, mỗi ký tự sẽ được coi là 4 byte khi xác định cột khóa của bạn. Do đó, bạn cần nhân giới hạn ký tự của trường khóa của mình với, 1, 3 hoặc 4 (trong ví dụ của tôi) để xác định số byte mà trường khóa đang cố gắng cho phép. Nếu bạn đang sử dụng uft8mb4, bạn chỉ có thể xác định 191 ký tự cho trường gốc, InnoDB, khóa chính. Chỉ cần không vi phạm 767 byte.


17

Thay thế utf8mb4bằng utf8trong tập tin nhập khẩu của bạn.

nhập mô tả hình ảnh ở đây


Tại sao chính xác một người nên làm điều đó? Ngoài ra, nếu điều này có bất kỳ nhược điểm nào (mà tôi giả sử), bạn nên đề cập đến điều này
Nico Haase

16

bạn có thể thêm một cột trong md5 của các cột dài


Lưu ý rằng điều này sẽ không cho phép bạn thực hiện quét phạm vi trên các cột này. Độ dài tiền tố trên VARCHAR sẽ cho phép bạn giữ đặc điểm này, đồng thời gây ra các kết quả trùng khớp có thể có trong chỉ mục (và quét và tra cứu hàng để loại bỏ chúng) (xem câu trả lời được chấp nhận). (Đây thực chất là một chỉ số băm được triển khai thủ công, đáng buồn là MysQL không hỗ trợ các bảng InnoDB.)
Thanatos

Tôi không thể sử dụng các chỉ số tiền tố vì tôi cần duy trì khả năng tương thích với H2 cho mục đích thử nghiệm và được tìm thấy bằng cách sử dụng cột băm hoạt động tốt. Tôi sẽ mạnh mẽ khuyên bạn sử dụng một chức năng chống va chạm như SHA1 thay vì MD5 để ngăn chặn người dùng nguy hiểm từ việc tạo va chạm. Xung đột chỉ mục có thể bị khai thác để rò rỉ dữ liệu nếu một trong các truy vấn của bạn chỉ kiểm tra giá trị băm chứ không phải giá trị cột đầy đủ.
Tiểu Mike

13

Chúng tôi đã gặp phải sự cố này khi cố gắng thêm chỉ mục UNIQUE vào trường VARCHAR (255) bằng utf8mb4. Mặc dù vấn đề đã được nêu rõ ở đây rồi, tôi muốn thêm một số lời khuyên thiết thực cho cách chúng tôi tìm ra vấn đề này và giải quyết nó.

Khi sử dụng utf8mb4, các ký tự được tính là 4 byte, trong khi dưới utf8, chúng có thể là 3 byte. Cơ sở dữ liệu InnoDB có một giới hạn là các chỉ mục chỉ có thể chứa 767 byte. Vì vậy, khi sử dụng utf8, bạn có thể lưu trữ 255 ký tự (767/3 = 255), nhưng sử dụng utf8mb4, bạn chỉ có thể lưu trữ 191 ký tự (767/4 = 191).

Bạn hoàn toàn có thể thêm các chỉ mục thông thường cho VARCHAR(255)các trường bằng utf8mb4, nhưng điều xảy ra là kích thước chỉ mục được tự động cắt ở mức 191 ký tự - như unique_keyở đây:

Ảnh chụp màn hình Sequel Pro hiển thị chỉ mục bị cắt ở mức 191 ký tự

Điều này là tốt, bởi vì các chỉ mục thông thường chỉ được sử dụng để giúp MySQL tìm kiếm thông qua dữ liệu của bạn nhanh hơn. Toàn bộ lĩnh vực không cần phải được lập chỉ mục.

Vì vậy, tại sao MySQL tự động cắt chỉ mục cho các chỉ mục thông thường, nhưng lại đưa ra một lỗi rõ ràng khi cố gắng làm điều đó cho các chỉ mục duy nhất? Chà, để MySQL có thể tìm ra nếu giá trị được chèn hoặc cập nhật đã tồn tại, nó cần phải thực sự lập chỉ mục cho toàn bộ giá trị và không chỉ là một phần của nó.

Vào cuối ngày, nếu bạn muốn có một chỉ mục duy nhất trên một trường, toàn bộ nội dung của trường phải phù hợp với chỉ mục. Đối với utf8mb4, điều này có nghĩa là giảm độ dài trường VARCHAR của bạn xuống còn 191 ký tự hoặc ít hơn. Nếu bạn không cần utf8mb4 cho bảng hoặc trường đó, bạn có thể thả nó trở lại utf8 và có thể giữ các trường có độ dài 255 của bạn.


11

Đây là câu trả lời ban đầu của tôi:

Tôi chỉ cần bỏ cơ sở dữ liệu và tạo lại như thế này và lỗi đã biến mất:

drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;

Tuy nhiên, nó không hoạt động cho tất cả các trường hợp.

Đây thực sự là một vấn đề của việc sử dụng các chỉ mục trên các cột VARCHAR với bộ ký tự utf8(hoặc utf8mb4), với các cột VARCHAR có nhiều độ dài ký tự nhất định. Trong trường hợp utf8mb4, độ dài nhất định là 191.

Vui lòng tham khảo phần Chỉ mục dài trong bài viết này để biết thêm thông tin về cách sử dụng các chỉ mục dài trong cơ sở dữ liệu MySQL: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database- ký tự-set-to-utf8mb4


Đã giải quyết vấn đề cho thiết lập openmeetings (BTW bạn đã lưu đêm của tôi :-)
Ludo

11

5 cách giải quyết:

Giới hạn đã được nâng lên trong 5.7.7 (MariaDB 10.2.2?). Và nó có thể được tăng lên với một số công việc trong 5.6 (10.1).

Nếu bạn đang đạt giới hạn vì cố gắng sử dụng CHARACTER SET utf8mb4. Sau đó thực hiện một trong các thao tác sau (mỗi cách có một nhược điểm) để tránh lỗi:

  Upgrade to 5.7.7 for 3072 byte limit -- your cloud may not provide this;
  Change 255 to 191 on the VARCHAR -- you lose any values longer than 191 characters (unlikely?);
  ALTER .. CONVERT TO utf8 -- you lose Emoji and some of Chinese;
  Use a "prefix" index -- you lose some of the performance benefits.
  Or... Stay with older version but perform 4 steps to raise the limit to 3072 bytes:

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=1;
SET GLOBAL innodb_large_prefix=1;
logout & login (to get the global values);
ALTER TABLE tbl ROW_FORMAT=DYNAMIC;  -- (or COMPRESSED)

- http://mysql.rjweb.org/doc.php/limits#767_limit_in_innodb_indexes


10

Tôi đã sửa vấn đề này với:

varchar(200) 

thay thế bằng

varchar(191)

tất cả các varchar có hơn 200 thay thế chúng bằng 191 hoặc đặt chúng thành văn bản.


Đây là những gì làm việc cho tôi quá. Tôi đã có một varchar (250), nhưng dữ liệu không bao giờ dài. Thay đổi nó thành varchar (100). Cảm ơn ý tưởng :)
Arun Basil Lal

7

Tôi đã thực hiện một số tìm kiếm về chủ đề này cuối cùng đã có một số thay đổi tùy chỉnh

Đối với bàn làm việc MySQL 6.3.7 Phiên bản pha đồ ​​họa có sẵn

  1. Bắt đầu Workbench và chọn kết nối.
  2. Đi đến quản lý hoặc sơ thẩm và chọn Tệp tùy chọn.
  3. Nếu Workbench yêu cầu bạn cho phép đọc tệp cấu hình và sau đó cho phép nó bằng cách nhấn OK hai lần.
  4. Tại trung tâm nơi tùy chọn Quản trị viên cửa sổ tệp đến.
  5. Chuyển đến tab InnoDB và kiểm tra innodb_large_prefix nếu nó không được kiểm tra trong phần Chung.
  6. đặt giá trị tùy chọn innodb_default_row_format thành NĂNG ĐỘNG.

Đối với các Phiên bản bên dưới 6.3.7, các tùy chọn trực tiếp không khả dụng, vì vậy cần phải đi với dấu nhắc lệnh

  1. Bắt đầu CMD với tư cách quản trị viên.
  2. Chuyển đến giám đốc nơi cài đặt máy chủ mysql Hầu hết các trường hợp tại "C: \ Program Files \ MySQL \ MySQL Server 5.7 \ bin" vì vậy lệnh là "cd \" "cd Chương trình tập tin \ MySQL \ MySQL Server 5.7 \ bin".
  3. Bây giờ Chạy lệnh mysql -u userName -p cơ sở dữ liệu Bây giờ nó yêu cầu mật khẩu của người dùng tương ứng. Cung cấp mật khẩu và nhập vào dấu nhắc mysql.
  4. Chúng ta phải thiết lập một số cài đặt toàn cục, nhập từng lệnh bên dưới từng bộ một innodb_large_prefix = on; đặt innodb_file_format = barracuda toàn cầu; đặt innodb_file_per_table toàn cầu = true;
  5. Bây giờ, cuối cùng chúng ta phải thay đổi ROW_FORMAT của bảng được yêu cầu theo mặc định COMPACT của nó, chúng ta phải đặt nó thành NĂNG ĐỘNG.
  6. sử dụng lệnh sau đây thay đổi bảng tên_bảng ROW_FORMAT = DYNAMIC;
  7. Làm xong

Tôi không thể tìm thấy điều này: 6. đặt giá trị tùy chọn innodb_default_row_format thành NĂNG ĐỘNG.
Adrian Cid Almaguer

nếu tôi sử dụng, set global innodb_default_row_format = DYNAMIC;tôi thấy thông báo này: ERROR 1193 (HY000): Biến hệ thống không xác định 'innodb_default_row_format'
Adrian Cid Almaguer

Làm thế nào bạn có lỗi từ bàn làm việc đến cmd? Tôi đã làm nó từ bàn làm việc nó có tùy chọn trực tiếp.
Abhishek

Tôi làm điều đó từ CMD vì tôi không thấy tùy chọn trong Workbench
Adrian Cid Almaguer

1
Tôi thấy vấn đề var này đã được giới thiệu trong v5.7.9 và tôi có phiên bản v5.6.33, cảm ơn
Adrian Cid Almaguer

7

Đối với ấu trùng 5.7 đến 6.0

Các bước để làm theo

  1. Tới App\Providers\AppServiceProvider.php.
  2. Thêm điều này để cung cấp use Illuminate\Support\Facades\Schema;trong đầu.
  3. Bên trong chức năng Boot Thêm cái này Schema::defaultStringLength(191);

đó là tất cả, thưởng thức.


Làm việc cho Laravel 5,8 quá.
Neo

Đây là một giải pháp tồi. Lý do: các chỉ mục không có nghĩa là dài vô tận. Khi bạn áp dụng một chỉ mục duy nhất cho một cái gì đó, bạn muốn chỉ mục được cố định - trong hầu hết thời gian. Điều đó có nghĩa là bạn KHÔNG NÊN tạo ra những thứ emailđộc đáo, nhưng bạn nên băm email và làm cho nó trở nên độc đáo. Không giống như dữ liệu chuỗi thô, giá trị băm có chiều rộng cố định và có thể được lập chỉ mục và tạo thành duy nhất mà không gặp sự cố. Thay vì hiểu vấn đề, bạn đang truyền bá những thực tiễn khủng khiếp không mở rộng quy mô.
NB

5

thay đổi đối chiếu của bạn. Bạn có thể sử dụng utf8_general_ci hỗ trợ hầu hết tất cả


"Hầu như" là một gợi ý khá hay rằng đây không phải là một giải pháp lâu dài
Nico Haase

4

Chỉ cần thay đổi utf8mb4để utf8khi tạo bảng giải quyết vấn đề của tôi. Ví dụ: CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;đến CREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;.


4

Để khắc phục điều đó, điều này làm việc cho tôi như một nét duyên dáng.

ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;

Tôi xác nhận rằng vấn đề của tôi là đối chiếu cơ sở dữ liệu quá. Tôi không có bất kỳ lời giải thích cho nó. Tôi sử dụng mysql v5.6.32 và đối chiếu DB là utf8mb4_unicode_ci.
sân gỗ

2

Dựa trên cột được đưa ra dưới đây, 2 cột chuỗi biến đó đang sử dụng utf8_general_ciđối chiếu (bộ utf8ký tự được ngụ ý).

Trong MySQL, bộ utf8ký tự sử dụng tối đa 3 byte cho mỗi ký tự. Do đó, nó sẽ cần phân bổ 500 * 3 = 1500 byte, lớn hơn nhiều so với 767 byte mà MySQL cho phép. Đó là lý do tại sao bạn nhận được lỗi 1071 này.

Nói cách khác, bạn cần tính toán số ký tự dựa trên biểu diễn byte của bộ ký tự vì không phải mọi bộ ký tự là một biểu diễn byte đơn (như bạn đoán.) IE utf8trong MySQL sử dụng tối đa 3 byte cho mỗi ký tự, 767 / 3≈255 các ký tự và cho utf8mb4, đại diện tối đa 4 byte, 767 / 4≈191 ký tự.

Người ta cũng biết rằng MySQL

column1 varchar(20) utf8_general_ci
column2  varchar(500) utf8_general_ci

1

Tôi thấy truy vấn này hữu ích trong việc phát hiện cột nào có chỉ mục vi phạm độ dài tối đa:

SELECT
  c.TABLE_NAME As TableName,
  c.COLUMN_NAME AS ColumnName,
  c.DATA_TYPE AS DataType,
  c.CHARACTER_MAXIMUM_LENGTH AS ColumnLength,
  s.INDEX_NAME AS IndexName
FROM information_schema.COLUMNS AS c
INNER JOIN information_schema.statistics AS s
  ON s.table_name = c.TABLE_NAME
 AND s.COLUMN_NAME = c.COLUMN_NAME 
WHERE c.TABLE_SCHEMA = DATABASE()
  AND c.CHARACTER_MAXIMUM_LENGTH > 191 
  AND c.DATA_TYPE IN ('char', 'varchar', 'text')

1

Vui lòng kiểm tra nếu sql_modegiống

sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

nếu có, đổi thành

sql_mode=NO_ENGINE_SUBSTITUTION

HOẶC LÀ

khởi động lại máy chủ của bạn thay đổi tập tin my.cnf của bạn (đặt sau)

innodb_large_prefix=on

1

Trong trường hợp của tôi, tôi gặp vấn đề này khi sao lưu cơ sở dữ liệu bằng cách sử dụng các ký tự đầu ra / đầu vào chuyển hướng linux. Do đó, tôi thay đổi cú pháp như mô tả dưới đây. PS: sử dụng thiết bị đầu cuối linux hoặc mac.

Sao lưu (không có> chuyển hướng)

# mysqldump -u root -p databasename -r bkp.sql

Khôi phục (không có <redirect)

# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql

Lỗi "Khóa được chỉ định quá dài; độ dài khóa tối đa là 767 byte" đơn giản biến mất.


1

Độ dài chỉ mục & MySQL / MariaDB


Laravel sử dụng ký tự utf8mb4 được đặt theo mặc định, bao gồm hỗ trợ lưu trữ "biểu tượng cảm xúc" trong cơ sở dữ liệu. Nếu bạn đang chạy phiên bản MySQL cũ hơn bản phát hành 5.7.7 hoặc MariaDB cũ hơn bản phát hành 10.2.2, bạn có thể cần phải định cấu hình thủ công độ dài chuỗi mặc định được tạo bởi di chuyển để MySQL tạo chỉ mục cho chúng. Bạn có thể định cấu hình điều này bằng cách gọi phương thức Schema :: defaultStringL wavel trong AppServiceProvider của bạn :

use Illuminate\Support\Facades\Schema;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Schema::defaultStringLength(191);
}

Ngoài ra, bạn có thể bật tùy chọn innodb_large_prefix cho cơ sở dữ liệu của mình. Tham khảo tài liệu của cơ sở dữ liệu của bạn để biết hướng dẫn về cách bật tùy chọn này.

Tham khảo từ blog: https://www.scratchcode.io/specified-key-too-long-error-in-laravel/

Tham khảo từ tài liệu chính thức của laravel: https://laravel.com/docs/5.7/migations


0

Nếu bạn đang tạo một cái gì đó như:

CREATE TABLE IF NOT EXISTS your_table (
  id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
  name varchar(256) COLLATE utf8mb4_bin NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY name (name)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

nó phải giống như

CREATE TABLE IF NOT EXISTS your_table (
      id int(7) UNSIGNED NOT NULL AUTO_INCREMENT,
      name varchar(256) COLLATE utf8mb4_bin NOT NULL,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ROW_FORMAT=FIXED;

nhưng bạn cần kiểm tra tính duy nhất của cột đó từ mã hoặc thêm một cột mới dưới dạng MD5 hoặc SHA1 của cột varchar


3
Sau đó, khóa duy nhất của bạn bị mất. Tôi nghĩ rằng cách tốt hơn chỉ đơn giản là giảm độ dài của tên xuống còn 191.
vào

1
Tại sao phải kiểm tra tính duy nhất theo chương trình nếu đó là tính năng DB gốc?
Paweł Tomkiel 27/05/2015

0

Do giới hạn tiền tố, lỗi này sẽ xảy ra. 767 byte là giới hạn tiền tố đã nêu cho các bảng InnoDB trong các phiên bản MySQL trước 5.7. Nó dài 1.000 byte cho các bảng MyISAM. Trong phiên bản MySQL 5.7 trở lên, giới hạn này đã được tăng lên 3072 byte.

Chạy phần sau trên dịch vụ cung cấp cho bạn lỗi sẽ giải quyết vấn đề của bạn. Điều này phải được chạy trong MYSQL CLI.

SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;

-1

Thay đổi CHARSET của trường chỉ mục phàn nàn thành "latin1"
tức là ALTER TABLE tbl THAY ĐỔI myfield myfield varchar (600) CHARACTER SET latin1 DEFAULT NULL;
latin1 lấy một byte cho một ký tự thay vì bốn ký tự


5
Và nếu anh ta cố gắng chèn các ký tự không phải là latin1 thì sao?
Paweł Tomkiel

4
Đây là điều tồi tệ nhất để làm. Không bao giờ làm điều đó.
Sabas

1
trong trường hợp của tôi, nó nằm trên một cột lưu tên đường dẫn chỉ có các ký tự hex ... vì vậy đây có thể là một giải pháp cho ai đó. nó thực sự phụ thuộc vào những gì bạn muốn lưu trữ ...
codewandler

Tôi đã phải đánh giá thấp điều này, vì sử dụng latin1 không phải là một giải pháp trong năm 2016AD. Tôi vẫn còn gặp ác mộng khủng khiếp về thời gian chúng tôi phải chuyển đổi cơ sở dữ liệu từ latin1 sang utf8 vào năm 2007. Không đẹp. Ở tất cả.
Đặt cược vào

Tôi đã có cùng một vấn đề về một chỉ mục duy nhất trên hai cột nơi tôi lưu trữ địa chỉ bitcoin và ID giao dịch. Đây sẽ luôn là các ký tự ASCII, vì vậy tôi sẽ sử dụng latin1 trên các cột này. Nâng cao.
alexg

-2

Nếu bạn đã thay đổi innodb_log_file_sizegần đây, hãy thử khôi phục giá trị trước đó đã hoạt động.

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.