MySQL/Tritonn - SELinuxにご注意を(回避方法あり)
いつの頃からかは知りませんが、Linux(CentOSとか)をインストールするとデフォルトでSELinuxが有効(Enfocing)になっていました。
GUIインストーラ作業時にDisabledにするか、あるいは/etc/selinux/configでSELINUX=disabledにしておけばいいのですが、そのままにしておくと/etc/init.dからのmysqlの起動に失敗します。
こんな感じ。
[root@centos52 ~]# service mysql start Starting MySQL..Manager of pid-file quit without updating f[失敗]
mysqld_safeを直接叩くと起動できたり、
[root@centos52 ~]# which mysqld_safe /usr/bin/mysqld_safe [root@centos52 ~]# mysqld_safe & [1] 2985 [root@centos52 ~]# Starting mysqld daemon with databases from /var/lib/mysql [root@centos52 ~]#
エラーログを見ると
081210 09:52:34 mysqld started 081210 09:52:36 mysqld ended 081210 09:53:08 mysqld started 081210 09:53:09 mysqld ended
特に何も書かれていないので原因が特定しにくいのですが、これはSELinuxの設定に起因しています。
厳しい言い方をすると、mysqld側のログ出力が甘い、ということなのですが。
というわけでご注意を。
問題回避のためのSELinux設定例。
tritonn@tritonn64:~$ cat /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - SELinux is fully disabled. SELINUX=disabled # SELINUXTYPE= type of policy in use. Possible values are: # targeted - Only targeted network daemons are protected. # strict - Full SELinux protection. SELINUXTYPE=targeted
JNIでHelloWorld!
JNIはJavaプログラムからC/C++で記述されたライブラリのAPIを呼び出すためのインタフェースです。ずいぶん昔からある仕様ですが、あまりなじみが無いので触ってみた。
イメージ的にはELF形式のjavaプログラムから共有ライブラリに対してdlopenで動的にリンクして呼び出すって感じですな。MySQLのplugin(UDF, pluggable storage engine)と技術的には同類。
ということで以下は、Linux x86 (CentOS4.5) + JavaSE6 でJNI HelloWorldした記録です。
参考:http://www.alles.or.jp/~torutk/oojava/maneuver/2001/jni/jni.html
ダウンロード&インストール
ここからJavaSE6のJDKを入手してインストール。RPMが楽でいい。お奨め。
既存環境にgcj(java-1.4.2-gcj-compat.noarch)とかが入っているとインストールがうまく行かないので先に抜いておく。
以下のコマンドでインストール完了確認
java -version
どうやらRPMで入れると、
/usr/bin/java等 ↓ /usr/java/default/*** ↓ /usr/java/latest/*** ↓ /usr/java/jdk1.6.0_11
みたいにつながるようにセットアップしてくれる(RPM版)。
ただし、javahコマンドは/usr/binにsymlinkが作られないので
cd /usr/bin && sudo ln -s /usr/java/default/javah
する必要あり。
Javaプログラム作成
こんな感じのクラスを作る。
package tritonn; public class HelloJNI { static { System.loadLibrary("HelloJNIImpl"); } public native void printHello(); public static void main(String[] args) { new HelloJNI().printHello(); } }
ポイントはstaticイニシャライザでSystem.loadLibraryを呼ぶこと。JNI経由で呼ぶ予定のメソッドをnative修飾子付きの宣言だけしておくこと。
Cヘッダ作成
javahコマンドでCプログラム用のヘッダを自動生成する。完全修飾クラス名で指定。
javah tritonn.HelloJNI
作業ディレクトリにこんなヘッダファイルができる。tritonn_HelloJNI.hとか。(パッケージ名_クラス名.hかな)
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class tritonn_HelloJNI */ #ifndef _Included_tritonn_HelloJNI #define _Included_tritonn_HelloJNI #ifdef __cplusplus extern "C" { #endif /* * Class: tritonn_HelloJNI * Method: printHello * Signature: ()V */ JNIEXPORT void JNICALL Java_tritonn_HelloJNI_printHello (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
これを実装したCプログラムを作る。
Cプログラム作成
いくつかなじみの無いマクロがでてきてますが、気にせずヘッダファイルにある関数シグネチャ通りに実装。
#include <jni.h> #include "tritonn_HelloJNI.h" #include <stdio.h> JNIEXPORT void JNICALL Java_tritonn_HelloJNI_printHello (JNIEnv * a, jobject b) { printf("Hello JNI World!!\n"); return; }
Cプログラムのコンパイル
JDKに含まれているjni.hとかは(RPM版でインストールしても)/usr/includeとかには入らないので、-Iで明示する必要有り。また共有ライブラリにするので -fPIC を忘れずに。
gcc -fPIC -I/usr/java/default/include -I/usr/java/default/include/linux -c HelloJNIImpl.c
共有ライブラリの作成
ここで指定するライブラリ名がJavaプログラムでSystem.loadLibraryする対象となります。
gcc -shared -o libHelloJNIImpl.so HelloJNIImpl.o
実行
とりあえずLD_LIBRARY_PATH指定して実行。
tritonn@dev32:~$ LD_LIBRARY_PATH=`pwd` java tritonn.HelloJNI Hello JNI World!!
おぉぉ!うまく行きましたね!
/usr/libとかに*.soを放り込んでldconfigを実行すればLD_LIBRARY_PATHは不要になると思います。
mysqldにおけるスロークエリログ判定実装
スロークエリログはどのように計測され、出力されているのかをまとめてみました。
測定方法
THDクラスのset_time関数で計測開始、end_time関数で計測終了。
計測開始ポイント
- mysqld --bootstrapを使用している場合はmysql_parse関数の直前
- ネットワーク経由の場合はネットワークコマンド(COM_XXX)の種別判定直前
- ただしQUERYコマンド(COM_QUERY、SQLCOM_XXX)の場合はmysql_parse関数の直前で再設定
というわけでまとめるとSQL文のparse直前に計測を開始
計測終了ポイント
スロークエリログの出力
log_slow_statement関数はクエリ単位で必ず呼ばれ、その中でオプションの有無、設定した閾値、計測した値に基づいて出力する。
まとめ・補足
Tritonn: configure --enable-abortをやめて自動判別にすべきか
すごく細かい話なんですが。というかここ最近ずっとBlogを書いていなかったけれども、また書きたくなってきた。しかも細かい話。備忘録的な。
Senna 1.1.4では、
- "--enable-abort"でビルドされたらsen_index_set_abort_callbackがlibsennaに存在する
- そうでなければsen_index_set_abort_callbackがlibsennaに存在しない
というのが成立しているので、Tritonn側はAC_CHECK_LIBで自動判別すべきかもと思い始めた。
「libsennaは"--enable-abort"でビルドしてるけどTritonnからの呼び出し時には使わない」とかっていうケースは考慮しなくてもOKじゃないかと。
Tritonnのconfigure時にAC_CHECK_LIBで自動判別すると以下のメリットがあると思う
- Tritonnのconfigureオプションを増やさずに済む(シンプルなのは良いこと)
- 「うっかり付け忘れ」「間違えて付ける」ことによる不整合が起きなくなる
一方デメリットは、
- TritonnからはSennaの--enable-abortを使わないけど別のSennaクライアントからは使うパタンに対応できない
- Tritonnビルド時のlibsenna.soは--enable-abort付きだったけど、後でlibsenna.soを入れ替えたときに--enable-abort無しになった場合、Tritonnが起動エラー
かなぁ。そもそもenable abortは必要な機能だと思うし、デメリットに該当するパタンに遭遇する確率ってほぼゼロなんじゃないかとも思い。
明日までに考えが変わらなければ自動判別にしてしまおう。
性格によるものなのか、こういう細かいところも割りと気になってしまいます。設定手段を増やせばユーザの選択肢は増えますが、その分学習コストも上がるしミスの発生確率も上がるし、最適解は何かなーといつも考えてます。
話は変わりますが、ライブラリのビルドオプションとかって皆さんはどうやって取得してるんでしょう。よく○○-cfgというコマンドがインストールされてビルドに必要な情報(-Iとか-Lとか)を取得できるようになっているパタンがありますが、SennaもMeCabもconfigureオプションは取れないような。
configure --enable-abort
senna 1.1.4で取り込まれたokuさんによるKILLパッチについてのメモ。
configure.ac
# abort AC_MSG_CHECKING([whether enable abort]) AC_ARG_ENABLE(abort, [AC_HELP_STRING([--enable-abort], [enable query abortion. [default=no]])], , [enable_abort="no"]) if test "x$enable_abort" != "xno"; then force_enable_dynamic_malloc_change="yes" AC_DEFINE(USE_QUERY_ABORT, [1], [use abort]) fi AC_MSG_RESULT($enable_abort)
"--enable-abort"でUSE_QUERY_ABORTマクロが定義される。
tritonn-1.0.12 残タスク
tritonn-1.0.11のquick fix版として1.0.11aを出したかったのですが、日が経ってしまったので、いくつか追加で修正等を行って1.0.12としてリリースする予定です。
完了したもの
リリースまでにやる
- okuさんのabortパッチの取り込み
- configureオプション"--enable-abort"の追加
- libsennaにシンボルsen_index_set_abort_callbackがあるかどうかをconfigure時にチェック
- configure時にsenna-cfg --versionでlibsennaのバージョンをconfigure時にチェック
- logo掲載
- リファレンスマニュアルの充実(configureオプション一覧,サーバ変数一覧,senna flags一覧)
ビルド時の留意事項
- ソース配布版作成時の"--with-embedded-server"対応
- バイナリ配布版で"--enable-abort"をつける
- RPM配布版ではdevelパッケージも出す