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

追記

以下のようにSELinux側で設定を追加することでも対応可能とのことです。byコメント欄

restorecon -FRv /var/lib/mysql

ということは、mysql.specに書いておけばいいのかな!?

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修飾子付きの宣言だけしておくこと。

Javaプログラムのコンパイル

普通にjavacすればよろし。

javac tritonn/HelloJNI.java

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直前に計測を開始

計測終了ポイント

  • thd->end_time関数自体はlog_slow_statement関数の中で呼ばれるのみ
  • log_slow_statement関数はSQL文の結果返却を含む全ての処理が終わった後、終了処理(お掃除)が走る直前で呼ばれる。
  • セミコロンによる複数行ステートメントの場合は各ステートメント終了時。

スロークエリログの出力

log_slow_statement関数はクエリ単位で必ず呼ばれ、その中でオプションの有無、設定した閾値、計測した値に基づいて出力する。

まとめ・補足

  • ネットワーク経由でコマンドを受け取った後の「parse」「optimize」「execution」「結果返却」全て処理時間の合算である。
  • mysqlコマンドラインクライアントで表示される実行時間はmysqlコマンド自身が計測した値で、mysqldサーバで計測したものとは別。

ついでに一般クエリログは?

  • 各ネットワークコマンドの処理の初期処理に相当するタイミング
    • COM_QUERY(SQL文)ならparse前

ということです。

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とか)を取得できるようになっているパタンがありますが、SennaMeCabも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マクロが定義される。

senna.h

#ifdef USE_QUERY_ABORT
void sen_index_set_abort_callback(sen_index *i, int (*cb)(void*), void *arg);
#endif /* USE_QUERY_ABORT */

で、APIが有効になる。

Tritonn

どうしようかな。

Tritonn配布バイナリではパッチを有効にしたい。でもソースからビルドする場合にはSennaの--enable-abortのON/OFFを考慮しないといけない(デフォルトはOFFだし)。

というわけで、

  • Tritonnのconfigureに"--enable-abort"を追加し、パッチをifdefで囲う
  • "--enable-abort"のデフォルト値はOFF
  • バイナリ配布版ではTritonn/Sennaともに"--enable-abort"をつけてビルドしたものを出す

という感じにしようかと。

tritonn-1.0.12 残タスク

tritonn-1.0.11のquick fix版として1.0.11aを出したかったのですが、日が経ってしまったので、いくつか追加で修正等を行って1.0.12としてリリースする予定です。

完了したもの

  • sql_yacc.yyのshift/reduce数を調整し、bisonエラー回避
  • USING句を2重に使用した場合の適用値が本家と異なっていた問題を修正

リリースまでにやる

  • okuさんのabortパッチの取り込み
    • configureオプション"--enable-abort"の追加
    • libsennaにシンボルsen_index_set_abort_callbackがあるかどうかをconfigure時にチェック
  • configure時にsenna-cfg --versionでlibsennaのバージョンをconfigure時にチェック
    • tritonn-1.0.12ではsenna-1.1.4以上が必要(1.1.4が必要になるのはenable-abort有効時のみだけどあえて1.1.3を使う要望はあるかしら)
  • logo掲載
  • リファレンスマニュアルの充実(configureオプション一覧,サーバ変数一覧,senna flags一覧)

ビルド時の留意事項

  • ソース配布版作成時の"--with-embedded-server"対応
  • バイナリ配布版で"--enable-abort"をつける
  • RPM配布版ではdevelパッケージも出す

Sun Tech Days 2008 in Tokyoのライトニングトークにでます

明日から始まる Sun Tech Days 2008 in Tokyoの2日目(12/3)、OSSコミュニティ枠のライトニングトークに出させていただくことになりました。

MySQL系イベントというよりももう少し一般よりのイベントのようですし、時間も5分間ですので、あまり突っ込んだ話はできないですが、見かけたらお声がけ下さい。