あーかいぶすハイディフィニション

ここはもう更新しとらんのじゃ

クエリ発行してたら「ERROR 1114 (HY000): The table 'tablename' is full」って怒られた話

なんでさ!

はい基本情報チェーック

利用しているデータベースは全体で 2Gbyte ちょっとあって、MySQL Cluster (7.2.17) で動いてる。is full って文句を言われた時に実行してたクエリはぼかすけどこんなん。

mysql> mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
ERROR 1114 (HY000): The table 'tablename' is full

特定の箇所を全部うめるとかいうあれ、二万件毎を処理してた。

原因はなんだ?

オンメモリデータベースなのと、直近の ndb_mgmd.log から「Datamemory Usage 80%」を超えてたので、あー容量たりねえのかな?と思って、API node の my.cnf をちょちょいと変更。

[root@apinode1 ~]# vi /etc/my.cnf

#DataMemory=5G
DataMemory=7G

[root@apinode ~]# ndb_mgm
ndb_mgm> all stop
[root@ndbnode ~]# ndbd -n
[root@apinode ~]# ndb_mgm
ndb_mgm> all start

だいぶはしょってますが、DataMemory 増やしてクラスタ全体を再起動してます。これで DataMemory 増えたし平気でしょ!

mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
ERROR 1114 (HY000): The table 'tablename' is full

ゴボボーッ!ナンデ!!

ということで、全ての稼働中ノードのキャッシュメモリを強制的に解放して、メモリに空きを作って見ます(以下は実際の作業結果です)。

[root@ndbnode ~]# free -m
             total       used       free     shared    buffers     cached
Mem:         11912      10151       1760          0        286       2926
-/+ buffers/cache:       6938       4974
Swap:        12079          0      12079
[root@ndbnode ~]# sync
[root@ndbnode ~]# echo 3 > /proc/sys/vm/drop_caches
[root@ndbnode ~]# free -m
             total       used       free     shared    buffers     cached
Mem:         11912       6901       5011          0          2         24
-/+ buffers/cache:       6874       5038
Swap:        12079          0      12079

apinode でも別に実行してありますが、 cached が解放されて良い感じですね?では、再度試してみましょう!

mysql> mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
ERROR 1114 (HY000): The table 'tablename' is full

ほんぎゃあ!

アイエエエ、助けて

ずっと「mysql cluster full」「助けて」「マッポー」など訳の分からないワードでググっておりましたが、別に mysql cluster に限定しなくてよかったんですね。なんせクエリを実行するのは MySQL そのものなんすから。
MySQL Reference Manual for version 3.21.31. - �����Ȥ褯���륨�顼
ワッザ!という訳で tmp_table_size を確認してみましょう。

mysql> SHOW VARIABLES LIKE 'tmp_table_size';
+----------------+-----------+
| Variable_name  | Value     |
+----------------+-----------+
| tmp_table_size | 536870912 |
+----------------+-----------+
1 row in set (0.00 sec)

アッ、アイエエエ?!

[root@apinode1 ~]# less /etc/my.cnf

tmp_table_size=512M

オッヴェ!こんだけ確保してても駄目なの?決断的に増やしますよ。

[root@apinode1 ~]# less /etc/my.cnf

#tmp_table_size=512M
tmp_table_size=768M

mysql> SHOW VARIABLES LIKE 'tmp_table_size';
+----------------+-----------+
| Variable_name  | Value     |
+----------------+-----------+
| tmp_table_size | 805306368 |
+----------------+-----------+
1 row in set (0.00 sec)

どうよ!

mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
おk
mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
おk
mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
おk
mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 20000;
ERROR 1114 (HY000): The table 'tablename' is full

ほんぎゃあ!

わかったこと

そら更新する箇所によってデータが入ってたり入ってなかったりで、結局一律同じ LIMIT で回すと tmp_table_size の制限を超えてしまう箇所があるわな、というオチでした。書き込むテーブル自体はまだまだ書き込める状況でも、一度のクエリ実行結果で tmp_table_size を超えると何故か「The table 'tablename' is full」という表示になる ( tmp or heap table is full とか出てくれればわかりやすかったね )。
という訳で、一度に実行する数を少なくすることで回避しました。

mysql> UPDATE tablename SET column1 = '' WHERE 〜〜〜 LIMIT 5000;
おk

tmp_table_size をいじらなくても最初からこうしてれば解決した気もしますねこれ……。