2018年9月27日木曜日

db tech showcase Tokyo 2018に参加してきました

2018/09/19~21の db tech showcase Tokyo 2018 | db tech showcase (このサイト、URLに年を示すものが何もないので来年になると上書きされちゃうんですよね) に参加してきました。
タイトルは「Dive into MySQL Error」、その名の通り、MySQLのエラーのちょっと深いところを覗いていくセッションです。
当日朝イチで雨にも関わらず足を運んでくださったみなさま、スライドをご覧になっていただいたみなさま、
3日目朝イチのセッションを1日目の懇親会終了後に即決してくださった インサイトテクノロジー の石川さん、松尾さん、
本当にありがとうございました。




1日目の懇親会が終わるまでは完全にオーディエンスの予定だったので資料は何も用意しておらず、取り敢えず @mita2 さんと @keny_lala さんの発表だけ聞ければいいかな、と思いながら、「MySQL UDFとGo言語で作るビッグデータ前処理基盤」 の話が気になって聞いていたりしました。

MySQL at Yahoo! JAPAN

  • 規模すごい…(小並感)
  • 「サイズが小さいのがたくさん増えていく」
  • Percona XtraDB Cluster

LINEのMySQL運用について

  • MySQLとRedis
  • 通称mondb+
  • HA用のスクリプトを自分でメンテするとつらいけど、ブラックボックスより安心しませんか? :-P

日本発!MySQL UDFとGo言語で作るビッグデータ前処理基盤

  • Goで作った超すごい住所名寄せライブラリーをMySQLのUDFから叩く
  • MySQLのUDFがどうこうじゃなくてGoで作ったライブラリーがすごい
  • 質疑時間で MySQLでUDF作ってる人 が「わたしはこうやってますよ」って言ってたのが面白かった
  • 繰り返しになるけれどMySQLどうでもいい、Goで作ったライブラリーが超すごい
    • ミーカンパニーさんもちゃんと認識していたw

1日目のセッションを聞いて、懇親会で ミーカンパニーさんGoのライブラリーを すごいすごい言って、いい感じに酔っぱらったところで3日目の登壇を快諾していただき(その場にいた @tadayama_jp さんが「俺の時は怒られたのに…」って仰ってて超笑った(そこに立ってらしたのを認識してなかったので))、半日でスライドを仕上げて金曜日に至りました。
今年はなんか忙しさにかまけて、一日中いる日も無かったし飲んだのも初日だけでしたが、セッションやらせてもらってすごく楽しうございました。
どうもありがとうございました。

「max_allowed_packetの基本的な動き」がどうしてそうなるのかのはなし

TL;DR

  • 1SQLのサイズ上限は、max_allowed_packetとnet_buffer_lengthで制御される。
  • max_allowed_packetはグローバルにデフォルト値を持ち、コネクション確立時にセッションにコピーされ、セッション変数は読み込み専用である。(セッションに適用するにはconnectなど再接続が必要)
  • net_buffer_lengthはグローバルにデフォルト値を持ち、コネクション確立時にセッションにコピーされ、セッション変数も変更可能である。
  • net_buffer_length => max_allowed_packetの場合、net_buffer_lengthが1SQLの上限値になる。
  • net_buffer_length < max_allowed_packetの場合、max_allowed_packetが1SQLの上限値になる。
とまとめられているが、これは

ということ。

まず、 max_allowed_packet の上限に引っかかった時に出るエラーである、 “Got a packet bigger than ‘max_allowed_packet’ bytes” からたどっていくことにする。
(See also Dive into MySQL Error - Speaker Deck
ソースコードは5.7.23のものにしていますよん。
$ grep "max_allowed_packet" include/mysqld_ername.h
{ "ER_NET_PACKET_TOO_LARGE", 1153, "Got a packet bigger than \'max_allowed_packet\' bytes" },
{ "ER_TOO_LONG_STRING", 1162, "Result string is longer than \'max_allowed_packet\' bytes" },
{ "ER_WARN_ALLOWED_PACKET_OVERFLOWED", 1301, "Result of %s() was larger than max_allowed_packet (%ld) - truncated" },
MySQLへのJDBC接続で、とあるバグを踏むまでの話 -(1)「max_allowed_packet」の基本的な働き - なからなLife ではバルクインサートだったので、 ER_NET_PACKET_TOO_LARGE の方だと思う。
$ grep -r ER_NET_PACKET_TOO_LARGE . | grep -v mysql-test
./include/sql_state.h:{ ER_NET_PACKET_TOO_LARGE                 ,"08S01", "" },
./include/mysqld_ername.h:{ "ER_NET_PACKET_TOO_LARGE", 1153, "Got a packet bigger than \'max_allowed_packet\' bytes" },
./include/mysqld_error.h:#define ER_NET_PACKET_TOO_LARGE 1153
./libmysql/libmysql.c:      else if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
./sql-common/client.c:    set_mysql_error(mysql, net->last_errno == ER_NET_PACKET_TOO_LARGE ?
./sql-common/client.c:    if (net->last_errno == ER_NET_PACKET_TOO_LARGE)
./sql/net_serv.cc:    net->last_errno= ER_NET_PACKET_TOO_LARGE;
./sql/net_serv.cc:    my_error(ER_NET_PACKET_TOO_LARGE, MYF(0));
./sql/rpl_rli_pdb.cc:    with error ER_NET_PACKET_TOO_LARGE.
./sql/rpl_slave.cc:          mi->report(ERROR_LEVEL, ER_NET_PACKET_TOO_LARGE,
./sql/share/errmsg-utf8.txt:ER_NET_PACKET_TOO_LARGE 08S01
./include のやつは定義系だし、 ./libmysql, ./sql-common のやつは == で比較しているのでこのエラーを受け取った後にどうするかの処理だろう。
./sql/rpl_* はレプリケーション関連なので取り敢えずパスして、 ./sql/net_serv.cc の中で代入しているからここが本丸のような気がする。
grepで引っかかった2行は net_realloc という関数の中に入っていて、名前から察するにここが
パケットメッセージバッファーは net_buffer_length バイトに初期化されますが、必要に応じて max_allowed_packet バイトまで大きくできます。
のパケットメッセージバッファーを大きくする処理なのであろう。
コイツが呼ばれているところを探しに行く。
$ grep -r net_realloc .
./include/mysql.h.pp:my_bool net_realloc(NET *net, size_t length);
./include/mysql_com.h:my_bool net_realloc(NET *net, size_t length);
./libmysql/libmysql.c:    res= net_realloc(net, buf_length + length);
./sql/net_serv.cc:my_bool net_realloc(NET *net, size_t length)
./sql/net_serv.cc:  DBUG_ENTER("net_realloc");
./sql/net_serv.cc:      must match the size of the buffer allocated in net_realloc().
./sql/net_serv.cc:  if ((pkt_data_len >= net->max_packet) && net_realloc(net, pkt_data_len))
./libmysql とはlibmysqlclient.so(Connector/C)のコードなので外すとするとまだ同じ ./sql/net_serv.cc の行。開いてみると net_read_packet の中で、「今のパケットメッセージバッファーよりもデータが大きければ net_realloc 」な記述に当たる。
というわけで、
  • net_buffer_length => max_allowed_packetの場合、net_buffer_lengthが1SQLの上限値になる。
の理由は、 net_buffer_length のサイズでアロケートされたパケットメッセージバッファが足りなくなって拡張しようとした時に初めて max_allowed_packet と比較されてエラーに分岐するから、でした。
Cこわくないよ!
(あっdbts2018のブログがまだ下書きn