2016年2月5日金曜日

MySQL 5.7.8から導入されたVersion Tokenとやらの動作

ドキュメントはこちら。
MySQL :: MySQL 5.7 Reference Manual :: 5.1.8.4 Version Tokens

サーバーの持ってるトークンとクライアントが持ってるトークンを比較して、一致しなければエラーにしてくれる仕組み。


取り敢えず何はなくともインストール。version_token.soはバンドルされてるので、ドキュメントの通りにINSTALL PLUGINとCREATE FUNCTIONを貼り付ければOK。


mysql> INSTALL PLUGIN version_tokens SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_set RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_show RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_edit RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_delete RETURNS STRING SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_lock_shared RETURNS INT SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_lock_exclusive RETURNS INT SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE FUNCTION version_tokens_unlock RETURNS INT SONAME 'version_token.so';
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE 'version_token_%';
+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| version_tokens_session        |       |
| version_tokens_session_number | 0     |
+-------------------------------+-------+
2 rows in set (0.00 sec)


サーバー側のトークンを設定するにはUDFを使うので、剥き出しで使うにはSELECTステートメントを使う。サーバー側のトークンを設定したり読み出したりするにはSuper権限が必要。

mysql> SELECT version_tokens_set('MySQL=dolphin');
+-------------------------------------+
| version_tokens_set('MySQL=dolphin') |
+-------------------------------------+
| 1 version tokens set.               |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| MySQL=dolphin;        |
+-----------------------+
1 row in set (0.00 sec)


クライアント側のトークンはSETステートメントで指定する…せめてConnector/Cにはmysql_optionsとか無いの?


mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| yoku0825@%     |
+----------------+
1 row in set (0.00 sec)

mysql> SELECT @@version_tokens_session;
+--------------------------+
| @@version_tokens_session |
+--------------------------+
| NULL                     |
+--------------------------+
1 row in set (0.00 sec)

mysql> SET version_tokens_session= 'MySQL=sealion!?';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT CURRENT_USER();
ERROR 3136 (42000): Version token mismatch for MySQL. Correct value dolphin

mysql> SELECT @@version_tokens_session;
ERROR 3136 (42000): Version token mismatch for MySQL. Correct value dolphin

*クライアント側のバージョントークンが指定されていない場合はバージョントークンの比較はされない*
クライアント側でバージョントークンが指定されていて、かつ、サーバーのバージョントークンと違う場合はError: 3136が返される。
権限とかそういうレベルではなく、ステートメントそのものが拒否される状態。


mysql> SET version_tokens_session= 'MySQL=dolphin';
ERROR 3136 (42000): Version token mismatch for MySQL. Correct value dolphin

バージョントークンを設定し直そうとしてもバージョントークンのチェックに引っかかってSETステートメントが転ける。おとなしく繋ぎ直す他にない。


mysql> SET version_tokens_session= 'MariaDB=sealion';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT CURRENT_USER();
ERROR 3137 (42000): Version token MariaDB not found.

存在しないトークンをセットした場合もステートメントが転ける。


最初にチラ見した時に「これ使えば追加要素認証(ユーザー名、パスワードの他にトークンを使う)もできる?」とか思ったけど、クライアント側のトークンをセットしない場合は比較されないし、期待されている値をエラーメッセージに埋め込んでしまうのでそういう用途には使えない。


mysql> SELECT version_tokens_set('MariaDB=sealion');
+---------------------------------------+
| version_tokens_set('MariaDB=sealion') |
+---------------------------------------+
| 1 version tokens set.                 |
+---------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_show();
+-----------------------+
| version_tokens_show() |
+-----------------------+
| MariaDB=sealion;      |
+-----------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_edit('MySQL=dolphin');
+--------------------------------------+
| version_tokens_edit('MySQL=dolphin') |
+--------------------------------------+
| 1 version tokens updated.            |
+--------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT version_tokens_show();
+--------------------------------+
| version_tokens_show()          |
+--------------------------------+
| MariaDB=sealion;MySQL=dolphin; |
+--------------------------------+
1 row in set (0.00 sec)

version_tokens_setだとトークンを追加はできなくて上書かれる。セミコロン区切りで'MySQL=dolphin;MariaDB=sealion'と渡してやるか、version_tokens_editで指定する(editといいつつINSERT .. ON DUPLICATE UPDATEと同じような動き)


mysql> SET version_tokens_session= 'MariaDB=sealion';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| yoku0825@%     |
+----------------+
1 row in set (0.00 sec)

mysql> SET version_tokens_session= 'MariaDB=sealion;sushi=beer';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT CURRENT_USER();
ERROR 3137 (42000): Version token sushi not found.

サーバー側に複数のトークンがセットされている場合、クライアントが申告したトークンが全てサーバーに含まれていればステートメントは成功する。要らないものを付けると失敗する。

ドキュメント の例を見る限り、read_onlyを指定できないマルチマスターな環境下でそれでも特定の粒度でマスターを分ける、みたいな感じに使うことを想定されているんだろうか。Group Replicationですかそうですかわかりますん。

ぱっと思い付いたのは、WEBサービスのAPIをバージョンごとに公開している場合なんかにAPIのコードにSET SESSION version_tokens_session= 'v1=true'みたいなのを入れておいて、v2が作られたらサーバー側のトークンを'v1=true;v2=true'にして、v1が消し去られるタイミングでversion_tokens_edit('v1=false')ってやってやると、API v1から来るクエリーだけを転けさせられるようになる、みたいな感じだろうか。

ただ、サーバー側のトークンは揮発性(MySQLの再起動で消える)ので、ちょっと微妙な感じはする。どう使えるだろう。


あとこのプラグインの面白いところは、これAudit PluginなのでAudit Pluginの書き方のサンプルとして面白かった。 このへん。

2016年1月15日金曜日

AnemometerというMySQLスローログ専用の可視化ツールの弱点と、その克服スクリプト

一部の人にしか知られていない Anemometer というMySQLのスローログ専用の可視化ツールがある。

box/Anemometer: Box SQL Slow Query Monitor


中身はpt-query-digestの テーブル出力機能 (サマライズした結果をMySQLのテーブルに保存する機能があるのだ)に依存していて、スローログの可視化というよりはpt-query-digestの可視化というのがたぶん正しい。

だけどこのやり方にはちょっと弱点があって、pt-query-digestはクエリーをサマライズする時に発生時間の情報を 「そのダイジェストが最初に現れた時間(ts_min)」と「そのダイジェストが最後に現れた時間(tx_max)」 という値にサマライズしてしまう。よく見る出力結果の中では"Time range"として表示されている。


# Query 14: 0.00 QPS, 0.00x concurrency, ID 0xAF17C328E1020443 at byte 10391443
# This item is included in the report because it matches --outliers.
# Scores: V/M = 13.70
# Time range: 2015-11-02 10:55:08 to 2015-12-18 15:20:14
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count          0      13
# Exec time      0    163s      2s     51s     13s     29s     13s      9s
# Lock time      0     5ms   271us   445us   354us   424us    65us   301us
# Rows sent      9 544.87k   3.91k 104.73k  41.91k  97.04k  33.62k  46.68k
# Rows examine   0  12.00M 238.87k   3.04M 945.00k   1.46M 808.81k 725.01k
# Query size     0  24.29k   1.87k   1.87k   1.87k   1.86k    0.00   1.86k

コマンドラインからの出力結果を眺める分には便利なんだけど、グラフ化しようという時にこれはつらい。Anemometerはts_min(最初にスローログに現れた時刻)をグラフにプロットするので、今既にあるスローログをpt-query-digestで食わせてAnemometerで表示させると、チェックサムごとにts_minの時刻に1回だけスパイクしたようなグラフになってしまう。

これは、上手くない(´・ω・`)






pt-query-digest側でこの集約を無効化できればいいんだけど、オプションとしては存在せず、中に手を入れるにしても結構奥まったところにあっていじくりたくない。

定期的にpt-query-digestに--since, --until オプションを使って食わせてもいいんだけど、秒単位とは言わずとも分単位くらいでは見たい…となると、1日ぶんのログファイルを食わせるのに見るかどうかもわからないのに1440回pt-query-digestを起動しなければならない。それも嫌だ。


という訳で乱雑に書いたPerlスクリプト。Anemometer用にスローログを食ってくれるのでanemoeater(どや
my_script/anemoeater.pl at master · yoku0825/my_script
yoku0825/anemoeater


$ ./anemoeater.pl --docker path_to_slowlog

シンプル。--dockerを使わなくとも、Anemometerが既に構成されている環境があれば、--hostとか--userで指定してやればいい。--dockerに任せると構成済みの yoku0825/anemometer を起動して、スクリプトで1分ごとに丸めて送る。

パイプでpt-query-digestを呼ぶので結構遅い(11Mのスローログを食わせるのに、pt-query-digestで直接食わせると10秒ちょい、anemoeaterだと24パラにしても1分ちょい)

これで無事、過去のスローログをまとめて食わせても




ちゃんとジグザグしてくれるようになった。

2015/12/01 00:00~2016/01/01 23:59のログを8並列でぶち込むのはこんな感じで書きます。

$ ./anemoeater.pl --since 201512010000 --until 201601012359 --parallel 8 --docker path_to_slowlog
real    0m17.471s
user    1m47.847s
sys     0m11.117s

これでDockerでAnemometerの起動まで終わってるんだから、都度都度パースして突っ込めそう。しめしめ。


【2016/02/03 17:43】
yoku0825/anemoeater として別リポジトリーにしました。

2016年1月14日木曜日

MySQL Bug #79977 "utf8mb4_unicode_520_ci don't make sense for Japanese FTS" で言いたいこと

英語で書いてたら自分でもよくわからなくなってきたので。

MySQL Bugs: #79977: utf8mb4_unicode_520_ci don't make sense for Japanese FTS


* 本質的には MySQL Bugs: #76553: Sushi-Beer issue of MySQL with utf8mb4 の関連。
  * Unicode実装が不完全だから起こっている問題。


* 丸め問題

  * motherを意味する書き方に、はは(ひらがな)とハハ(カタカナ)とハハ(半角カタカナ)があって、
  * これらは(文字の形は違うけど)同じ音で同じ意味だから、大概の場合同じ文字として扱ってくれると嬉しい。


* 病院美容院問題

  * hospitalを意味する"びょういん"とheir salonを意味する"びよういん"
  * "ょ"と"よ"は文字の形は似てるけど、ほとんどの場合これは違う意味を持つから区別しないといけない。


* ハハパパ問題

  * Bug #76553でも説明されてるけど、ハハ(濁音なし)はmother、ババ(濁点)は"grand mother"、パパ(半濁点)は"daddy"を意味する。
  * これらは区別されないと困る。超困る。


* 最後に半角全角問題

  * "MySQL"と"MySQL"は同じに扱いたいよね。

* Hiragana-Katakanaと半角全角を区別しちゃうのは、不便だけど我慢できる。
* でも拗音促音と濁点半濁点を区別 *されない* のは(特に全文検索の側面で)機能要件を満たせない。LIKE演算子でもそうだけど。
  * 「びょういん」って検索して美容院が結果に含まれたら変でしょ?
* あと、これ、UNIQUE制約をぶっ壊す可能性があるのよね。
  * UNIQUE KEY(家族関係)って制約があって、俺には母親がいるからハハって入れて、俺に父親がいるからパパって入れようとすると、エラるよね? (いわゆる kamipoさんのハハパパ問題


表にまとめるとこんな感じ。ホントはutf8mb4_japanese_ciが欲しいところだよねって。

|                    | utf8mb4_bin | utf8mb4_general_ci | utf8mb4_unicode_ci | utf8mb4_unicode_520_ci|
|--------------------|-------------|--------------------|--------------------|-----------------------|
| Hiragana-Katakana  | cs (unkind) | cs (unkind)        | ci (good)          | ci(good)              |
| Youon              | cs (good)   | cs (good)          | ci (critical)      | ci(critical)          |
| Dakuten-Handakuten | cs (good)   | cs (good)          | ci (critical)      | ci(critical)          |
| Wide-Narrow        | cs (unkind) | cs (unkind)        | ci (good)          | ci(good)              |
| Sushi-Beer         | cs          | ci                 | ci                 | cs                    |

2016年1月13日水曜日

MySQL 5.7.11でdefault_password_lifetimeのデフォルトが0になるらしい!

日々の覚書: MySQL 5.7.4で導入されたdefault_password_lifetimeがじわじわくる の公開以来大反響をいただいていた (ブクマ がすごいことになっていて、思わず ばぐれぽ にブクマのリンクを貼り付けたほど)default_password_lifetime が。

5.7.11でついにデフォルト0になる!!! やった!!! やったよ!!!

ドキュメントはもう"default: 0 (>= 5.7.11)"の記載になってる。


これで、

MySQL 5.7にアップデートしてから

360日後にやってくる

_人人人人人人_
> 突然の死 <
 ̄Y^Y^Y^Y^Y^Y^ ̄

はなくなりました! (∩´∀`)∩ワーイ


いいぞ○racle!
"Affects me"してくれたみなさんもありがとうございました。


( ´-`).oO(さて、また自分が罠にならないように、あちこちのもの直さないとなー


【2015/01/14 13:59】
MySQL Bugs: #77277: default_password_lifetime should be set 0 as implicit default value

の最後の方で、Morgan Tocker(Oracle MySQL Community Manager改めOracle MySQL Product Manager)が他にも色々(default_password_filetime > 0だったらワーニング出そうぜ とか SYSスキーマでいつexpireされるか見るビュー作ろうぜ とか)出してくれてる。



@morgo++

2015年12月31日木曜日

2015年のMySQL 5.7騒動を振り返って

ポエムです。
もう1時間半ちょっとで年も明けますが、今年も色々な方にお世話になりました。ありがとうございました。


2015年は振り返るまでもなくMySQL 5.7漬けでした。
というか、去年はまだMySQL 5.7漬けじゃなかったんですね。そのことにむしろびっくり。もっと長い間 disり 調べ続けていたような気がします。

2015/03のPercona Liveに合わせてリリースされた(と思う) MySQL 5.7 最大のリリース、MySQL 5.7.6-m16 に始まり、あっという間にリリースされた MySQL 5.7.7-rc 、フィーチャーフリーズされずに出てきた MySQL 5.7.8-rc (RC2) 、それどころかフィーチャーフリーズされないまま、RC2からGAで機能追加がされているという MySQL 5.7.9-GA 。。

ちなみにMariaDB 10.0が 10.0.9(RC2) でフィーチャーフリーズせずに新機能を追加した時に「馬鹿なwwww」とか思った記憶があるんですが、見事にMySQLにブーメランになって返ってきましたね。アイタタタタ。

そしてMySQL史上類を見ない膨大な量のリリースノート。htmlタグ込みとはいえ、5.7.6だけじゃなくて他のも十分すごい。


$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-1.html | wc
   2631   13546  158162

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-2.html | wc
   5028   26154  307853

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-3.html | wc
   2392   11339  139404

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-4.html | wc
   3056   14810  184922

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-5.html | wc
   4322   22095  261145

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html | wc
   4760   24208  302234

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-7.html | wc
   1471    6774   91604

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-8.html | wc
   4178   21629  268333

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-9.html | wc
   3054   14926  186386

$ curl -s http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-10.html | wc
   1278    5749   78511

こうしてみると確かに5.7.7は確かにRCだけど5.7.6から1か月くらいで出たし、印象薄いな。。
ともあれ、無事(大きな事故もなく?)GAとしてリリースされたMySQL 5.7、2年越しで(節目節目でとはいえ)更新を追っかけ続けて、結構疲れました。しばらくはこの1年間の貯蓄ででつつましく導入&運用していきたい感じ。

slideshare を見ても、4月MyNAのInnoDB FTSを皮切りにMySQL 5.7を(リライトも再演もやったので、結構な回数になった)積極的にやってきたんだなーって感じになりました。

やってきたんだなーっていうか、やりすぎたなーと思っています(二重の意味で)
"MySQL 5.7"で"罠"がサジェストされるとは。正直すまんかった。

「5.7はn倍速い」
「5.7は新機能がいっぱい」
うん、それは知ってる。でも気を付けなきゃユーザーが痛い目見るものもあるよね? それも紹介してよ。

そんなコンセプトでGAまで活動を続けた結果。


MySQL :: 資料ダウンロード > MySQL 最新情報セミナー2015秋 > MySQL 5.7 新旧パラメタ比較

「どこかの誰かさんがMySQL 5.7のパラメーターは罠い罠い言い続けた結果」
「お客様からも"5.7は罠なの?"とお問い合わせをいただくようになり」
「こんなものを用意した」

(∩´∀`)∩ワーイ

これが俺の今年の一番の成果であることは間違いありません。
これを「○racle公式の資料」として掲載するために、某氏とか某氏とか結構骨を折ってもらったんじゃないかという気もします。ありがとうございました。


あ、この資料、2015/12/31 22:16 JST時点の版だとlog_error_verbosityのデフォルト値が間違ってるので注意してくださいね :-P

それではまた来年もよろしくお願いします。
来年は何してるんだろうなー。

2015年12月25日金曜日

MySQL Routerをlocalhostに置いたらどれくらいの遅延になるかを考えるメモと、MySQL Routerは本当につらくなかったのかのまとめ

この記事は MySQL Fabric&Routerつらくない Advent Calendar 2015 の25日目の記事です。

まずはどうやって遅延を計測しようか考えているメモをだだだと書きなぐる。後半にまとめっぽいものを。

計測環境。サーバーもクライアントもc4.xlarge。サーバーはyumリポジトリーで5.7.10を突っ込んで起動しただけ。


# rpm -i http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm
# yum install -y mysql-community-server
# service mysqld start
# grep password /var/log/mysqld.log
# mysql -p
mysql> UNINSTALL PLUGIN validate_password;
mysql> CREATE USER yoku0825;

クライアントは接続してSELECT NOW()して切断するだけのもの。


$ cat -n test.cc
     1  #include <unistd.h>
     2  #include "mysql.h"
     3
     4  int main()
     5  {
     6    MYSQL mysql;
     7    int m;
     8
     9    mysql_init(&mysql);
    10
    11    for (m= 0; m <= 10000; m++)
    12    {
    13      mysql_real_connect(&mysql, "172.31.15.74", "yoku0825", "", NULL,
    14                         3306, NULL, 0);
    15      mysql_query(&mysql, "SELECT NOW()");
    16      MYSQL_RES *res= mysql_store_result(&mysql);
    17      mysql_close(&mysql);
    18    }
    19  }
$ gcc -I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient -g3 -O0 test.cc
$ time ./a.out

real    0m6.865s
user    0m0.248s
sys     0m0.552s

直接接続だと大体700usくらい。
c4.xlargeは4コアあるので4つ起動してみると


$ time (./a.out & ./a.out & ./a.out & ./a.out ; wait)


real    0m22.573s
user    0m0.508s
sys     1m10.168s

これくらい。ダメだ。ほとんどsysにもっていかれてる。まともに並列処理を書けるC力がほしい。
Routerはソースからコンパイル。サーバーホストだけを指定して単にプロキシーするだけの状態にする。


$ wget http://dev.mysql.com/get/Downloads/MySQL-Router/mysql-router-2.0.2.tar.gz
$ tar xf mysql-router-2.0.2.tar.gz
$ cd mysql-router-2.0.2
$ cmake .
$ make
$ sudo make install
$ sudo cp -ip /usr/local/share/doc/mysqlrouter/sample_mysqlrouter.ini /usr/local/etc/mysqlrouter/mysqlrouter.ini
$ sudo vim /usr/local/etc/mysqlrouter/mysqlrouter.ini
..
[routing:basic_failover]
# To be more transparent, use MySQL Server port 3306
bind_port = 7001
mode = read-write
destinations = 172.31.15.74
..

mysql_real_connectの向き先を127.0.0.1:7001に変えて、中央値は大体これくらい。


$ time ./a.out

real    0m8.816s
user    0m0.192s
sys     0m0.916s

$ time (./a.out & ./a.out & ./a.out & ./a.out ; wait)

real    0m9.761s
user    0m1.432s
sys     0m4.192s

コンテキストスイッチが減ってRouter経由の方が快適という結果に。top見てても遥かに良い感じ(mysqlrouterが100%張り付くので、a.outは20%くらいずつになる。これが結果として綺麗にキャップになっていい感じになるという。。)
もっとちゃんとしたクライアントだと、シングルスレッドのmysqlrouterが問題になってくると思う。取り敢えず、10秒で10000コネクション * 4プロセスだから4000connection/sならさばけてる(mysqlrouterの%usrが張り付くけど)

これを超えてくる場合、mysqlrouterのプロセスを複数起動して分散させることになるし、MySQL Routerはどうやら動的に設定を変えることはできないので、gracefulっぽくするためには



tcpdump + pt-query-digestでレイテンシーを見てみると、直接接続の場合はmysql_real_connectの中央値が467usにクエリーの中央値が152us。

# Query 1: 1.40k QPS, 0.70x concurrency, ID 0x5D51E5F01B88B79E at byte 42297981
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.00
# Time range: 2015-12-24 10:33:50.782145 to 10:33:57.922968
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         33   10000
# Exec time     75      5s   431us     1ms   498us   568us    39us   467us
# Rows affecte   0       0       0       0       0       0       0       0
# Query size    43 292.97k      30      30      30      30       0      30
# Warning coun   0       0       0       0       0       0       0       0
# String:
# Databases
# Hosts        172.31.5.70
# Users        yoku0825
# Query_time distribution
#   1us
#  10us
# 100us  ################################################################
#   1ms  #
#  10ms
# 100ms
#    1s
#  10s+
administrator command: Connect\G

# Query 2: 1.40k QPS, 0.22x concurrency, ID 0xF450CBDB69FA3A64 at byte 42299217
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.00
# Time range: 2015-12-24 10:33:50.782371 to 10:33:57.923132
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         33   10000
# Exec time     24      2s   142us   419us   159us   204us    16us   152us
# Rows affecte   0       0       0       0       0       0       0       0
# Query size    17 117.19k      12      12      12      12       0      12
# Warning coun   0       0       0       0       0       0       0       0
# String:
# Databases
# Hosts        172.31.5.70
# Users        yoku0825
# Query_time distribution
#   1us
#  10us
# 100us  ################################################################
#   1ms
#  10ms
# 100ms
#    1s
#  10s+
# EXPLAIN /*!50100 PARTITIONS*/
SELECT NOW()\G


Router経由だと固定でレイテンシーがあがるだけだと思ってたけど、Connectの方がぁゃιぃ。フツーのクエリーを計測するよりも(ルーティング判定のぶん)コストが高そう。もうちょっと別のクエリーも投げるパターンにして計測しないとダメだ。

単純クエリーで10usくらいの差なら、まともなクエリーになるに従って十分問題ないと見ていいはず。あ、でも、往復のパケット量が増えればそれぞれのパケットにレイテンシーが載るから結果セットが転送されきるまでのレイテンシーは無視できないのかも知れない。これも計測パターン増やさないと。


# Query 1: 1.07k QPS, 0.62x concurrency, ID 0x5D51E5F01B88B79E at byte 5655092
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.00
# Time range: 2015-12-24 10:35:26.307560 to 10:35:35.641810
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         33   10000
# Exec time     76      6s   488us     1ms   581us   690us    65us   541us
# Rows affecte   0       0       0       0       0       0       0       0
# Query size    43 292.97k      30      30      30      30       0      30
# Warning coun   0       0       0       0       0       0       0       0
# String:
# Databases
# Hosts        172.31.5.70
# Users        yoku0825
# Query_time distribution
#   1us
#  10us
# 100us  ################################################################
#   1ms  #
#  10ms
# 100ms
#    1s
#  10s+
administrator command: Connect\G

# Query 2: 1.07k QPS, 0.19x concurrency, ID 0xF450CBDB69FA3A64 at byte 45635077
# This item is included in the report because it matches --limit.
# Scores: V/M = 0.00
# Time range: 2015-12-24 10:35:26.307968 to 10:35:35.642044
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count         33   10000
# Exec time     23      2s   145us   577us   174us   214us    28us   159us
# Rows affecte   0       0       0       0       0       0       0       0
# Query size    17 117.19k      12      12      12      12       0      12
# Warning coun   0       0       0       0       0       0       0       0
# String:
# Databases
# Hosts        172.31.5.70
# Users        yoku0825
# Query_time distribution
#   1us
#  10us
# 100us  ################################################################
#   1ms
#  10ms
# 100ms
#    1s
#  10s+
# EXPLAIN /*!50100 PARTITIONS*/
SELECT NOW()\G

MySQL Fabricが載ってくると当然結果は変わるだろうから、次はFabric Cache Pluginバージョンも試す(台数がかさむなぁ。。)

ダラダラ書いたけど、次に測らないといけないところはわかった気がする。


さて、本題(?)

MySQL Routerは本当につらくなかったのか?

残念ながら つらくなかったです。


# 情報量(主にドキュメント)

* 初期のMySQL Fabricはひどかった。
* それよりは、今のMySQL Routerはマシ。
* ただし、今のMySQL Fabricの方がMySQL Routerより情報ある(と思う)
* つまり、結局は今後に期待。
  * ただ、MySQL FabricよりはMySQL Routerの方が流行ると思うので、日本語の情報も充実していくんじゃないかなぁ。

# 安定性

* MySQL Fabricも良くなってるんじゃないか疑惑
  * 去年試してた時 は結構さっくり刺さってハングしてたけど、今年は1回も突き刺さってない
* MySQL Routerは取り敢えず安定。Fabric Cache Plugin使ってる時の動作が若干不安…?
  * 再現しないので、要追試。

# 使いやすさ、使いたさ

## MySQL Fabric

* グラフィカルなCLIでレプリケーションクラスターの管理。したい。
* GTID依存なので、まずGTIDをONにして回るのが大変。
  * バックアップからリストアしたやつをレプリケーションクラスターに追加とかもGTID依存なので、5.6だと苦労しそう(5.7ならきっといける)
  * GTID依存だからこそ色々簡単なんだよなー、と思うとGTIDはやっぱりいいものだったのだな。
  * MariaDBさんがいるとダメ(GTIDの実装が全然違って互換性がない)
* いろんな実装がコネクター依存すぎてブラックボックスが超怖い。Connector/Jでは上手くいくけど他はダメとかフツーにありそう。

## MySQL Router

* 次世代MySQL Proxyとして使い始められる
  * スレーブの分散、Routerに置き換えようかと真面目に検討中。
  * Lua書かなくていい。ただし、MySQL RouterのプラグインはC++な上にドキュメントがまだないぽい。
* Fabric Cache PluginはMySQL Fabricのブラックボックスを解消するものなので、とてもイケていると思う。
  * ただ、アレだけで1章書いてもいいんじゃないかってくらいもっと説明して欲しいところがいっぱいある。。
* MySQL Fabricと連携させないなら、4.0 5.5とかでもイケるんじゃないかな(old_passwordの壁があるか…?)

# デバッグとソースコードリーディング

* MySQL FabricはPythonで、俺のPython力の欠如によりつらかった
  * ゆるぼ: Pythonista
* MySQL RouterはC++でgdbで突き刺すスタイルなのでデバッグが楽
* とはいえこれはMySQLオタク向けの項目なので、ほとんどの人には無縁

というわけで!

つらくないよ!

つらくないから、僕と結託して 地雷 MySQL Router友達になってよ!

お待ちしております :)

ConoHaで自作Billing Alertを使っているはなし

このエントリーは ConoHa Advent Calendar 2015 の25日目のエントリーです。メリークリスマス!

22日目に既に ConoHaちゃんが好きすぎるので、WebAPIを叩くためのGemを(途中まで)作ってみた という記事がありますが、どう見てもカブっています本当にありがとうございました。
(わたしはConoHaちゃんが好きすぎて作ったわけではないですが)

TL;DR

すぐに落とせば大丈夫だと思ってConoHaの一番高いヤーツでベンチマークを流したら、消すのを忘れて痛い目を見た(巷で噂になっているアレほどではない)

ちなみに 論理削除Casual Talks のときの、「tpcc-mysqlを論理削除に魔改造したらどれくらい速く / 遅くなるか」というものすごくどうでもいいベンチマークに使いました。つらい。

使い方

$ git clone https://github.com/yoku0825/p5-conoha-api.git
$ cd p5-conoha-api
$ cpanm --installdeps .
$ ppit set conoha
---
"password": 'your_api_password'
"tenantId": 'your_tenantId'
"username": 'your_api_username'
$ bin/conoha-billing
775

( ゚д゚) あら簡単

自分用なので色々投げやりです。username, password, tenantIdはConfig::Pitに"conoha"で設定されているとハードコードしてあります。きっと車輪の再発明で、どこにでもいくらでもクライアントは転がってそうなんですが、調べるよりはHTTP::Tinyで書いた方が早いなと思って自分に必要なものだけbinに詰めた感じ。

基本的にTokenさえ取ってdefault_headersに詰めた後はどうにでもなるので、使いたいAPIが出てきてから拡張するのは難しくない、はず。

bin/conoha-billingは単に最新のinvoiceを取ってきて数字だけぬるっと返すので、cronに

$ crontab -l
0 10 * * * [ $(/home/yoku0825/git/p5-conoha-api/bin/conoha-billing) -gt 1000 ] && (echo "Billing alert!!" | mail -s "conoha billing alert" yoku0825@gmail.com)

としておくだけで、簡単に事故は防げるわけです。がんばれ、過去の俺。

yoku0825/p5-conoha-api



ちなみに普段、ConoHaのVPSは
* MroongaのDockerイメージをビルドしたりテストしたり
* rpmパッケージをビルドしたり
* MySQLのソースコード読むのに使ってたり
* Slackに定期的にポストを投げ込んでくれるbotがDockerコンテナーで動いてAPIの口を開けてたり
* GitBucketのDockerコンテナーが動いていたり
* MySQLが4つくらい起動していたり
* MySQL Routerがつらくなかったり
します。

グローバルIP持っててお安いのはいいですね(でもDockerとか色々詰め込んでるから、現プランのSSD 50GBより旧プランの100GBの方が良かったかな。。)

ちなみに3次元のこのはちゃんは初代の人がすきです :)


それでは、良いお年を。