Understanding MySQL Internalsを読む(3)

前回の記事

gdbデバッグするときのお薦めbreakpoint個所

P.35の表より。これでgdbデバッグ効率UP〜。

操作対象 お薦めbreakpoint個所 見ると幸せになれるかもしれない変数
SELECT文を実行 mysql_select() *thd, thd->query, *tables, *join
INSERT文を実行 mysql_insert() *thd, thd->query, *table, fields, values_list
UPDATE文を実行 mysql_update() *thd, thd->query, *table_list, fields, values, *conds
DELETE文を実行 mysql_delete() *thd, thd->query, *table_list, *conds
クエリキャッシュから結果を返せるか確認 Query_cache::send_result_to_client() *this, *thd, sql
クライアントからのパケットの読み込み my_net_read() *net
クライアントへのパケットの書き込み my_net_write() *net, packet, len
接続を認証 check_connection() *thd, thd->net
マスタ側で更新処理をログ出力 MYSQL_LOG::write(Log_event *) *event_info, *event_info->thd, event_info->thd->query
スレーブの開始 start_slave_threads() *mi
スレーブのIOスレッドの実行 handle_slave_io() *mi
スレーブのSQLスレッドの実行 handle_slave_sql() *rli
テーブルを開く open_table() *thd, thd->query, db, table_name, alias
frmファイルを読む openfrm() name, alias
MyISAMテーブルを開く ha_myisam::open() *this, name
InnoDBテーブルを開く ha_innobase::open() *this, name
テーブルロックを要求する mysql_lock_tables() *thd, thd->query, **tables
トランザクションをcommitする ha_commit_trans() *thd, *trans

新しいソースファイルを追加したい場合(hacking)

新しくC/C++ファイルを追加したい場合は以下の手順に従うこと。

  1. ファイルを追加したディレクトリのMakefile.amをテキストエディタで開く。
  2. "SOURCES"というsuffixの変数を見つける。例えばsqlディレクトリなら"mysqld_SOURCES"、myisamディレクトリなら"libmyisam_a_SOURCES"となっている。
  3. SOURCES変数に追加したファイルの名前を追記する。
  4. "INCLUDES"という変数および"noinst_HEADERS"という変数を見つける。"make install"でincludeディレクトリに含めて欲しいヘッダファイルはINCLUDESに、そうでないヘッダファイルはnoinst_HEADERSに追記する。

これでおしまい。

安定性向上のためのCoding Tips

  • スレッドセーフなプログラミングを行うこと。
  • 多くのグローバル変数は変更時に使用すべきmutexが用意されているのでこれを使う。
  • 利用可能なスタックサイズは非常に小さいことを意識する。100bytes以上のメモリを割り当てる場合はsql_alloc関数またはmy_malloc関数を使うこと。
  • より少量のメモリ割当てでは、可能ならmy_malloc関数ではなくsql_alloc関数を使う。sql_alloc関数は事前にメモリを割り当てているメモリプールからメモリを割り当てるが、my_malloc関数はmalloc関数のwrapperであるため。sql_alloc関数はdo_command関数以後のスタックで呼出可能。sql_alloc関数で割り当てたメモリはクエリの実行が完了するまでの間のみ有効。それ以上の期間でのメモリの割り当てを行いたい場合はmy_malloc関数を使う。
  • 大量のメモリを割り当てる場合はmy_malloc関数を使用し、なるべく短い時間でmy_free関数でメモリを解放する。
  • sql_alloc関数で割り当てたポインタは解放してはいけない。メモリプールはクエリ実行の最後にfree_root関数により解放される。
  • my_malloc関数で割り当てたポインタは、my_free関数で解放する。
  • C++の例外は使用してはいけない。どんな場合でも、ビルド時に例外機構を無効化してビルドしている。
  • C++STL、iostream等、libstdc++へのリンクを必要とするC++による拡張機能を利用してはいけない。
  • 依存ライブラリを増やしてはいけない。
  • 既存のMySQLのコードを可能な限り再利用する。

ポータビリティ向上のためのCoding Tips

  • libcの関数を直接呼んではいけない。代わりにmysysディレクトリとstringsディレクトリに実装されているポータビリティ向上のためのWrapper関数を使用する。通常、そうしたWrapper関数はlibcの関数名にprefixとして"my_"が付けられている。
  • プラットフォームによってbyte orderが異なることを意識し、int4storeやint4korrなどのマクロを使用することでbyte orderの違いを吸収する。
  • メモリのアラインメントを意識する。ポインタに直接数字を代入してはいけない。
    • 悪い例
    *((char*)p+1) = n;
    • 良い例
    memcpy((char*)p+1, &n, 4);
    • 移植性が高い最も良い例
    int4store((char*)p+1, n);
  • 特定のプラットフォームやコンパイラ向けの最適化コードを書く場合には、#ifdefブロックで囲い、これに該当しない環境でのコードも#elseでちゃんと書くこと。
  • CソースファイルにC++スタイルのコメントを記述しないこと。Cコンパイラがサポートしていたとしても避けるべき。
  • 変数のバイトサイズを定義したい場合には、include/my_global.hに定義されているuint8やuint32などの定義済みの型を使用すること。

パフォーマンス向上のためのCoding Tips

  • パフォーマンスを自然と意識したコーディングを心がける習慣を身につける。CPUがどのように動作するのかを学び、自分が書いたC/C++のコードがアセンブラレベルでどのように実行されるのかを視覚化できるようにすること。
  • 既存のMySQLのコードを可能な限り再利用する。
  • 自分が書いたコードが呼び出す関数の先でどんな処理が行われるのかを意識すること。
  • 不要なシステムコールの呼出は行わない。複数の呼出はひとつにまとめることができないかどうかを考える。例えば、my_read関数とmy_write関数の呼出の代わりにIO_CACHE関数を使用するなど。
  • 不要なオブジェクトのインスタンス化は行わない。
  • inline関数では大きな関数を書いてはいけない。

コーディングスタイルについてのTips

  • internals.htmlで説明されているコーディングガイドラインに従うこと。
  • コードを記述する際に、自分が書いたコード以外の部分をエディタが自動整形しないようにすること。
  • 関数は処理が成功した場合は0を返し、失敗した場合は0以外の値を返すようにすること。
  • 複数の関数を連続して呼び出し、エラーのチェックを行う場合は以下のようなスタイルで書くと良い。
    if (a() || b() || c())
      goto err;
  • true/falseの代わりにTRUE/FALSEを使用する。
  • Cではmy_bool、C++ではboolを使用する。
  • C++ではオブジェクトの参照ではなくポインタを引数に使う。
  • パフォーマンス上、クリティカルではない個所であっても、最適化されたコードを書くようにすること。