MySQLに対応した評価ツールDBT-1を利用したハードリソース変更によるパフォーマンスへの影響の考察

ミラクル・リナックス株式会社

概要

クアッドコア(quad core)Intel Xeonプロセッサ搭載マシン(x86_64)上での性能評価をおこなった。DBT-1を利用したハードリソース変更のパフォーマンスへの影響について考察する。

詳細

5.0.24と5.0.32の評価データを比較検討する。 5.0.24はあきらかに8CPU数(ここではコア数を指す)ではスケーラビリティが落ち、CPU数が4でピークとなっている。 BT値で比較すると、8コアの場合、5.0.24では220〜230で頭うちになるが4コアでは400ぐらいになる。


図1 接続数による性能推移

一方、8コアの場合、5.0.24と5.0.32を比較すると5.0.24でみられた性能上の問題は5.0.32では解消しているのが確認できる。


図2 接続数による性能推移

5.0.24のoprofileのデータ(トップ5)を比較すると、4コアの場合以下になる。
-------------------------------------------------------------------------------
==> cpu=4-mysql=5.0.24-gcc3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <==
CPU: Core Solo / Duo, speed 2666.77 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
49661094 20.3662  libpthread-2.3.4.so      pthread_mutex_trylock
24660214 10.1132  libpthread-2.3.4.so      pthread_mutex_unlock
16691946  6.8454  mysqld                   btr_search_guess_on_hash
12933540  5.3041  mysqld                   rec_get_offsets_func
10784991  4.4230  mysqld                   mutex_spin_wait
-------------------------------------------------------------------------------

一方、8コアの場合、下記のとおりである。
-------------------------------------------------------------------------------
==> cpu=8-mysql=5.0.24-gcc=3.4/cpu=8-mysql=5.0.24-gcc=3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <==
CPU: Core Solo / Duo, speed 2666.7 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) cou
nt 100000
samples  %        app name                 symbol name
180708060 38.8878  libpthread-2.3.4.so      pthread_mutex_trylock
105501875 22.7037  mysqld                   mutex_spin_wait
49569939 10.6673  libpthread-2.3.4.so      pthread_mutex_unlock
11292438  2.4301  mysqld                   btr_search_guess_on_hash
9457097   2.0351  mysqld                   rec_get_offsets_func
-------------------------------------------------------------------------------

ここで明らかのようにpthread_mutex_trylock、mutex_spin_wait、pthread_mutex_unlockが実行コストの約7割となり、ロックの競合が多発している。特にpthread_mutex_trylockおよび、mutex_spin_waitの増加が著しい。 これはあきらかにmutex_spin_lockを中心としたロック競合であると言える。

oprofileによる分析

oprofileによる分析手順は通常下記のようなプロセスになる。
  1. oprofileデータを取得する
  2. opreportで時間のかかっているルーチンのトップ5を確認する
  3. call graphデータを取得する
  4. トップ5のルーチンがどこから呼ばれているか確認、分析する
  5. 改良案について、同様なプロセスを繰り返す
例えば、mutex_spin_wait(MySQL 5.0.24)は下記のようなコールグラフを持つ
-------------------------------------------------------------------------------
  299373    4.1407  mysqld      mysqld                   buf_page_get_known_nowait
  342988    4.7439  mysqld      mysqld                   row_search_for_mysql
  919809   12.7220  mysqld      mysqld                   buf_page_get_gen
  4638092  64.1499  mysqld      mysqld                   btr_search_guess_on_hash
 7230090   9.1619  mysqld      mysqld                   mutex_spin_wait
  7230090  72.1552  mysqld      mysqld                   mutex_spin_wait [self]
  1812007  18.0836  mysqld      mysqld                   pthread_mutex_trylock
  817859    8.1621  mysqld      mysqld                   ut_delay
-------------------------------------------------------------------------------

btr_search_guess_on_hash, buf_page_get_gen, row_search_for_mysql, ...から呼ばれていることが分かる。

mutex_spin_waitのコードからは字面の上でpthread_mutex_trylockは見えないが、 ./innobase/include/os0sync.ic のos_fast_mutex_trylockで利用されている。 ./innobase/include/sync0sync.icのmutex_test_and_setでos_fast_mutex_trylock は呼ばれている。このmutex_test_and_setはmutex_spin_waitで呼ばれている。

このようにしてソースコードを分析していってボトルネックとなる部分を発見する。

ロック競合について

さて、5.0.24ではpthread_mutexだけではなく独自のmutexを実装している。独自にreserver_cellというデータ構造を持ちそれでロックを管理しているのだが、ロック取得のコストが高い。

ロック競合は、(1)ロック対象にアクセスする確率、(2)ロック取得にかかるコスト、(3)ロックを取得している時間などにより発生する。

同時にロック対象にアクセスする確率が高ければ競合する確率も当然高くなる。通常は、この確率を減らす目的でロック対象の粒度をより小さくする。データベース全体へのロックよりもファイル、テーブル、ページ、レコードへのロックの方が小さくなるので、より粒度の小さいロックへと変換する必要がある。

ロック取得にかかるコストは、システムコールの場合、コンテキストスイッチが発生するので、spin loopに比較してコスト高になる。一方、spin loopの場合、CPUを100%占有するため、他のスレッドないしプロセスが動けないので、全体としてのスループットに影響がある。従って、すぐにロックを取得できると考えられる対象に関してはspin loop長い事待つことが予測される対象に関してはシステムコールを利用するなどのバランスが重要になってくる。

spin loopも通常はCPUバスをロックし、アトミックにtest and set(IA-32の場合cmpxchg命令)をおこなうため、コストが高い。ナイーブに実装するとspin loopの最中にバスロックがかかってしまい、ロック対象へのアクセス以外もブロックする結果になるので注意が必要である。

-------------------------------------------------------------------------------
./innobase/sync/sync0sync.c
mutex_spin_waitの実装

spin_loop:
  /* 下記はコストが高い*/
  if (mutex_test_and_set(mutex) == 0)
  {
    /* Succeeded! */
    goto finish_timing;
  }

  i++;

  /* SYNC_SPIN_ROUNDS 回数ループする */
  if (i < SYNC_SPIN_ROUNDS)
  {
    goto spin_loop;
  }
-------------------------------------------------------------------------------

innodbの実装ではinnodb_sync_spin_loops(SYNC_SPIN_ROUNDS)回数スピンしてその間にロックを取得できなかったらsleepして待つ実装になっている。

従って、負荷が増大し、ロック対象にアクセスする確率があがればあがるほど、ロック競合が発生する。単位時間あたり処理できる量を越えると、スループットは頭うちとなり、それ以上、ロック競合等により処理ができなくなる。

EU=2200で性能上の問題が解消した5.0.32についてCPU数4と8のoprofileのデータを下記にしめす。

-------------------------------------------------------------------------------
==> cpu=4-mysql=5.0.32-gcc=3.4/oprofile061211-eu=2200-op=default-none/opreport-l.txt <==
CPU: Core Solo / Duo, speed 2666.74 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
27607749 12.4331  libpthread-2.3.4.so      pthread_mutex_trylock
16409288  7.3899  mysqld                   btr_search_guess_on_hash
15570000  7.0119  mysqld                   rec_get_offsets_func
13151738  5.9228  libpthread-2.3.4.so      pthread_mutex_unlock
9934356   4.4739  libc-2.3.4.so            memcpy
-------------------------------------------------------------------------------

どちらのデータもmutex_spin_waitが発生しなくなっている。
-------------------------------------------------------------------------------
==> cpu=8-mysql=5.0.32-gcc=3.4/oprofile-eu=2200-op=default-none/opreport-l.txt <==
CPU: Core Solo / Duo, speed 2666.76 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Unhalted clock cycles) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
47097502 16.8391  libpthread-2.3.4.so      pthread_mutex_trylock
19636300  7.0207  libpthread-2.3.4.so      pthread_mutex_unlock
18600010  6.6502  mysqld                   rec_get_offsets_func
18121328  6.4790  mysqld                   btr_search_guess_on_hash
11453095  4.0949  mysqld                   row_search_for_mysql
-------------------------------------------------------------------------------

これは実装が、ロックの対象の粒度をより小さくしている、待ち行列(reserve_cell)の無駄な処理をやめているなどの変更を行った結果、ロック対象にアクセスする確率が減少したため、競合の頻度が減ったためと考えられる。

今後の方向としては、(1)粒度が大きいロックについては、より小さいロックへの変更、(2)ロックコストがより安いロックアルゴリズムの適用、(3)ロック期間(ロックを取得してから解放するまでの時間)のさらなる削減などを組み合わせることが必要と考える。 現時点では(1)の効果が大きいと考えられるので、ロック競合のさらなる分析検討が必要である。

このデータの性能データ

関連する考察データ

  • 関連する考察データは登録されていません。

⇒ コメント表示 ⇒ コメント登録