GA

2015/04/30

MySQL 5.7.6以降では暗黙のテンポラリーテーブルがあふれると死ぬ

MySQL 5.7.6から、JOINした時とかに作る暗黙のテンポラリーテーブルでMemoryストレージエンジンで収まらなくなった時に固定化するテンポラリーテーブル(Created_tmp_disk_tablesがカウントアップされるアレ)のストレージエンジンがInnoDBになった。

MySQL :: MySQL 5.7 Reference Manual :: 8.4.4 How MySQL Uses Internal Temporary Tables
MySQL :: MySQL 5.7 Reference Manual :: 5.1.4 Server System Variables

テンポラリーテーブル用(こっちはCREATE TEMPORARY TABLEで作るやつも含んだ言い方だと思いねえ)のテーブルスペースはREDOロギングを無効化してあったりといいこともたくさんあるんだけれど、mysqldを再起動しない限りibtmp1ファイルは決して小さくならない。



こんなエラーを見たことはないだろうか。俺はある。設定が古いサーバーだと特によくある。

mysql57> SELECT @@internal_tmp_disk_storage_engine;
+------------------------------------+
| @@internal_tmp_disk_storage_engine |
+------------------------------------+
| MYISAM                             |
+------------------------------------+
1 row in set (0.00 sec)

mysql57> SELECT * FROM (SELECT * FROM t1 WHERE val LIKE 'abc%') AS t1, (SELECT * FROM t1 WHERE val LIKE 'def%') AS t2 ORDER BY t1.num;
ERROR 126 (HY000): Incorrect key file for table '/tmp/#sql_2974_0.MYI'; try to repair it


$ tail error.log
..
2015-04-30T14:59:45.558360+09:00 87 [ERROR] /usr/mysql/5.7.7/bin/mysqld: Incorrect key file for table '/tmp/#sql_2974_0.MYI'; try to repair it
2015-04-30T14:59:45.573411+09:00 87 [ERROR] Got an error from unknown thread, /home/yoku0825/mysql-5.7.7-rc/storage/myisam/mi_write.c:223


暗黙のテンポラリーテーブルがtmpdirに作られて、容量が足りなくなるとこうなる。

$ ll /tmp
合計 792816
-rw-r----- 1 yoku0825 yoku0825 402935808  4月 30 14:59 2015 #sql_2974_0.MYD
-rw-r----- 1 yoku0825 yoku0825      1024  4月 30 14:59 2015 #sql_2974_0.MYI
-rw-rw-r-- 1 yoku0825 yoku0825 408888897  4月 30 14:45 2015 md5
drwx------ 2 yoku0825 tmux         4096  4月 17 15:45 2015 tmux-3012

$ df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/vg_00-lv_root
                       42G   40G   33M 100% /


暗黙のテンポラリーテーブルがDiskを食いきってクエリーがアボートされるとストレージ容量は元に戻る。
internal_tmp_disk_storage_engine= MyISAMのとき(MySQL 5.7.5とそれ以前)はこんな動作だった。

ところがInnoDBになるとibtmp1にこのデータを書くし、ibtmp1はmysqldの再起動まで容量が空かないので、

mysql57> SELECT @@internal_tmp_disk_storage_engine;
+------------------------------------+
| @@internal_tmp_disk_storage_engine |
+------------------------------------+
| INNODB                             |
+------------------------------------+
1 row in set (0.03 sec)

mysql57> SELECT * FROM (SELECT * FROM t1 WHERE val LIKE 'abc%') AS t1, (SELECT * FROM t1 WHERE val LIKE 'def%') AS t2 ORDER BY t1.num;
ERROR 1114 (HY000): The table '/tmp/#sql_2974_0' is full

$ tail error.log
..
2015-04-30T15:04:22.549484+09:00 87 [ERROR] InnoDB: posix_fallocate(): Failed to preallocate data for file ./ibtmp1, desired size 67108864 bytes. Operating system error number 28. Check that the disk is not full or a disk quota exceeded. Make sure the file system supports this function. Some operating system error numbers are described at http://dev.mysql.com/doc/refman/5.7/en/operating-system-error-codes.html
2015-04-30T15:04:22.590070+09:00 87 [Warning] InnoDB: 1048576 bytes should have been written. Only 794624 bytes written. Retrying again to write the rem

クエリーがアボートしても容量は開放されていない。


$ ll /usr/mysql/5.7.7/data/ibtmp1
-rw-r----- 1 yoku0825 yoku0825 485236736  4月 30 15:04 2015 /usr/mysql/5.7.7/data/ibtmp1

$ df -h
Filesystem            Size  Used Avail Use% Mounted on
/dev/mapper/vg_00-lv_root
                       42G   40G   17M 100% /


ベンチマーク取りながらうっかり100%突っ込んだ人とかならわかると思うんだけれど、DiskがFullな状態で更新かけるとbinlogに書き込もうとしたときにそこで詰まって

mysql57> INSERT INTO t1 VALUES (0, 'test');

何も言わずにmysqldは応答を返さない。


(gdb) bt
#0  0x00000038862aca3d in nanosleep () at ../sysdeps/unix/syscall-template.S:82
#1  0x00000038862ac8b0 in __sleep (seconds=0) at ../sysdeps/unix/sysv/linux/sleep.c:138
#2  0x0000000000e9a111 in wait_for_free_space (filename=0x7f3d6f428690 "./bin.000002", errors=)
    at /home/yoku0825/mysql-5.7.7-rc/mysys/errors.c:85
#3  0x0000000000e9147c in my_write (Filedes=19, Buffer=0x7f3d5772e0c2 "", Count=108, MyFlags=52)
    at /home/yoku0825/mysql-5.7.7-rc/mysys/my_write.c:90
#4  0x0000000000e7f579 in inline_mysql_file_write (info=0x1cfc0c8, need_append_buffer_lock=)
    at /home/yoku0825/mysql-5.7.7-rc/include/mysql/psi/mysql_file.h:1136
#5  my_b_flush_io_cache (info=0x1cfc0c8, need_append_buffer_lock=)
    at /home/yoku0825/mysql-5.7.7-rc/mysys/mf_iocache.c:1537
#6  0x0000000000e2a317 in MYSQL_BIN_LOG::flush_cache_to_file (this=0x1cfbdc0, end_pos_var=0x7f3d6fda9520)
    at /home/yoku0825/mysql-5.7.7-rc/sql/binlog.cc:8187
#7  0x0000000000e39a14 in MYSQL_BIN_LOG::ordered_commit (this=0x1cfbdc0, thd=0x7f3d55ffa000, all=,
    skip_commit=) at /home/yoku0825/mysql-5.7.7-rc/sql/binlog.cc:8467
#8  0x0000000000e39c62 in MYSQL_BIN_LOG::write_binlog_and_commit_engine (this=0x1cfbdc0, thd=0x7f3d55ffa000,
    all=) at /home/yoku0825/mysql-5.7.7-rc/sql/binlog.cc:7858
#9  0x0000000000e3a1d4 in MYSQL_BIN_LOG::commit (this=, thd=0x7f3d55ffa000, all=)
    at /home/yoku0825/mysql-5.7.7-rc/sql/binlog.cc:7643
#10 0x000000000079dc60 in ha_commit_trans (thd=0x7f3d55ffa000, all=,
    ignore_global_read_lock=) at /home/yoku0825/mysql-5.7.7-rc/sql/handler.cc:1615
#11 0x0000000000d1c0fe in trans_commit_stmt (thd=)
    at /home/yoku0825/mysql-5.7.7-rc/sql/transaction.cc:395
#12 0x0000000000c687ec in mysql_execute_command (thd=0x7f3d55ffa000) at /home/yoku0825/mysql-5.7.7-rc/sql/sql_parse.cc:4591
#13 0x0000000000c6acb0 in mysql_parse (thd=0x7f3d55ffa000, parser_state=)
    at /home/yoku0825/mysql-5.7.7-rc/sql/sql_parse.cc:5159
#14 0x0000000000c6c1c6 in dispatch_command (command=COM_QUERY, thd=0x7f3d55ffa000,
    packet=0x7f3d55c1b011 "INSERT INTO t1 VALUES (0, 'test')", packet_length=33)
    at /home/yoku0825/mysql-5.7.7-rc/sql/sql_parse.cc:1249
#15 0x0000000000d3a1dc in handle_connection (arg=)
    at /home/yoku0825/mysql-5.7.7-rc/sql/conn_handler/connection_handler_per_thread.cc:298
#16 0x0000000000f066b7 in pfs_spawn_thread (arg=0x7f3d693a2210)
    at /home/yoku0825/mysql-5.7.7-rc/storage/perfschema/pfs.cc:2147
#17 0x00000038866079d1 in start_thread (arg=0x7f3d6fdac700) at pthread_create.c:301
#18 0x00000038862e88fd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:115

$ tail error.log
2015-04-30T15:12:50.592458+09:00 87 [ERROR] Disk is full writing './bin.000002' (Errcode: 28 - No space left on device). Waiting for someone to free space...
2015-04-30T15:12:50.592539+09:00 87 [ERROR] Retry in 60 secs. Message reprinted in 600 secs
2015-04-30T15:22:50.705673+09:00 87 [ERROR] Disk is full writing './bin.000002' (Errcode: 28 - No space left on device). Waiting for someone to free space...
2015-04-30T15:22:50.705730+09:00 87 [ERROR] Retry in 60 secs. Message reprinted in 600 secs
2015-04-30T15:32:50.831917+09:00 87 [ERROR] Disk is full writing './bin.000002' (Errcode: 28 - No space left on device). Waiting for someone to free space...
2015-04-30T15:32:50.831991+09:00 87 [ERROR] Retry in 60 secs. Message reprinted in 600 secs
2015-04-30T15:42:50.963577+09:00 87 [ERROR] Disk is full writing './bin.000002' (Errcode: 28 - No space left on device). Waiting for someone to free space...
2015-04-30T15:42:50.963636+09:00 87 [ERROR] Retry in 60 secs. Message reprinted in 600 secs
2015-04-30T15:52:51.098086+09:00 87 [ERROR] Disk is full writing './bin.000002' (Errcode: 28 - No space left on device). Waiting for someone to free space...
2015-04-30T15:52:51.098142+09:00 87 [ERROR] Retry in 60 secs. Message reprinted in 600 secs

ああ、一応ちゃんとリトライアウトはあるのか、よかった。。 主人をibtmp1に殺されてからはや1時間くらいになりますが、リトライ *アウト* はしてなかった。まだクエリーはエラーになってくれない。たぶん、これからもなってくれない。

デフォルトのまま使うと間違ってでっかい暗黙のテンポラリーテーブル作るクエリー投げちゃった、てへ☆ が笑い事でなくなるので、

innodb_tmp_data_file_pathをautoextendじゃない形でサイズを決め打っておくか(innodb_data_file_pathと同じノリで記述できる。暗黙のデフォルトはibtmp1:12M:autoextend。コールドパラメーターなのでMySQLの再起動が必要) または internal_tmp_disk_storage_engineをMyISAMにセットしておくか(こっちはオンラインでOK)

秘伝のmy.cnfにinnodb_tmp_data_file_pathを足しておくか。。


【2020/06/19 14:53】
この記事はMySQL 5.7を対象としたもので、MySQL 8.0.13とそれ以降ではこれは発生しないっぽくなっている(瞬間的に容量を食うのは一緒だが、mysqldを再起動しなくても容量が解放できるようになっている)

日々の覚書: MySQL 8.0.13とそれ以降ではibtmp1は肥大化しない(あるいは、 /var/lib/mysql/#innodb_temp ディレクトリの正体)

0 件のコメント :

コメントを投稿