2015年6月8日月曜日

MySQLプラグイン作成時のWindows版だけにある「シンボルの壁」

また、需要が無いかも知れない情報かもですけど、嵌ったのでメモ代わりに残しておきます。ちなみに、コンパイラの種類、バージョン、オプションをちゃんと合わせないとアライメントずれでバイナリ互換が無くなる等のプラグイン作成基礎知識には触れません。普段Linuxの人が、気楽にWindows版も対応しようとした場合の落とし穴についてのみです。あと、私のon Windows開発歴はほぼ0なので間違っていたらごめんなさい。

MySQLプラグインは基本的に、プラグイン側からmysqld側のシンボルを参照することが多いです。例えばストレージエンジンプラグインならmysqldの中のhandlerクラスを継承して実装する必要があったり、プラグインからmysqldの状態を知ろうとすれば、そのグローバル変数やら、保護しているmutexやらも必要になります。

基本的にLinuxでは、同じヘッダファイルさえ読めば公式配布バイナリでも大概のものにアクセスできますが、Windowsではそうはいきません。Windowsの実行形式(exeとかdllとか)では、ダイナミックリンク時にどのシンボルを見せるか(EXPORT)、見に行くか(IMPORT)を明示的に指定する必要があります。デバッグ用のシンボル(.pdb)はプラグイン読み込み時には関係ありません。

mysqld.exeからプラグインdllを読みに行く場合は、プラグインの情報を記述した構造体を見に行くだけですから問題にはあまりならないでしょう。(プラグインのサンプルに則ればプラグイン側の必要な情報はEXPORTされるはずで、変更の必要はまず無いはず。)

問題はその逆です。mysqld.exeのどのシンボルがプラグインから見えるかは、mysqld.exe のリンク時に決まります。ソースツリーを見ると、mysqld.exe のリンク時に mysqld.def を作って読ませています。win/create_def_file.js はその時に利用される、.libファイルからシンボルを抽出して .def ファイル形式に変換するもので、それは sql/CMakeLists.txt の中で使用されています。その対象は、

"FOREACH (CORELIB sql binlog slave mysys mysys_ssl dbug strings binlogevents_static)"

となっており、innobase.lib は含まれていないので、通常は mysqld.exe のInnoDB関連にはアクセスできません。この行に 'innobase' を加えると、InnoDBのシンボルも参照可能な mysqld.exe がビルドされます。

また、プラグイン側も適切にIMPORT設定しなければいけません。mysqld.exe のリンクの際に、リンクライブラリmysqld.lib(EXPORTの確認用ライブラリ)が出力され、プラグインの作成時にリンクされるようになっていて、エラーが出るので取りこぼすことは無いと思います。リンク時にエラーが出た参照は、インクルードファイルで 'extern' で読むところを、'extern MYSQL_PLUGIN_IMPORT' として明示的にIMPORTする必要があります(実体は"__declspec(dllimport)")。これはプラグイン側だけの問題で、mysqld.exeのビルドには関係しません。

という以上の事情から、たとえば「InnoDBの内部が見れる便利なinformation_schema作ったよ。みんな使ってみて!」と言っても(いや、まだ作ってないですが…)、Windows版だけは折角プラグインとしてビルドしても、mysqld.exeもビルドしなおさなければいけないわけです。

一応バグとして上げましたが、需要が無ければ直るかどうか。。。(Bug#77251)

直れば色々某プラグインとかもWindows用InnoDBダイレクトアクセス版とか作れるようになるのと思うのですががが。(handlerから使うとMySQL形式-InnoDB形式変換が意外と重かったと思うので。) いや、Linux版だけなら作れるわけですががが。

2015年4月13日月曜日

MySQL + jemalloc on Windows

需要が無いかもしれませんが、産まれて初めて、MySQL on Windows について触れます(起動したのも初めて)。 私自身はWindows上のMySQLを性能評価できる環境には無いのですが、 敢えてSQL ServerとWindowsという相手のホームグラウンドでアウェイ対決させよう、 という猛者のためにこのエントリを残します。 猛者向けですし、私は無責任でおねがいします。陰ながら応援します。

MySQL on Windows

MySQLの性能・スケールを追求・普及していく上で、Windows上での性能評価はやはり避けては通れないと思います。 (実際にどこまで使うかは別として、正確な要素比較は必要でしょう。) しかし、標準APIを使わなければいけない「地の利が無い」状態では不安が残ります。 できるだけ不安要素は予め解消しておきたいものです。

経験上、不安要素は3つあります。

(1) メモリアロケータ

Linuxでも、glibcのmalloc()はコンパクトですがスケールがよろしくないので、 jemallocなどのスケールするメモリアロケータをLD_PRELOADで読み込まないと、 InnoDBはそのポテンシャルを発揮できません。 当然Windowsでも似たような状況であるはずです。

(2) ファイルIO

実際に遭遇したものを挙げると、fsyncとwriteの並列性が悪く、アプリケーション側でシリアライズしないと 性能が落ちてしまう事象に遭遇した(間接的にクレームを受けた)ことがあります。 更新が多く、トランザクションログの書き込みがボトルネックとなるような場合は注意が必要でしょう。

(3) イベント

InnoDBでは、内部のmutex/rw_lockのロック待ちでは、スピンとイベント待ちの2段構えです。 バッファプールのブロックあたりmutexとrw_lockがあって、現状それぞれイベントを持ちますから、 ギガバイト単位のバッファプールを確保すると、10万〜100万個単位でイベントが作られます。 Linux/Unix系では今のところ目立った問題になったことは無いのですが、 過去Windows系で問題(イベント数が多いと性能が落ちる)になっているのを見たことがあります。 何かエディションの問題だったのかも知れませんが不安が残ります。

(3)については、mutex/rw_lock競合が少なければ多分深刻ではなく、(2)については更新の激しい要件を避ければ良さそうですが、 (1)はどんな処理でも関係することですので、解決しなければ安心できません。

jemalloc による標準malloc関係の差し替え

WindowsにはLinuxのLD_PRELOADみたいな機能は無いので、実際にビルドしてリンクする必要があります。 この手の議論は昔からあるみたいです。 (参考:Patching the Windows CRT)

議論のポイントを抜き出すと…
  • [主] Windows C runtime (CRT) で malloc を差し替えるには CRT の改変が必要ですごく面倒だ
  • [コメ] 俺Windowsの開発者だけど、malloc() 変えちゃえばいいだけじゃないの? 先にリンクしちゃえよ。そっち使うから。
  • [主] おいおい、strdup()-free()ってされたらどうするんだよ。
  • [その他] ザワザワ
結局、Mozillaでは、nsprでプラットフォーム毎のラインタイムの差異を吸収するしてるからそれでいいわけですが、MySQLではそうはいきません。

ブログの主は受け入れてませんが、このWindowsの開発者の方のコメントが重要ポイントです。 後からstrdup()をリンクするときに、strdup()がmalloc()を呼ぶ場合にjemalloc側のmalloc()を呼ぶ、ということです。 WindowsのCRT自体を変更する必要は無いわけです。

とはいえ、全く差し替えちゃうと多少の不整合は出てくるようなので、realloc()、free()等は、 HeapAlloc()なのかjemallocなのかポインタを見て多少交通整理してやる必要はあるみたいです。

パッチを作ってみました。jemalloc-3.6.0-windows4prelink.patch

ビルドの説明も後述しますが、これで、mysql-5.6.23のRelWithDebInfoのビルドで、"mysql-test-run.pl --suites=main,innodb" がパスするので大丈夫とは思います。 さて、これで前述の不安(1)は解決しそうです。

でも

私はこのパッチの権利を放棄します。権利を主張することはありません。自由に使ってください。

パッチの使用は自己責任です。私及びあなた以外の主体は一切の責任を負いません。

とはいえ、以下のビルドでスケーラビリティ確認してくれる猛者を求めています。;-)

偶々成功しただけかも知れないビルド手順

sakaikさんのエントリで解説されていることを前提にしますが、違うのは、
  • jemallocをリンクすること
  • 「日本語」ロケールのまま正しくビルドすること(面倒くさいから)
です。

インストールしておくもの

  • 適切な Visual Studio (少なくとも、2012"以外"じゃないと日本語ロケールでは正しくビルドできないらしい。)
       ※人生で初めて触ったので何が適切かよくわかりません。
  • コマンドプロンプトからパスを通してインストール (MySQLビルド用;パスにはスペースを含まない方がいい)
       cmake (とりあえず私は、cmake-3.1.3-win32-x86.exe)
       bison (とりあえず私は、bison-2.4.1-setup.exe)
       perl (mysql-test-run.pl実行するなら)
  • mozilla-build (jemallocビルド用)
       ※単にMinGWじゃなくてVCを使うMSYSとして使います。
       e.x.) Visual Studio 2013 で 64bit版 をビルドするなら、"start-shell-msvc2013-x64.bat"のコンソール

jemalloc-3.6.0 のビルド (malloc()差し替え用)

mozilla-build のコンソールで、"tar jxf"も"patch -p1"も使えるので、展開して前述のパッチを当てます。

configureします。
> export EXTRA_CFLAGS="-O2 -MT -favor:INTEL64"
> ./configure --enable-cc-silence --with-jemalloc-prefix=""
出力される "JEMALLOC_PREFIX :" の項目が空欄であることを確認。

※-MDだと後でうまくいかなかった。(リンク時にエラーor無視(リンクされない)。何故かはまだ知らない。)

ビルド
> make
使うのは、lib/jemalloc_s.lib です。どこか参照しやすいパスに置きます。

MySQL のビルド

  • "VS2013 x64 Native Tools コマンド プロンプト" を起動して、展開したソースにcd
  • ビルドするディレクトリをつくってcd (e.x. Win64 とか)
  • cmake を実行して プロジェクトファイルを生成する。
       ※CMAKE_EXE_LINKER_FLAGS= には、先ほどビルドしたjemallocのライブラリを指定
> cmake .. -G "Visual Studio 12 Win64"  -DCMAKE_EXE_LINKER_FLAGS="D:\objs\jemalloc_s.lib" -DCMAKE_C_FLAGS="-U_UNICODE -U_MBCS" -DCMAKE_CXX_FLAGS="-U_UNICODE -U_MBCS" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_CONFIG=mysql_release -DINSTALL_LAYOUT=STANDALONE -DFEATURE_SET=community -DWITH_EXTRA_CHARSETS=complex -DWITH_SSL=bundled -DWITH_ZLIB=bundled -DDISABLE_SHARED=ON -DWITH_EMBEDDED_SERVER=OFF -DDEBUG_EXTNAME=OFF
  • UTF-8 のリテラルを含むソースファイルに BOM をつけて、実行時文字コードを指定する #pragma を足す。
      (最低限、UTF-8のリテラルを含む3ファイル、
        "sql/sql_locale.cc"
        "storage/perfschema/unittest/pfs_connect_attr-t.cc"
        "strings/ctype-utf8.c"
       はつけたほうが良さそう。)
      (変換したらちゃんとdiffとかで差分が他に無いことを確認したほうがいい)

      "#pragma execution_character_set("utf-8")" という行を足さねばならない。
      (他のコードを壊さずに!)
        これがないと、折角UTF-8なリテラルをわざわざcp932に変換しようとする…
  • 日本語環境特有で悪さをするコードを削除する (Bug#76555 報告済バグの応急処置)
     こんな感じ
--- mysql-5.6.23_orig/mysys/charset.c   2015-04-03 10:37:16 +0900
+++ mysql-5.6.23/mysys/charset.c        2015-04-01 13:38:01 +0900
@@ -952,7 +952,7 @@ CHARSET_INFO *fs_character_set()
     */
     fs_cset_cache=
                 #ifdef HAVE_CHARSET_cp932
-                        !strcmp(buf, "cp932") ? &my_charset_cp932_japanese_ci :
+                        //!strcmp(buf, "cp932") ? &my_charset_cp932_japanese_ci :
                 #endif
                         &my_charset_bin;
   }
  • jemalloc使用で顕在化するバグを直してもいい (Bug#76670 報告済)
     バグの実害は多分ない、クラッシュリカバリ時にログにエラーが山のように残るだけ。。。
--- mysql-5.6.23_orig/storage/innobase/os/os0file.cc    2015-04-03 10:37:09 +0900
+++ tmp/mysql-5.6.23/storage/innobase/os/os0file.cc     2015-04-13 12:19:57 +0900
@@ -900,6 +900,8 @@ os_file_readdir_next_file(
 next_file:
        ret = FindNextFile(dir, lpFindFileData);

+       DWORD error = GetLastError();
+
        if (ret) {
                ut_a(strlen((char*) lpFindFileData->cFileName)
                     < OS_FILE_MAX_PATH);
@@ -940,7 +942,7 @@ next_file:

        if (ret) {
                return(0);
-       } else if (GetLastError() == ERROR_NO_MORE_FILES) {
+       } else if (error == ERROR_NO_MORE_FILES) {

                return(1);
        } else {
  • ビルドするディレクトリにできた MySQL.sln をダブルクリック
  • 画面上部「ソリューション構成」を "Debug"(デフォルト) から "RelWithDebInfo" に切り替える
  • 「ビルド(B)」-->「ソリューションのビルド(B)」
  • たぶん "0 失敗" で終わるはず。
  • "INSTALL" という名前のプロジェクトをビルドすると、デフォルトの"C:\Program Files\MySQL" 以下にインストールされる。 ※場所の指定はcmakeの時にできたはず。

テストmain.mysqldumpだけ日本語環境では.errにエラーが出てしまうようですが、
ヘブライ文字のファイル名にアクセスしようとして??????.frmがNOTFOUNDなエラーなら、
リリースバイナリでも多分同様なのでここでは気にしないことにします。


Debugではなんかリンクする順番が変わってしまうのか上手くいかなかったです。
今のところこれ以上調べる理由はないので私は追求しません。

疲れてきてコピペが雑になってきたので、
以上です
よろしくおねがいします >> 猛者共

2015年3月9日月曜日

InnoDB Deep Talk #2 (仮) に引っ張りだされました。

先日、「InnoDB Deep Talk #2 (仮)」というイベントでお話ししてきました。

「事前情報は講演者の名前のみ(内容未定)」という、振り返ってみると異常な状況にも関わらずお集まりいただきありがとうございました。

今回は、前回とは違って少しだけ公開を意識して作ったあったので公開してみます。


これで手持ちのネタは出し尽くしたので、また暫くブログの更新は無いかもしれませんがご容赦を。。。

2015年2月4日水曜日

再出発します。

ご無沙汰しています。

 作ってから公開されるまでのタイムラグで思い出してまで書く余裕が無くて暫く更新をお休みしていました…。 その代わり、MySQL-5.7では細かい修正も含めてある程度思っていた通りのInnoDBの改善ができたのではないかと思います。

 で、本題。唐突ですが、先月末でInnoDBチームを辞しました。理由は色々ありますが、ネガティブな理由は一個もありません。願わくば、体がもう一つ欲しいくらいです。

 もう一度、自分のユーザー視点を現在のものにアップデートするべく、一般ユーザーに戻ります。その方がInnoDBを更にフットワーク良く改善できると考えたからです。とはいえ、今の立場で大きな案件があるまで、新しい領域には踏み込めないでしょう。暫くはInnoDBチーム在職中に行った変更で、リリース済みのものについて解説していきます。

その後、実用的な技術情報やツール(もしも作ったら)を共有していけたらと考えています。

 よろしくお願いします。
 これは寧ろ、MySQL-5.7は大丈夫だという証拠です。

2011年12月5日月曜日

転職等、状況のご報告

一部の関係者や、勘の鋭い方はお気づきだと思いますが、11月にPerconaを辞して、12月よりInnoDB teamの一員として働くこととなりました。XtraDB等Perconaの製品については少なくとも現職にある限りは、関与することは今後基本的にありません。

XtraDBは私にとっては、InnoDBという優れたアーキテクチャの持つポテンシャルを引き出す手段を素早く積極的に世に問うための重要なチャネルでした。しかし利用者が増えるに連れ、利用者や会社にとっては製品としての位置づけが強くなってしまったようです。この立場の違いが、開発の方針のあらゆる面での意見の相違を生んだと思います。迅速な進歩を失ってしまっては、永続的な存在意義は無いと、例え現在満足していても、将来問題に直面したときに小手先で解決できることなどもう無いのです、先に手を打たなければ。体裁を気にして将来の継続的なメンテナンスコストを犠牲にするような実装は開発リソースが少ないのだからいくら顧客がお金を払うといってもやるべきではなかったと思います。顧客が直接お金を払って前に進む形式では未来の問題は解決できず、満足した時点で終了。つまり、XtraDBのInnoDBへの提案という役割は一定の成果は得たものの、もう終わったのだと判断しました。開発リソース豊富な本家の進歩に追いつくことはもうできないのではないかと思います。

今回幸運にも、本家に参加する機会を得ました。今まで、ソースコードのみを教師に孤独に我流で腕を磨いてきましたが、これからは、自分よりもInnoDBに詳しい方々と、垣根無く議論し前へ進んでいけるのだと思うと、非常に期待と興奮を覚えます。皆さんも期待してもらっていいと思います。

反骨精神の私を今でも突き動かすのは、確か6・7年前(?)、某社有名CEOが「10年経とうがオープンソースのデータベースは商用のものには追いつくことは無い」という公式発言をしたことです(次の雇い主という説もあるが…)。多分タイムリミットはあと3・4年ですが納得のいく結論が得られるように頑張りますので、応援等宜しくお願いいたします。あ、その後もInnoDBの改善は続けますよ勿論。最早ライフワークですから。

2011年10月7日金曜日

さらなる更新系処理の並列実行性の向上について

InnoDB性能フリークの皆様(日本語圏にいるかどうか解りませんが…)、ご無沙汰しております。MySQL-5.6では我々外野開発者が色々実装してきた改良・機能について人気の高い物・有効な物から順に積極的に取り込まれる様子が見て取れます。これはMySQLの開発コミュニティにとって素晴らしい進歩です。我々の活動が多くのユーザーに支持され、本家側からも無視できなくなり、何らかの進歩を阻害する要因が解決されたのではないでしょうか?感無量です。これで私も更に前へ進むことが出来るというものです。というわけで、今回は今まで誰も触れてこなかった問題に触れてみます。

InnoDB の更新系処理のスケーラビリティは、私が過去に数多のRDBMSをベンチマークした経験上、商用RDBMSと比べてまだ明らかに低い印象を受けます。この記事を読むような人は勿論InnoDBのmutex/rw_lockの競合状態の確認はできるのでもう知ってると思いますが(飛ばします^^;)、index->lock の競合です。ご存知の通りInnoDBのテーブルは主キーのB-Tree構造であるので、この競合はすべてのテーブル/インデックスに当てはまります。index->lock の排他ロックはB-Tree構造が変更されるかも知れない場合に確保されます。leafブロックはblock->lockで排他制御するのですが、root/branchブロックは纏めて一個のindex->lockで代表させています。そして、それらのロックはトランザクションの一部であるミニトランザクション終了まで(修正しました:10/7 16:00)確保されます。例えば、3段B-Treeにleafブロックを1個追加するような処理であってもindex全体がロックされてしまい、その間、関係ない他のbranchブロックの先への処理が全くできないのです。

厳密に言うと、境界時の多少の例外処理はありますが、leafブロックを1個追加する場合は基本的にはそのブロックへのポインタが十分収まる場合には、一つ上の階層のbranchブロックさえロックできていればそれが最小限のロックなのです(説明用に他の細かい事情は大幅に省略)。とにかく単純に考えても最適化したら効果大きそうでしょう?

で、先月やってみました。先ず、5.5.xで。しかし、直ぐにkernel_mutexの競合に阻まれました。row/tableロックの管理のための取得が特に多い模様でした。5.6.xではkernel_mutexは機能毎に分割されているのでもう少し効果は大きいはずと考え、次は5.6.2で。今度はピーク性能は明らかに上がるのに多接続時の性能が大幅ダウン。私がカンファレンス等で長年説明していますが、競合箇所が1カ所でも残っていると、他の競合を解決すればするほど残されたその一カ所に集中し、見た目の性能は下がってしまうのです。これを進歩と捉えられない人は前に進めないのです。これは進歩!(リリースはできないですが…)

さて次の競合はやはり、kernel_mutex(~5.5)から派生しているlock_sys->mutex(5.6~)です。今週、さっくりspace_id(テーブルスペースのID)をキーに分割してみましたら、見事な結果が得られました。tpc-c系の負荷で32CPU以上までスケールしているように見えます。まぁ、その分、過多接続の性能は少し下がっているかも知れませんが(次の競合で)。まだデバッグ用のコードをまだ全く書いていない(UNIV_DEBUGでは動作しない)状態なのでリリースにはまだ遠いですが性能は大きく変わることは無いと思います。デッドロック検出関係が少し不安ですが、思ったよりも今動いてるので多分大丈夫でしょう。

…数字?出しませんよ。性能フリークなら、性能は自分で試す癖をつけましょう。私も他人の結果は信用しないのです、大抵分析不足なので。最悪の場合、プロモーション用に極端なケースのみが切り取られていたりします。

実験用。試してwktkするためだけのパッチセットです。
https://code.launchpad.net/~yasufumi-kinoshita/percona-server/5.6-xtradb-performance-alpha
まだ、5.6.2用です。でもノーマルの5.6.2と比較して効果を試すには十分かと思います。

(*バグバグだったので随分前に消しました。何時の日か何らかの形で復活を目指しています。)

その他気づいたこととしては、query_cache絡みの処理がスケーラビリティを阻害しているので切りました。query_cache_size = 0 だけでは多接続時に重く、完全に切れていないようなので、明示的に query_cache_type = OFF とすることで解決しました。

次の競合は、InnoDB側では無いかも知れません。有効にすると結構重くなってしまうので好きではないのですが(さらにそのせいで「電子顕微鏡のジレンマ」に近い問題もあるかもしれない)、performance_schema
で調べてみたところ、'wait/synch/mutex/sql/LOCK_open' みたいです、2位は 'wait/synch/mutex/innodb/log_sys_mutex'。ちなみに開発者が解決すべき競合は SUM_TIMER_WAIT 順で見るのが正しいと思います。log_sys->mutex つまり、トランザクションログのシリアライズ絡みの排他処理が見えるところまで来ました。かなり更新系処理のスケーラビリティが理想に近づいたと思います。次は…mysqld側か…。

2011年4月15日金曜日

頂いた件の御挨拶

(読者も少ないと思うし、日本語なので、独り言的なプライベートな感じですが。)

InnoDBに惚れ込み、その伝導のためにInnoDBのポテンシャルを可能な限り引き出す。それがこの5、6年の活動の根底にありました。その内のCPUスケーラビリティの問題を解析するための集大成とも言える(個人的に)セッションをMySQL CE 2010 (前回) で持たせて頂きましたが、一部の突出したユーザー極数人を除いては反応はあまり良くなかったのを覚えています。なので、自分の活動が一般のユーザーに分かる形で評価されることは無く、これは陽のあたらない縁の下の闇家業のようなものなのだと感じていたたところです。この一年は性能改善の進歩はあるのですが、一般ユーザーに説明すべき新しい知見は特に得られていなかったので、プライベートの問題解決を優先して MySQL CE 2011 の参加は見送っていました。

そのような状況下で、賞を頂いた報を受け、素直に驚き喜んでいます。

これ以上活動を続けることが実はあまり誰の役にも立ってないのではないか、という疑心暗鬼な状況は解消されました。先へ進むGOサインを頂いたと受け取っています。

オープンソースの醍醐味は、私のような、多くの外野の開発者がフットワーク軽く誰の承認も無く個人のやり方で問題解決を試み、公開することができることだと思います。

そんな外野の私を支持してくれる、雇い主のPercona社、
そしてFaceBook社、RightNow社を筆頭に会社の最深部(?)の私に届くほど困難な仕事をいつも依頼してくれる超エキスパートユーザーの方々、
さらにすべてのXtraDBのユーザーに感謝します。

むむむ。全員日本語は通じないかな?日本じゃ誰もXtraDB使っていないかもしれないし…
では、こっそり心の中で感謝しています。:-)

外野開発をビジネス的に支援してくれるユーザーがいる限り、外野開発者が欲をかいてそれを裏切らない限り、オープンソースソフトウェアはオープンソースソフトウェアとして死んでしまうことは無いと確信しています。大丈夫。これで、まだ何年でも闘えそうです。

※SSDも大体分かってきたので、次はHandlerSocket向けのチューニングとかにも手を付けてみたいのですが、ベンチマークソフトが無いので先ずははそこからです。誰か作ってくれないかな…。目下、余力が無いのですよ^^;