2017年11月6日月曜日

mysqlbinlog: [ERROR] unknown variable 'default-character-set=utf8mb4' と言われないためのTIPS

前にいつかどこかでメモした気がしなくもないけれど見つからなかったので。  ⇒ 書き上げてから思い出した、 第33回 MySQLのオプションファイル my.cnfの豆知識[その2]:MySQL道普請便り だ。。

default-character-setmysql コマンドラインクライアントとか mysqldump あたりで使う、クライアントの文字コードを指定するためのオプション。
MySQL 5.0以前ではサーバー側のデフォルトの文字コードを指定するオプションも同じ名前だったけど、現在ではそっちは character-set-server に名前が変わっている。 MySQL 5.0では互換性のためにどちらの名前も使えたけれど、5.1で猶予期間が終わって大量の秘伝のタレを死に追いやったのはもはや懐かしい話。。
で、タイトルのエラーが出るような my.cnf
[client]
default-character-set=utf8mb4
と書いてあるケースがほとんどだと思う( [mysqlbinlog] セクションにわざわざ自分で default-character-set を書いたりしないだろうから)
[client] セクションは割と便利で、コマンドラインクライアントのほとんどがコンフィグファイルからそのセクションを読んでくれる。逆を言うと、 [client] セクションを読んでしまうプログラムが対応していないオプションを [client] セクションに書いてしまうと、タイトルのようなエラーが出ることになる。
どのプログラムがどのセクションを読むかは、ソースコードから load_default_groups でgrepするとぽこぽこ出てきたりする。 [client] セクションを読むやつだけだっと抜粋。
./client/check/mysqlcheck.cc:static const char *load_default_groups[] = { "mysqlcheck", "client", 0 };
./client/dump/program.cc:const char *load_default_groups[]=
{
  "client", /* Read settings how to connect to server. */
  "mysql_dump", /* Read special settings for mysql_dump. */
  0
};

./client/mysql.cc:static const char *load_default_groups[]= { "mysql","client",0 };
./client/mysql_secure_installation.cc:static const char *load_default_groups[]= { "mysql_secure_installation", "mysql", "client", 0 };
./client/mysqladmin.cc:static const char *load_default_groups[]= { "mysqladmin","client",0 };
./client/mysqlbinlog.cc:static const char *load_default_groups[]= { "mysqlbinlog","client",0 };
./client/mysqldump.c:static const char *load_default_groups[]= { "mysqldump","client",0 };
./client/mysqlimport.c:static const char *load_default_groups[]= { "mysqlimport","client",0 };
./client/mysqlshow.c:static const char *load_default_groups[]= { "mysqlshow","client",0 };
./client/mysqlslap.cc:static const char *load_default_groups[]= { "mysqlslap","client",0 };
./client/mysqltest.cc:static const char *load_default_groups[]= { "mysqltest", "client", 0 };
./client/upgrade/program.cc:const char *load_default_groups[]=
{
  "client", /* Read settings how to connect to server */
  "mysql_upgrade", /* Read special settings for mysql_upgrade*/
  0
};

./extra/resolveip.c:/*static char * load_default_groups[]= { "resolveip","client",0 }; */
結構色々出てくる。 ほとんどのものはそう滅多に使わないし、正直これを全部考慮するのは無理ゲーなので、ワーニングを許容するなら loose接頭辞 と組み合わせて設定するのがオススメだ。
[client]
loose-default-character-set=utf8mb4
このように loose 接頭辞をつけておくと、 default-character-set を理解しない mysqlbinlogmysqlslap でもエラーでアボートせずにワーニングだけ出力して勘弁してくれる。
$ mysqlslap
mysqlslap: [Warning] unknown variable 'loose-default-character-set=utf8mb4'
参考までにウチのmy.cnfからクライアント用のセクションだけ引っこ抜いたのはこんな感じだった。
[client]
port= __port__ #<<
socket= __datadir__/mysql.sock #<<
loose-default-character-set= utf8mb4

[mysqldump]
quick
max_allowed_packet= 1G
single-transaction
#lock-all-tables
events
routines
triggers
master-data=2
#dump-slave=2

[mysql]
no-auto-rehash
show-warnings
prompt= "__hostname__ [\d]> " #<<
#safe-updates
syslog

今日のネタ提供は @purple_jwl さんでした。ごちそうさまでした。






2017年10月30日月曜日

MySQL 8.0.0で予約語だったROLEが8.0.1ではキーワードになっていたはなし

TL;DR

  • hmatsu47 さんに MySQLのキーワードと予約語 の中で「ROLEは予約語じゃなくてキーワード」と教えてもらった
  • 予約語とキーワードの違いは、 role_or_ident_keyword にリストされているかどうか? (ちなみに5.7だと単に keyword ってリストになってる)

シンボルのリスト

ラベルのリスト

ほげってみる

$ diff -c1 sql/sql_yacc.yy.orig sql/sql_yacc.yy
*** sql/sql_yacc.yy.orig        2017-09-19 20:33:50.000000000 +0900
--- sql/sql_yacc.yy     2017-10-30 16:23:04.657625949 +0900
***************
*** 13343,13345 ****
          | RESTORE_SYM           {}
-         | ROLE_SYM              {}
          | ROLLBACK_SYM          {}
--- 13343,13344 ----

$ make && make install

mysql80 8> SELECT @@version;
+--------------------+
| @@version          |
+--------------------+
| 8.0.3-rc-debug-log |
+--------------------+
1 row in set (0.00 sec)

mysql80 8> CREATE TABLE t1 (role int);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'role int)' at line 1
転けるようになった。

なんというか

予約語のキーワードと非予約語のキーワードの境目って案外大したことなかった…。 これを機に色々ゴニョって遊んでみ(ない

2017年10月4日水曜日

ファイルを吐かないtarコマンドの進捗を確認する

ファイルを吐かないというのは、MySQLのdatadirをtarボールに固めながら圧縮してS3にアップロードするようなケース(なんて限定的)
$ tar -C /var/lib -c mysql | pzstd -qc | aws s3 cp - s3://...
S3さん、アップロード終わるまで状況が見えないので、保管先のファイルサイズから全体をざっくり見積もることができなかった。俺のやり方が悪いだけかも知れない。
取り敢えず lsof で今掴んでいるファイルを見ることくらいまではぱっと思い付いた。
$ ps auxww | grep tar
root      1181  0.0  0.4 289208  4800 ?        Ssl  23:15   0:00 /usr/bin/docker-containerd-current -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --shim docker-containerd-shim --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --runtime docker-runc --runtime-args --systemd-cgroup=true
yoku0825  3259  0.3  0.1 123344  1232 pts/0    S+   23:22   0:00 tar c data
yoku0825  3263  0.0  0.0 112644   968 pts/1    R+   23:22   0:00 grep --color=auto tar

$ lsof -p 3259
COMMAND  PID     USER   FD   TYPE DEVICE  SIZE/OFF      NODE NAME
..
tar     3259 yoku0825    0u   CHR  136,0       0t0         3 /dev/pts/0
tar     3259 yoku0825    1w  FIFO    0,8       0t0     28213 pipe
tar     3259 yoku0825    2u   CHR  136,0       0t0         3 /dev/pts/0
tar     3259 yoku0825    3r   DIR  253,0      4096  71088914 /usr/mysql/5.7.19/data
tar     3259 yoku0825    4r   REG  253,0  21091934  68051378 /usr/mysql/5.7.19/data/bin.000011
たとえば今は bin.000011 を掴んでる、というところまではわかる。
が、 tar コマンドってファイルをどういう順番で掴むのかがわからず、bin.000011 が全体で何番目のファイルなのかよくわからない。 経験則として、 tar cvf とか tar xvf でファイル名だだーっと流している時でも、少なくともファイルパスでソートされているようには見えない。
困った時のソースコード。
ディレクトリーをターゲットにtarボールを作る時には create_archivedump_filedump_file0dump_dirget_directory_entries と来て、その中からgnulibstreamsavedir を呼び出している。
streamsavedir がやってることは単に readdir しているだけで、この時の並び順は特に決まった規則がある訳ではないらしい。
ところで readdir したものをそのままといえば、ls のソート無しオプションである -f な訳で、
$ ls -fl 
total 239212
drwxr-x---  9 yoku0825 yoku0825     4096 Oct  3 23:10 .
drwxrwxr-x 11 yoku0825 yoku0825      148 Jul 18 15:13 ..
-rw-r-----  1 yoku0825 yoku0825 79691776 Oct  3 23:10 ibdata1
-rw-r-----  1 yoku0825 yoku0825       56 Jul 18 15:12 auto.cnf
drwxr-x---  2 yoku0825 yoku0825     4096 Aug 22 18:29 mysql
...
drwxr-x---  2 yoku0825 yoku0825     4096 Aug 10 15:10 i_s
drwxr-x---  2 yoku0825 yoku0825     8192 Aug 10 15:10 p_s
-rw-r-----  1 yoku0825 yoku0825  3818805 Aug 18 18:52 bin.000006
-rw-r-----  1 yoku0825 yoku0825 21091934 Aug 24 18:41 bin.000011
-rw-r-----  1 yoku0825 yoku0825 48221936 Sep  6 12:44 bin.000012
-rw-r-----  1 yoku0825 yoku0825    86553 Sep 15 15:47 bin.000017
-rw-r-----  1 yoku0825 yoku0825    10626 Sep 20 16:30 bin.000024
-rw-r-----  1 yoku0825 yoku0825      169 Aug 22 14:26 relay.000001
-rw-r-----  1 yoku0825 yoku0825      209 Sep 12 13:16 bin.000015
-rw-r-----  1 yoku0825 yoku0825      227 Sep 19 20:49 bin.000022
大体残り50MBくらいかなあと。ディレクトリー階層があるところなら親ディレクトリーがどの場所に並んでて、そのディレクトリーの中でファイルが何番目に位置しているか…とか丁寧に調べれば大体今何%のところにあるかは出せるような気がする。
とはいえまあ pipe viewer 使えばいいよねとは思う。
$ tar -C /var/lib -c mysql | pv | pzstd -qc | aws s3 cp - s3://...
 247MiB 0:00:04 [50.2MiB/s] [         <=>
pv 使うの忘れた時とかcronからキックされた時用かな。

2017年9月13日水曜日

Test::More::Colorでtoku_bassさんに助けてもらったはなし

TL;DR

@toku_bass さんありがとうございます!!!!1

今から24時間ほど前に 日々の覚書: Test::More::Colorが息してなくてつらい というエントリーを書いた。
そして24時間後の今、

tokubass/p5-Test-More-Color-SupportTest2

あなたが!!!! 神か!!!!
$ perl -MTest::More -M"Test::More::Color qw{foreground}" -E 'say $Test::More::VERSION; ok(1)'
1.302096
ok 1
# Tests were run but no plan was declared and done_testing() was not seen.





( ´-`).oO(あっテキストだとこの感動が伝わらない



「困ったなあ(自分では直せないけど)」から誰かに助けてもらえるのって本当に嬉しい。

ありがとうございます!
@toku_bass ++ !!

2017年9月12日火曜日

Test::More::Colorが息してなくてつらい

TL;DR
最近 Test::More::Color で上手く色がつかない(´・ω・`)

古いのを引っ張り出してきて試してみるけれど、残っているもののうち Test::Simple-1.001014だとOK(色が付く)で、Test::Simpe-1.302086 だとNG(色が付かない)
Test2を使うようにしたこのコミットで、 _print が削除されてるので、v1.302013_001 以降のバージョンではTest::More::Colorが息をしなくなってる。
割と色んなものをTest::More::Color使ってバシバシ色を付けていたので、どうしようかなぐぬぬ。
個人的にはもうどこをフックさせればいいのか全然わからないので仕方なくTest::Simple-1.001014をインストールしてつかっています(´・ω・`)

2017年8月12日土曜日

数秒おきに ERROR: 2006 MySQL server has gone away でmysqlコマンドラインクライアントの接続が切れる

はじまりは @uessy_akrさん のツイート。






“MySQL Server has gone away” はクライアント側のエラー CR_SERVER_GONE_ERROR で、「クライアントは接続張りっぱなしのつもりなのに次のクエリーを投げたら実はサーバー側から接続を切られていた」時に出るエラー。
よって原因になり得るのは
  • タイムアウトが非常に短く設定されていて、クエリーの間隔がサーバー側タイムアウトに引っかかっている
  • 非常に短い間隔でmysqldがダウンしている
    • サーバープロセスがクラッシュすれば当然、接続していたコネクションは破棄されるから。
      • で、mysqld_safeがすぐにmysqldを再起動するので直後の接続は成功して、またクラッシュするやーつ。
    • 地雷erにはこっちの方が馴染みがある。。
    • SHOW STATUS LIKE 'uptime'ps でmysqldの起動時刻を見たり、エラーログを見たりするのもいい。
  • ネットワークなどに問題があり、しょっちゅう接続が(中間のどこかで)プチプチ切れている
    • 体験したことないけど可能性としては一応
あたり。
で、なんかどうもどうやら






mysqldが毎回クラッシュしている方のケースだったらしい。。
signal 6SIGABRT で、MySQL内部のAssertに引っかかるとこれで落ちる。 ちなみに This could be because you hit a bug. は単なる(?)決まり文句で必ず言われるのであんまり関係ない。 体感で一番起きやすいのはInnoDBのページ破損でAssertに引っかかって食らうことが多い印象。
どうやら大正解だったっぽい。




バックアップがある場合はそこから切り戻すのが一番手っ取り早いし、それができない(最新の状態が壊れたところにしかないとか)場合は innodb_force_recovery を駆使して無理矢理データを引っこ抜くしかない。
この手順はここが丁寧(クラッシュしてるわけじゃないからちょっと状況は違うけど、「解決策 - I. InnoDB の修復」が修復方法に当たる)
( ´-`).oO(あれ、他所へのリンクを貼りまくってるだけの記事になってしまった…

2017年7月19日水曜日

GTIDを有効にしているのにレプリケーションのポジションがズレる場合

TL;DR


GTIDを有効にしてレプリケーションを組んでいる( MASTER_AUTO_POSITION=1 )場合、実行済みのトランザクションのGTIDは gtid_executed に記録される。
そしてI/Oスレッドは 自分のgtid_executedに含まれていない イベントがあれば寄越せ、というように接続時にマスターに要求するし、仮に自分のgtid_executedに含まれているイベントが寄越されてもSQLスレッドが同じGTIDのイベントの二重適用を防ぐ。
これでレプリケーションのポジションはズレない。ズレるはずがない。
gtid_executedが間違っていなければ
じゃあどこでgtid_executedが間違うのか。
  1. gtid_executedはそもそもサーバー変数で、オンメモリーだ。取り敢えずのところ、mysqldが停止しなければgtid_executedは間違わない(はず。間違うならそれはバグだ)
  2. mysqldが停止した場合、サーバー変数のgtid_executedは当然値が失われる。その場合、バイナリーログ(openしてれば)とmysql.gtid_executedから復元する
  3. mysql.gtid_executedはバイナリーログをopenしている場合はバイナリーログのスイッチ時とmysqldのシャットダウン時、openしていない場合はコミットの都度書き込む。マスターは確実にバイナリーログをopenしているので前者の動きになる
  4. ところで、ここに稼働中のマスターサーバーから取ったXtraBackupがあるじゃろ?
  5. トランザクションで保護するからmysql.gtid_executedテーブルはXtraBackup開始時の値が保管されるじゃろ?
  6. XtraBackupはバイナリーログは取らないじゃろ? (あればbinlogに記録されている分からgtid_executedをリカバってくれる)
  7. リストアして CHANGER MASTER TO .. MASTER_AUTO_POSITION= 1 するとサクッとズレる
  8. _| ̄|○
リストアしてから CHANGE MASTER TO .. の前に、 RESET MASTER で gtid_executedの中身を吹っ飛ばし、 SET GLOBAL gtid_purged = '..' で設定するのが正しいと思う。
MySQL 5.7.18とそれ以前のmysqldumpでも同じことが起きる( --all-database でmysql.gtid_executedをダンプしてしまう)し、事実 去年隣にいた人 はmysqldumpで踏んでた。これは最近リリースされたMySQL 5.7.19のmysqldumpでは直っているそうだけれども。
まあ何にせよ注意。。