【後編】待機リクエスト/データベース接続プールのチューニング
「WebLogic Serverの性能をいかにして最大限に引き出すか」――これはすべてのWebLogic Serverユーザーにとって最大の関心事の1つだろう。この疑問に対し、オラクルのコンサルタントが秘伝のチューニング・ノウハウを明かしてくれた。前編に続いて、2011年11月に開催された「Oracle DAB & Developers Days 2011」で実施されたセッション「オラクルコンサルが実施するWebLogic Serverのパフォーマンスチューニング」の内容を基に、そのエッセンスを紹介しよう。今回は、待機リクエスト/データベース接続プールのチューニング手法を説明する(編集部)。
【ポイント3】待機リクエストに対するチューニング――「容量制約」を設定する
前編で紹介した2つのチューニング・ポイントに続き、3つ目のポイントを紹介しよう。それは、リクエストの滞留に対するチューニングである。これに関するパラメータには、「容量制約」と「共有容量」がある。前者は、各ワークマネージャが許容する待機リクエスト数、後者はワークマネージャ全体で許容する待機リクエスト数であり、いずれも実行スレッド数と待機リクエスト数を足し合わせた値を設定する。
ただし、「共有容量」を設定する際には十分な注意が必要だ。例えば、「共有容量」の値として8を設定した場合に、実行スレッド数が5となり、さらに3つの滞留リクエストが生じると、それ以降のリクエストを受け付けずにエラーが返される。このパラメータを利用してワークマネージャ全体の待機リクエスト数に制約をかけると、特定のアプリケーションにアクセスが偏った際、他のアプリケーションがリクエストを受け付けなくなるという好ましくない状況が発生する恐れがある。そこで、ここでは個々のワークマネージャに対して設定できる「容量制約」によるチューニングについて説明する。
それでは、容量制約を300~3,500まで変化させた場合、つまり待機できるリクエスト数を増やしていった場合の平均レスポンスとエラー率を見てみよう。その結果から、待機リクエスト数を抑制すればするほど、必要以上にリクエストを待機させないため、レスポンスは向上するが、リクエスト・エラーの発生率が高くなることがわかる。つまり、レスポンスとエラー発生率はトレードオフの関係にあるわけだ。
次に、「最大スレッド数制約」に300を設定し、「容量制約」の有無の影響を比較した結果を見てみたい。この場合、アクセス数を増やしていくと、300の実行スレッドでは処理しきれない量のレスポンスに達した際、「容量制約」を設定していない場合には一気にレスポンスが悪化する。しかし、「容量制約」を設定していれば(下図の例では500に設定)、一定数以上のリクエストが滞留することはない。これにより、滞留によるレスポンスの劣化を防止できるわけだ。当然、裏側では大量のリクエスト・エラーが発生するが、安定したレスポンスを維持できることの意義は大きいだろう。
【ポイント4】接続プールのチューニング指針――「初期容量」を適切に設定
続いて、接続プールについて見てみよう。これに関するパラメータには、データソース作成時に生成されるJDBC接続数である「初期容量」と、作成可能なJDBCの接続数である「最大容量」、そしてスレッドがJDBC接続を取得する際、プールの接続が枯渇しているときに待機する時間を設定する「接続予約の待機時間」といったものがある。
では、「初期容量」が少ないケース、具体的には「最大スレッド数制約」を300、「容量制約」を500、「初期容量」を0、「最大容量」を100に設定し、突発的にリクエストが発生した場合に、どのような挙動をするのか紹介しよう。この場合、最初は「初期容量」が0のため、いきなり接続がない状態で始まり、リクエストが来た瞬間、スレッドはコネクション待ちで待機するという結果になった。
次に、スレッドが枯渇すると、リクエストに割り当てられるスレッドがなくなるため、待機リクエストが発生する。その状況でJDBC接続を生成していくわけだが、生成には多くの時間がかかってしまう。これは、接続がないため実質的に稼働できるスレッドが制限され、実質接続数分のスレッドでしか処理できない状況に陥ってしまうためだ。そして「接続予約の待機時間」を過ぎたスレッドはタイムアウトとなって接続の取得に失敗し、そのエラーが積み重なっていくことになる。なお、接続生成時には、データベース側はサーバ・プロセスを次々に生成するため、大きな負荷がかかってしまう。
この問題を解消するには、「初期容量」と「最大容量」に同じ値を設定する必要がある。これにより、初めから最大接続数分のスループットで処理することが可能になる。
ただし、このように設定することでデータベース側の負荷を軽減できるが、この設定の場合は300のスレッド数に対してコネクションが100しかないので、コネクション不足の状態になってしまう。そのため、このように設定したとしても、待機スレッドや待機リクエストは発生する。また、「初期容量」を0にした場合より大幅に少ないものの、タイムアウトしてしまうスレッドも発生する。スループット重視でスレッドを多く設定したとしても、コネクションが足りない状況では結局、スループットが出ないわけだ。
この例は1スレッド1接続を必ず利用する処理であるため、この問題を解消するには、プールする容量(「初期容量」と「最大容量」の値)を「最大スレッド数制約」と同値にすればよい。それにより、接続待ちのスレッドをなくし、待機させるのはリクエストのみというかたちにできる。
以上をまとめると、接続プールのチューニング指針として、「初期容量」を適切に設定することで、接続生成待ちの待機スレッドや待機リクエストを減らすことができる。また、「初期容量」と「最大容量」を同じ値に設定すれば、接続生成待ちの待機リクエストやスレッドの発生を防げるのである。
そして、スループットを最大化するには、JDBC接続を利用するアプリケーションのスレッド数に、そのスレッドが利用するJDBC接続の数を掛け合わせた数以上の「初期容量」、「最大容量」を設定する。それにより、スレッドの接続取得待ちを解消し、スループットを最大化できる。特にフェールオーバー型マルチデータソースの待機用データソースに対して「初期容量」を適切に設定しておかないと、障害などでデータベース・インスタンスが落ちた際にフェールオーバーしても、その後にスループットが低下して性能が劣化する羽目になるので注意していただきたい。
MBean情報を取得してWebLogic Serverの状態を把握する
最後に、ここまでに説明したチューニングを行う際に参考となるMBeanの情報を紹介しておこう。
まず、ワークマネージャ関連のMBeanには、次のようなものがある。
| MBean名 | 概要 |
|---|---|
| 保留中のユーザーリクエスト数 (PendingUserRequestCount) |
リクエストの滞留有無と状況を把握する |
| アイドル・スレッド数 (ExecuteThreadIdleCount) |
アイドル・スレッドの枯渇の発生有無を把握する |
| スタンバイ・スレッド数 (StandbyThreadCount) |
実行スレッド数を把握するために、スタンバイ・スレッド数が必須となる |
| スレッド総数 (ExecuteThreadTotalCount) |
スレッドプールのスレッド総数を取得し、実行中のスレッド数を算出して総数の変動状況を把握する 「実行スレッド数」=「スレッド総数」-「スタンバイ・スレッド数」-「アイドル・スレッド数 」 |
また、データソース関連のMBeanには、次のようなものがある。
| MBean名 | 概要 |
|---|---|
| 現在アクティブな接続の数 (ActiveConnectionsCurrentCount) |
現在使用している接続数を取得し、使用状況を把握する |
| アクティブな接続の最大数 (ActiveConnectionsHighCount) |
スナップショットでは捕捉できない最大値を取得して、キャパシティの適切さの確認や、閾値設定の検討に使用する |
| 接続待機の現在数 (WaitingForConnectionCurrentCount) |
接続待機の有無を監視し、滞留状況を把握する |
| 接続待機の最大数 (WaitingForConnectionHighCount) |
スナップショットでは捕捉できない最大値を取得して、閾値設定の検討に使用する |
| 最大接続待ち時間(秒) (WaitSecondsHighCount) |
接続待ちの最大時間を取得することで、接続待ちによる性能への影響を見極める |
読者のシステムにおいてそれぞれの値をチェックし、WebLogic Serverのパフォーマンス状況の把握に努めていただきたい。
なお、これらのMBeanの情報取得には、WebLogic Serverのコマンドラインのスクリプト環境である「WebLogic Scripting Tool(WLST)」を使うことができる。例えば、データソースの情報を出力するには、次のようなWLSTスクリプトを使えばよい。
connect('weblogic','welcome1','t3://localhost:7001')
import time
import traceback
serverRuntime()
print "Day,Time,State,CurrCapacity,ActiveConnectionsCurrentCount,WaitingForConnectionCurrentCount"
while(true):
try:
oJDBCDataSourceRuntime = getMBean('JDBCServiceRuntime/<サーバ名>/JDBCDataSourceRuntimeMBeans/<データソース名>')
oState = oJDBCDataSourceRuntime.getState()
oCurrCapacity = oJDBCDataSourceRuntime.getCurrCapacity()
oActiveConnectionsCurrentCount = oJDBCDataSourceRuntime.getActiveConnectionsCurrentCount()
oWaitingForConnectionCurrentCount = oJDBCDataSourceRuntime.getWaitingForConnectionCurrentCount()
print time.strftime('%Y-%m-%d,%H:%M:%S') + "," + str(oState) + "," + str(oCurrCapacity) + "," + str(oActiveConnectionsCurrentCount) + "," + str(oWaitingForConnectionCurrentCount)
time.sleep(1)
except:
print "<<<error>>>"
traceback.print_exc()
disconnect()
break
このスクリプトを「test.py」として保存/実行した結果は、次のようになる。
$ java weblogic.WLST test.py ...略... Day,Time,State,CurrCapacity,ActiveConnectionsCurrentCount,WaitingForConnectionCurrentCount 2011-10-31,22:39:01,Running,10,0,0 2011-10-31,22:39:02,Running,10,0,0 2011-10-31,22:39:03,Running,12,9,0 2011-10-31,22:39:04,Running,22,18,0 2011-10-31,22:39:05,Running,29,15,0 2011-10-31,22:39:06,Running,29,14,0 2011-10-31,22:39:07,Running,16,15,0 2011-10-31,22:39:08,Running,19,15,0 2011-10-31,22:39:09,Running,19,15,0 2011-10-31,22:39:10,Running,19,14,0 2011-10-31,22:39:11,Running,19,15,0 2011-10-31,22:39:12,Running,19,14,0 2011-10-31,22:39:13,Running,19,0,0
WLSTについては、WebLogic Channel掲載記事『サポート・エンジニア直伝! 「WebLogic Scripting Tool」によるWebLogic Serverの管理/監視テクニック』でも説明しているので、ぜひそちらの記事もご覧いただきたい。
オラクルコンサルが実施するWebLogic Serverのパフォーマンスチューニング


