更新: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% /
エラーメッセージThe table 'my_table' is full
に騙されないでください。このまれなシナリオは、ディスクスペースとはまったく関係ありません。このテーブルフルの状態は、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つのものを得ました
Table is Full
状態になりましたこれは、106GBがInnoDBの内部配管に使用されていたことを意味します。当時、クライアントはext3を使用していました。
私にとっての解決策は、ibdata3を追加することでした。私はこれを以前の投稿で話しました 「テーブルがいっぱいです」を「innodb_file_per_table」で解決する方法は?
私は他の投稿でもこれについて議論しました
Mar 31, 2014
: テーブルがいっぱいであるために失敗した1つのクエリの後で、mysqlディレクトリが246Gに増加しますNov 25, 2011
: ファイルの行6308にあるエラー1114(HY000)&テーブルuser_analysisがいっぱいですinnodb_file_per_table が有効になっている場合でも、この状態が発生する可能性があることに注意してください。どうやって? ロールバックと元に戻すログは、ibdata1の制御されていないスパイクの原因です 。
インデックスをテーブルに追加してTable is Full
を取得しているため、テーブルは巨大である必要があり、単一のロールバックセグメント内に収まりません。次のことを行う必要があります。
データをダンプファイルに取得する
mysqldump --no-create-info mydb mytable > table_data.sql
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;
データを含む追加のインデックスをテーブルにロードします
mysql -Dmydb < table_data.sql
それで全部です。
問題は、ALTER TABLEが巨大なテーブルのすべての行を1つのトランザクションとして挿入しようとすることです。 mysqldumpを使用すると、単一のトランザクションのすべての行ではなく、一度に数千行のデータがテーブル(現在は新しいインデックス)に挿入されます。
what if this doesn't work?
について心配する必要はありません。元のテーブルは、何があってもmytable_old
という名前になります。バックアップとして使用できます。新しいテーブルが機能することがわかったら、バックアップを削除できます。
巨大なデータのダンプが心配な場合は、gzipしてください。
同じ手順を実行できますが、次のようになります
データをダンプファイルに取得する
mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz
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;
データを含む追加のインデックスをテーブルにロードします
gzip -d < table_data.sql.gz | mysql -Dmydb
または
gunzip < table_data.sql.gz | mysql -Dmydb
私はこの問題に関して別の側面について考えました。
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つのオプションがあります
大きなディスク(おそらく100 GB以上)を作成し、その大きなディスクに/var/lib/mysqltmp
をマウントすることもできます。
私の3ステップの提案は、ディスクスペースが限られている場合でも機能するはずです。
あなたが投稿したエラーメッセージが言うのは残念です
InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.
つまり、OSは問題ありません。また、この状況に関連するエラー番号はありません。
もう一つ知っておくべきことがあります。 MySQLドキュメントによると、InnoDBの高速インデックス作成はまだディスクに保存されます :
インデックスの作成中、ファイルは一時ディレクトリ(Unixでは$ TMPDIR、Windowsでは%TEMP%、または--tmpdir構成変数の値)に書き込まれます。各一時ファイルは、新しいインデックスを構成する1つの列を保持するのに十分な大きさであり、最終的なインデックスにマージされるとすぐに削除されます。
MySQLの制限により、TEMPORARY TABLEでインデックスを作成するときに「高速インデックス作成」を使用するのではなく、テーブルがコピーされます。これは MySQL Bug#39833 として報告されています。
[mysqld]
datadir = /mnt/cbsvolume1/var/lib/mysql
tmpdir = /mnt/cbsvolume1/var/lib/mysql
/mnt/cbsvolume1/var/lib/mysql
の空き容量が100G以上あることを確認してください