MySQLインデックスの作成がテーブルで失敗する


10

更新:tl; dr:問題はMySQLがTMPDIRインデックスの作成時にを使用することでした。そして私TMPDIRはディスク容量が足りなくなった。

元のQ:

InnoDBテーブルにインデックスを追加しようとしていて、を取得していtable is full errorます。十分なディスク容量があり、MySQL構成にはfile-per-table = 1があります。テーブルデータは85GBで、インデックスは約20GB-30GBであり、それよりもはるかに多くのディスク容量があると思います。私はext3も使用しているので、OSの観点からはファイルサイズの制限に問題はないと思います。

ログに記録されたエラーは次のようになります。

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

これを引き起こしている原因と解決方法を教えてください。

作成テーブル:

`CREATE TABLE `my_table` (
 `uid_from` bigint(11) NOT NULL,
 `uid_to` bigint(11) NOT NULL,
 `counter` int(11) NOT NULL,
 `updated` date NOT NULL,
 PRIMARY KEY (`uid_to`,`uid_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8`

データは87.4GBであり、約1.5Bの行があると推定しています。

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Variable_name   Value
tmpdir  /tmp

[root@web ~]# df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   24G   14G  64% /

1
「InnoDB:OSとファイルシステムがこのサイズのファイルをサポートしていることを確認してください。InnoDB:ディスクがいっぱいでないか、ディスククォータを超えていることも確認してください。」-確認しましたか?ext2を使用していますか?ext3?xfs?ユーザーのクォータを確認しましたか?
フィロ2014年

@Philは両方をチェックしました。私はext3を使用していますが、ディスク容量に問題がないことはほぼ間違いありません。ハンチは、ログが彼の限界に達したか何かに関係していることですが、実際に確認して修正する方法はわかりません。
ノーム2014年

「(マージ)」とは何のファイルでしょうか?
アクズミンスキー2014年

@akuzminskyうーん、わかりません。PHPMyAdminから新しいインデックスを作成しただけです。
ノーム2014年

TMPDIRの+1、それは私のサーバーでも問題でした。
チャドE.

回答:


8

エラーメッセージに騙されないでくださいThe table 'my_table' is full。このまれなシナリオは、ディスクスペースとはまったく関係ありません。このテーブルフルの状態は、InnoDBの内部配管に関係しています。

まず、このInnoDBアーキテクチャの図を見てください。

InnoDBアーキテクチャ

システムテーブルスペース(ファイルibdata)には、ロールバックセグメントごとに128のロールバックセグメントと1023のロールバックスロットしかないことに注意してください。これにより、トランザクションのロールバック容量のサイズが制限されます。つまり、1つのロールバックセグメントがトランザクションをサポートするために1023を超えるスロットを必要とする場合、トランザクションはそのtable is full条件にヒットします。

ニュージャージー州のレストランRed Lobsterについて考えてみましょう。それは200人の容量があるかもしれません。レストランが満員の場合、列に並ぶ人が外に出ることがあります。行列ができなくなった場合、店が満員になって帰ってしまう可能性があります。明らかに、解決策はニュージャージーを大きくする(またはディスク容量を増やす)ことではありません。解決策は、Red Lobsterレストランを大きくすることです。そうすれば、収容人数を240人まで増やすことができます。それでも、240人を超える人がレッドロブスターに来ることを決めた場合、外に列ができることがあります。

例を挙げれば、2TBのシステムテーブルスペースを持つクライアントがあり、innodb_file_per_tableが無効になっています。(ibdata1の346G、ibdata2のリセット)。このクエリを実行しました

SELECT SUM(data_length+index+length) InnoDBDataIndexSpace
FROM information_schema.tables WHERE engine='InnoDB';

次に、ibdata1とibdata2のファイルサイズの合計からInnoDBDataIndexSpaceを引きました。私は私に衝撃を与えた2つのものを得ました

  • システムテーブルスペース内に106GB残っていました。
  • 私はこれと同じTable is Full状態になりました

これは、106GBがInnoDBの内部配管に使用されていたことを意味します。当時、クライアントはext3を使用していました。

私にとっての解決策は、ibdata3を追加することでした。私はこれを以前の投稿で説明しました「innodb_file_per_table」で「テーブルがいっぱいです」を解決する方法は?

私は他の投稿でもこれについて議論しました

この状態は、innodb_file_per_tableが有効になっていても発生する可能性があることに注意してください。どうやって?ロールバックと元に戻すログは、制御されていないスパイクの原因であり、ibdata1の増加です

あなたの実際の質問

インデックスをテーブルに追加してを取得しているTable is Fullため、テーブルは巨大である必要があり、単一のロールバックセグメント内に収まりません。次のことを行う必要があります。

ステップ01

データをダンプファイルに取得する

mysqldump --no-create-info mydb mytable > table_data.sql

ステップ02

MySQLにログインしてこれを実行します

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

ステップ03

データを含む追加のインデックスをテーブルにロードします

mysql -Dmydb < table_data.sql

それで全部です。

問題は、ALTER TABLEが巨大なテーブルのすべての行を1つのトランザクションとして挿入しようとすることです。mysqldumpを使用すると、単一のトランザクションのすべての行ではなく、一度に数千行のデータがテーブル(現在は新しいインデックス)に挿入されます。

心配しないでくださいwhat if this doesn't work?。元のテーブルはmytable_old、何かの場合に名前が付けられます。バックアップとして使用できます。新しいテーブルが機能することがわかったら、バックアップを削除できます。

試してみる !!!

UPDATE 2014-06-16 11:13 EDT

より大きなデータのダンプが心配な場合は、gzipしてください。

同じ手順を実行できますが、次のようになります

ステップ01

データをダンプファイルに取得する

mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz

ステップ02

MySQLにログインしてこれを実行します

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

ステップ03

データを含む追加のインデックスをテーブルにロードします

gzip -d < table_data.sql.gz | mysql -Dmydb

または

gunzip < table_data.sql.gz | mysql -Dmydb

UPDATE 2014-06-16 12:55 EDT

私はこの問題に関して別の側面を考えました。

DMLではなくDDLを実行しているため、これがInnoDBの内部配管ではない可能性があります。以来DDLは、InnoDBのためにロールバックすることはできません、問題は、外部の配管にする必要があります。この外部配管はどこで詰まっていますか?OSの一時フォルダーが疑われます。どうして?

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

を参照してくださいdisk quota exceeded。このディスククォータはどこに課されていますか?

このクエリを実行する

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

あなたはそれだと言った /var/lib/mysqltmp

これをOSで実行します

df -h /var/lib/mysqltmp

なんとかして空き容量/var/lib/mysqltmpが足りなくなったようですが、結局のところ、14Gしか空き容量がありません。(インデックスを作成するための)DDLの目には、ibdata1ファイルではなく、tmpdir場所でロールバックが発生しました。/var/lib/mysqltmpがどこにもマウントされていない場合、一時データはルートパーティションに書き込まれています。/var/lib/mysqltmpがどこかにマウントされている場合、そのマウントは行データで埋められています。どちらの場合も、DDLを完了するための十分なスペースがありません。

ここには2つのオプションがあります

オプション1

大きなディスク(おそらく100 GB以上)を作成し、/var/lib/mysqltmpその大きなディスクにマウントすることもできます。

オプション#2

私の3ステップの提案は、ディスクスペースが限られている場合でも機能します。

解説

あなたが投稿したエラーメッセージが言うのは残念です

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.

つまり、OSは問題ありません。また、この状況に関連するエラー番号はありません。

もう1つ知っておくべきことがあります。MySQLドキュメントによると、InnoDBの高速インデックス作成はまだディスクに保存されています。

インデックスの作成中、ファイルは一時ディレクトリ(Unixでは$ TMPDIR、Windowsでは%TEMP%、または--tmpdir構成変数の値)に書き込まれます。各一時ファイルは、新しいインデックスを構成する1つの列を保持するのに十分な大きさで、最終的なインデックスにマージされるとすぐに削除されます。

MySQLの制限により、TEMPORARY TABLEにインデックスを作成するときに「高速インデックス作成」を使用するのではなく、テーブルがコピーされます。これはMySQLバグ#39833として報告されています

UPDATE 2014-06-16 13:58 EDT

[mysqld]
datadir                         = /mnt/cbsvolume1/var/lib/mysql
tmpdir                          = /mnt/cbsvolume1/var/lib/mysql

/mnt/cbsvolume1/var/lib/mysql100G以上の空き容量があることを確認してください


ダンプから最初の場所にテーブルを作成するのに数週間かかりました(85GBデータ)。これをもう一度やり直す以外に他の方法はありますか?テーブルを分割することをお勧めしますか?
ノーム2014年

インデックスを追加するためのALTER TABLEは、単一のトランザクションですべてをロードしようとするため、パーティショニングは役に立ちません。ディスクスペースではなく、InnoDBアーキテクチャと戦っていることに注意してください。この場合、mysqldumpはINSERTあたり数千行を挿入するので、ロールバックの可能性がある一時テーブルに全か無かの挿入ではないので、私の答えはあなたに役立つはずです。
RolandoMySQLDBA 2014年

ただし、このテーブルの作成には(再び)時間がかかります。ロールバックオプションを無効にしながらインデックス作成を実行する方法はありますか?
ノーム2014年

インデックスの作成はDMLではなくDDLです。DDLの動作は変更できません。そのため、私の投稿ではDML手法を使用しています。
RolandoMySQLDBA 2014年

ところで、テーブルにはいくつの行がありますか???
RolandoMySQLDBA 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.