#1071-指定されたキーが長すぎました。キーの最大長は767バイトです
-
06-07-2019 - |
質問
次のコマンドを実行したとき:
ALTER TABLE `mytable` ADD UNIQUE (
`column1` ,
`column2`
);
このエラーメッセージが表示されました:
#1071 - Specified key was too long; max key length is 767 bytes
column1およびcolumn2に関する情報:
column1 varchar(20) utf8_general_ci
column2 varchar(500) utf8_general_ci
varchar(20)
に必要なのは21バイトだけですが、varchar(500)
に必要なのは501バイトだけです。合計バイト数は522で、767未満です。なぜエラーメッセージが表示されたのですか?
解決
767バイトは記載されているプレフィックスの制限 MySQLバージョン5.6(および以前のバージョン)のInnoDBテーブル。 MyISAMテーブルの長さは1,000バイトです。 MySQLバージョン5.7以降では、この制限は3072バイトに増加しました。
utf8mb4でエンコードされた大きなcharまたはvarcharフィールドにインデックスを設定する場合、767バイト(または3072バイト)の最大インデックスプレフィックス長を4で除算して191にする必要があることにも注意する必要があります。これは、utf8mb4文字の最大長が4バイトであるためです。 utf8文字の場合、3バイトになり、インデックスプレフィックスの最大長は254になります。
1つのオプションは、VARCHARフィールドに下限を設定することです。
別のオプション(この問題への応答による)は、全体ではなく列のサブセット、つまり:
ALTER TABLE `mytable` ADD UNIQUE ( column1(15), column2(200) );
適用するキーを取得する必要があるため微調整しますが、このエンティティに関するデータモデルを確認して、MySQLにアクセスせずに目的のビジネスルールを実装できる改善があるかどうかを確認する価値があるかどうか疑問に思います制限。
他のヒント
UNIQUE
フィールドにVARCHAR(256)
インデックスを配置しようとするINNODB / Utf-8に問題がある場合は、VARCHAR(255)
に切り替えます。 255が制限のようです。
制限に達したとき。以下を設定します。
- INNODB
utf8
VARCHAR(255)
- INNODB
utf8mb4
VARCHAR(191)
MySQLは、文字列内の文字ごとのバイト数が最悪の場合を想定しています。 MySQLの 'utf8'エンコーディングの場合、そのエンコーディングではU+FFFF
を超える文字は許可されないため、1文字あたり3バイトです。 MySQLの「utf8mb4」エンコーディングの場合、1文字あたり4バイトです。これは、MySQLが実際のUTF-8と呼んでいるものだからです。
「utf8」を使用していると仮定すると、最初の列には60バイトのインデックスが、2番目の列にはさらに1500バイトが必要です。
クエリの前にこのクエリを実行します:
SET @@global.innodb_large_prefix = 1;
これにより、制限が3072 bytes
に増加します。
使用している文字エンコーディングは何ですか?一部の文字セット(UTF-16など)は、文字ごとに複数バイトを使用します。
Laravelフレームワークのソリューション
Laravel 5.4。*ドキュメントに従って;次のように、boot
ファイルのapp/Providers/AppServiceProvider.php
メソッド内でデフォルトの文字列の長さを設定する必要があります。
use Illuminate\Support\Facades\Schema;
public function boot()
{
Schema::defaultStringLength(191);
}
Laravelは、デフォルトで
utf8mb4
文字セットを使用します。これには、<!> quot; emojis <!> quotの格納のサポートが含まれます。データベース内。 5.7.7リリースより古いバージョンのMySQLまたは10.2.2リリースより古いバージョンのMariaDBを実行している場合、MySQLがインデックスを作成するために、移行によって生成されるデフォルトの文字列長を手動で設定する必要があります。これを構成するには、Schema::defaultStringLength
内でAppServiceProvider
メソッドを呼び出します。代わりに、
innodb_large_prefix
オプションを有効にすることもできます データベース。手順については、データベースのドキュメントを参照してください このオプションを適切に有効にする方法。
varchar(20)は21バイトしか必要としないが、varchar(500)のみが必要だと思う 501バイトが必要です。合計バイト数は522で、767未満です。 エラーメッセージは表示されましたか?
UTF8では文字列ごとに3バイトが必要です。したがって、20 + 500文字= 20 * 3 + 500 * 3 = 1560 バイトは< strong>超は 767 バイトを許可しました。
UTF8の制限は767/3 = 255文字です。1文字あたり4バイトを使用するUTF8mb4の場合、767/4 = 191文字です。
制限より長い列を使用する必要がある場合、この問題には2つの解決策があります。
- <!> quot; cheapper <!> quot;を使用します。エンコード(1文字あたりのバイト数が少ないもの)
私の場合、記事のSEO文字列を含む列に一意のインデックスを追加する必要がありました.SEOには[A-z0-9\-]
文字のみを使用しているため、文字ごとに1バイトのみを使用するlatin1_general_ci
を使用したため、列の長さは767バイトになります。 - 列からハッシュを作成し、その上でのみ一意のインデックスを使用します
私にとっての他のオプションは、SEOのハッシュを格納する別の列を作成することでした。この列には、SEO値が一意であることを保証するUNIQUE
キーがあります。また、検索を高速化するために、元のSEO列にKEY
インデックスを追加します。
エラーメッセージが表示される理由についての回答は、ここで多くのユーザーによって既に回答されています。私の答えは、それを修正してそのまま使用する方法についてです。
このリンクを参照してください。
- MySQLクライアント(またはMariaDBクライアント)を開きます。これはコマンドラインツールです。
- パスワードを要求し、正しいパスワードを入力します。
- このコマンドを使用してデータベースを選択
use my_database_name;
データベースが変更されました
-
set global innodb_large_prefix=on;
クエリOK、影響を受ける行はありません(0.00秒)
-
set global innodb_file_format=Barracuda;
クエリOK、影響を受けた行はありません(0.02秒)
- 管理を容易にするために、phpMyAdminなどのデータベースにアクセスします。 <!> gt;データベースを選択<!> gt;テーブルを表示構造 <!> gt; [操作]タブに移動します。 <!> gt; ROW_FORMAT を DYNAMIC に変更し、変更を保存します。
- 表の[構造]タブに移動<!> gt; [ユニーク]ボタンをクリックします。
- 完了。これでエラーは発生しません。
この修正の問題は、dbを別のサーバーにエクスポートした場合(たとえば、localhostから実ホストに)、そのサーバーでMySQLコマンドラインを使用できない場合です。そこで動作させることはできません。
Specified key was too long; max key length is 767 bytes
latin-1
文字セットを使用する場合にのみ1バイトが1文字に等しいため、そのメッセージが表示されます。 utf8
を使用すると、キー列を定義するときに各文字が3バイトと見なされます。 utf8mb4
を使用すると、キー列を定義するときに各文字が4バイトと見なされます。したがって、キーフィールドの文字数制限に1、3、または4(この例では)を掛けて、キーフィールドが許可しようとしているバイト数を決定する必要があります。 uft8mb4を使用している場合、ネイティブ、InnoDB、プライマリキーフィールドに対して191文字しか定義できません。 767バイトに違反しないでください。
md5の長い列の列を追加できます
utf8mb4を使用してVARCHAR(255)フィールドにUNIQUEインデックスを追加しようとしたときに、この問題が発生しました。ここではすでに問題の概要を説明していますが、これをどのように見つけて解決したかについて、実用的なアドバイスを追加したいと思いました。
utf8mb4を使用する場合、文字は4バイトとしてカウントされますが、utf8では3バイトとしてカウントされる場合があります。 InnoDBデータベースには、インデックスに767バイトしか含めることができないという制限があります。したがって、utf8を使用すると、255文字(767/3 = 255)を保存できますが、utf8mb4を使用すると、191文字(767/4 = 191)しか保存できません。
utf8mb4を使用してVARCHAR(255)
フィールドに通常のインデックスを絶対に追加できますが、インデックスサイズは191文字で自動的に切り捨てられます-unique_key
こちらのように:
これは問題ありません。通常のインデックスは、MySQLがデータをより速く検索できるようにするためだけに使用されるためです。フィールド全体にインデックスを付ける必要はありません。
では、なぜMySQLは通常のインデックスに対してインデックスを自動的に切り捨てますが、一意のインデックスに対してそれをしようとすると明示的なエラーをスローしますか?さて、MySQLが挿入または更新される値が既に存在するかどうかを判断できるようにするには、値の一部だけでなく、値全体を実際にインデックス化する必要があります。
1日の終わりに、フィールドに一意のインデックスを作成する場合、フィールドの内容全体がインデックスに収まる必要があります。 utf8mb4の場合、これはVARCHARフィールドの長さを191文字以下に減らすことを意味します。そのテーブルまたはフィールドにutf8mb4が必要ない場合は、utf8にドロップして255個の長さのフィールドを保持できます。
ここに私の元の答えがあります:
データベースを削除し、次のように再作成すると、エラーはなくなりました:
drop database if exists rhodes; create database rhodes default CHARACTER set utf8 default COLLATE utf8_general_ci;
ただし、すべてのケースで機能するわけではありません。
実際には、文字セットutf8
(またはutf8mb4
)を持つVARCHAR列でインデックスを使用する際の問題であり、VARCHAR列には特定の長さ以上の文字があります。 <=>の場合、その特定の長さは191です。
MySQLデータベースで長いインデックスを使用する方法の詳細については、この記事の長いインデックスのセクションを参照してください: http://hanoian.com/content/index.php/24-automate-the-converting-a-mysql-database- character-set-to-utf8mb4
このトピックを検索したところ、最終的にカスタム変更が行われました
MySQLワークベンチ6.3.7バージョンのグラフィカルなインターフェーズが利用可能
- ワークベンチを起動し、接続を選択します。
- 管理またはインスタンスに移動し、オプションファイルを選択します。
- ワークベンチから設定ファイルの読み取り許可を求められたら、[OK]を2回押して許可します。
- 中央に管理者オプションファイルウィンドウが表示されます。
- 「InnoDB」タブに移動し、「一般」セクションでチェックされていない場合は、innodb_large_prefixをチェックします。
- innodb_default_row_formatオプションの値をDYNAMICに設定します。
6.3.7未満のバージョンでは、直接オプションは使用できないため、コマンドプロンプトを使用する必要があります
- CMDを管理者として起動します。
- mysqlサーバーがインストールされているディレクターに移動ほとんどの場合、 <!> quot; C:\ Program Files \ MySQL \ MySQL Server 5.7 \ bin <!> quot;だからコマンドは <!> quot; cd \ <!> quot; <!> quot; cd Program Files \ MySQL \ MySQL Server 5.7 \ bin <!> quot;。
- Now Runコマンド mysql -u userName -p databasescheema 次に、それぞれのユーザーのパスワードを要求しました。 パスワードを入力し、mysqlプロンプトに入ります。
- いくつかのグローバル設定を設定し、以下のコマンドを1つずつ入力する必要があります グローバルinnodb_large_prefix = onを設定します。 グローバルinnodb_file_format = barracudaを設定します。 グローバル設定innodb_file_per_table = true;
- 最後に、必要なテーブルのROW_FORMATをデフォルトで変更する必要があります。そのCOMPACTを動的に設定する必要があります。
- 次のコマンドを使用 alter table table_name ROW_FORMAT = DYNAMIC;
- 完了
この問題を修正しました:
varchar(200)
に置き換え
varchar(191)
200を超えるすべてのvarcharを191に置き換えるか、テキストを設定します。
5つの回避策:
この制限は5.7.7で引き上げられました(MariaDB 10.2.2?)。そして、5.6(10.1)でいくらかの作業を行うことで増加させることができます。
CHARACTER SET utf8mb4を使用しようとしたために制限に達した場合。次に、エラーを回避するために次のいずれかを実行します(それぞれに欠点があります)。
⚈ 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
照合を変更します。ほとんどすべてをサポートする utf8_general_ci を使用できます
テーブルを作成するときにutf8mb4
をutf8
に変更するだけで問題が解決しました。例:CREATE TABLE ... DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
からCREATE TABLE ... DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
。
laravel 5.7または5.6または5.8の場合
従うべき手順
-
App\Providers\AppServiceProvider.php
に移動します。 - これをプロバイダーの
use Illuminate\Support\Facades\Schema;
上部に追加します。 - ブート機能内にこれを追加
Schema::defaultStringLength(191);
すべて、お楽しみください。
以下の列に基づいて、これらの2つの可変文字列列はutf8_general_ci
照合を使用しています(utf8
文字セットが暗黙的に指定されています)。
MySQLでは、utf8mb4
文字セットは各文字に最大 3バイトを使用します。したがって、500 * 3 = 1500バイトを割り当てる必要があります。これは、MySQLで許可されている767バイトよりもはるかに大きくなります。そのため、この1071エラーが発生します。
つまり、すべての文字セットが単一のバイト表現ではないため、文字セットのバイト表現に基づいて文字数を計算する必要があります(推測どおり)。 MySQLの<=>は、文字ごとに最大3バイト、767/3 <!>#8776; 255文字を使用し、<=>の場合、最大4バイト表現、767/4 <!>#8776; 191文字。
MySQLも知られています
column1 varchar(20) utf8_general_ci
column2 varchar(500) utf8_general_ci
このクエリは、インデックスが最大長に違反している列を検出するのに役立ちます。
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')
sql_mode
が似ているかどうかを確認してください
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
そうである場合、
に変更しますsql_mode=NO_ENGINE_SUBSTITUTION
または
my.cnfファイルを変更してサーバーを再起動します(以下を入力)
innodb_large_prefix=on
私の場合、Linuxリダイレクト出力/入力文字を使用してデータベースをバックアップしているときにこの問題が発生しました。したがって、以下に説明するように構文を変更します。 PS:LinuxまたはMac端末を使用しています。
バックアップ(<!> gt;リダイレクトなし)
# mysqldump -u root -p databasename -r bkp.sql
復元(<!> lt; redirectなし)
# mysql -u root -p --default-character-set=utf8 databasename
mysql> SET names 'utf8'
mysql> SOURCE bkp.sql
エラー<!> quot;指定されたキーが長すぎました。最大キー長は767バイト<!> quot;単純に消えました。
インデックスの長さ<!> amp; MySQL / MariaDB
Laravelは、デフォルトで設定された utf8mb4文字を使用します。これには、データベースに <!> quot; emojis <!> quot; を保存するためのサポートが含まれます。 5.7.7リリースより古いバージョンのMySQLまたは10.2.2リリースより古いバージョンのMariaDBを実行している場合、MySQLがインデックスを作成するために、移行によって生成されるデフォルトの文字列長を手動で設定する必要があります。これを構成するには、 AppServiceProvider:
内で Schema :: defaultStringLength メソッドを呼び出します。
use Illuminate\Support\Facades\Schema;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
}
または、データベースでinnodb_large_prefixオプションを有効にすることもできます。このオプションを適切に有効にする方法については、データベースのドキュメントを参照してください。
公式laravelドキュメントからの参照: https://laravel.com/ docs / 5.7 / migrations
問題のあるインデックスフィールドのCHARSETを<!> quot; latin1 <!> quot;
に変更します
つまり、ALTER TABLE tbl CHANGE myfield myfield varchar(600)CHARACTER SET latin1 DEFAULT NULL;
latin1は4文字ではなく1文字に対して1バイトを使用します
次のようなものを作成している場合:
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;
次のようになります
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;
ただし、コードからその列の一意性を確認するか、varchar列のMD5またはSHA1として新しい列を追加する必要があります
プレフィックスの制限により、このエラーが発生します。 767バイトは、MySQLバージョン5.7より前のInnoDBテーブルのプレフィックス制限です。 MyISAMテーブルの長さは1,000バイトです。 MySQLバージョン5.7以降では、この制限は3072バイトに増加しました。
エラーが発生したサービスで次を実行すると、問題が解決するはずです。これはMYSQL CLIで実行する必要があります。
SET GLOBAL innodb_file_format=Barracuda;
SET GLOBAL innodb_file_per_table=on;
SET GLOBAL innodb_large_prefix=on;
それを修正するために、これは私にとって魅力のように機能します。
ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci;
最近innodb_log_file_size
を変更した場合は、機能していた以前の値を復元してみてください。
私にとって、<!> quot;#1071-指定されたキーの問題が長すぎました。最大キー長は767バイト<!> quot;列サイズを200に制限することにより、primarykey / uniquekeyの組み合わせを変更した後に解決されました。
ALTER TABLE `mytable` ADD UNIQUE (
`column1` (200) ,
`column2` (200)
);