2011/Aug/28

PACELCで理解するCAPの定理(2)

第一回目は、CAPの定理をおさらいしました。今回は、いよいよPACELCについて見ていきます。

CAP定理の問題
Abadi氏は、CAPの定理には次のような問題があると言います。
CA型とCP型のシステムは、事実上区別できない。つまり、CAP定理では、あたかもCA, CP, APの計3種のシステムが存在するような印象を受けるが、実際にはCA/CP型とAP型の2種類しかない。
まずはこれについて考えてみましょう。CA型のシステムとは、ConsistentかつAvailableなシステムですが、Partitioningが起きたら、機能を失ってしまうシステムです。CP型のシステムとは、Consistentで、Partitioningが起きても機能を失わないが、その代わりAvailableではないシステムです。
なんだか、確かに分かりにくいですね。よりCAP定理の定義に忠実に従って正確に書いてみましょう。
  1. CA型のシステムとは:
    • Partitioningが起きない限り(=普段は)、ConsistentかつAvailableである
    • Partitioningが起きると、システムは機能を失う
  2. CP型のシステムとは:
    • 普段はConsistentである
    • Availableとは、「ノードがfailureしていないかぎり応答を返せること」なので、実は、普段(=Partitioningが起きていない時)はCP型のシステムもAvailableである
    • Partitioningが起きると、Availableではなくなるが、Consistentであり続ける
まだ分かりにくいので、具体例で見ていきましょう。

 2つのノードで同期的にレプリケーションを行っているリレーショナルDBを考えましょう。どのWriteも、両方のノードのディスクに書き終わらない限り完了しないという設定で組んでいるとします。もし2つのノード間で通信が途絶えてしまったら、両方のディスクに書けないのでシステムは機能を失ってしまいます。これが、CA型のシステムですね。

 これではシステム全体の可用性が少なすぎるとしましょう(どちらかのノードが壊れるか、通信が途絶えるかするだけでシステムが停止するので)。そこで、「通信が途絶えたら、IPの若い(順番が)方が自分のディスクだけをつかってオペレーションを継続する」という約束をすることにしましょう。こうすれば、通信が途絶えてもIPの若いほうが生きてさえいればオペレーションを続けることができます。
 IPが古い方のノードは「failしていないのに応答できない(正確には『リクエストを実行できない』というエラー応答となるわけですが)」ことになり、「failしていないノードはリクエストに応答しなくてはならない」というAvailabilityの条件を満たさなくなります。

 もし、どうしてもAvailabilityを保持したかったとしたらどうでしょうか?その時は、レプリケーションを非同期にする必要があります。お互いのDBが、発生した変更をキューに貯めておいて、通信が復活したら相手に伝達します。
 その間は、お互いDBの中身が違うので、Consistencyは守られません。
 つまり、事実上、CA型のシステムとCP型のシステムがあるわけではなく、Pが起きたときにCを選ぶか、Aを選ぶかという選択があるのです。

 ここまでが、「CA型とCP型のシステムは、事実上区別できない」とするAbadi氏の主張の説明です。Abadi氏はこの論理から、PACELCの"PAC"を提唱しています。「もしPが起きたら、AとCどちらを選びますか?」という意味です。

 次に、残りの"ELC"について見てみましょう。先に説明しますと、ELCとは else Latency xor Consistencyです。PACから書くとこうなります。

if P then Availability xor Consistency else Latency xor Consistency
 日本語で書くとこうなりますね。
もしネットワーク分断が起きたら、Availabilityを選びますか?それともConsistencyを選びますか? あと、ネットワーク分断が起きていない時は、Latencyを選びますか?それともConsistencyを選びますか?
 Latencyという要素が急に入ってきましたが、これはなんでしょうか?これが、Abadi氏が主張するCAPの定理の2つ目の問題です:
CAPの定理は、LatencyとConsistencyのトレードオフを考慮していない。
PACだけに注目すると、じゃぁPが発生していないときはAvailabilityとConsistencyどっちも取れてハッピーハッピーになるはずなのですが、世のNoSQLデータベースは普段からConsistencyを犠牲にしています。

 どうしてそんなことをするかというと、Latency(≒レスポンスタイム)を短くする為です。例えば、負荷分散のために10台のリレーショナルDBをレプリケーションして使いたいとしましょう(みんな同じ中身)。Consistencyを達成するためには、Create/Update/Deleteを行う時は10台全てにリクエストを発行して、完了するまで待たなければいけませんが、そんなことをしたらLatencyはかなり悪くなってしまいます。2台選んでリクエストを完了し、後からこの2台が他のノードにリクエストを非同期で発行するのはどうでしょうか?これなら運悪く1台が死んでももう1台がいますから1台にだけ発行するのに比べると信頼性がアップしますし、10ノードに発行するのに比べれば、Latencyが改善します。Cassandraなどは、実際にこれに似たようなことをやっています。

 話が少し横にずれてしまいましたが、要するに、LatencyとConsistencyはトレードオフの関係にあり、システムはどちらかを選ぶ必要があるということなのです(実際にはイチゼロじゃなくて、どちらをより優先するか連続的なスペクトルから選ぶ感じですが)。

 では、いくつか実例を見てまとめとしましょう。PCECシステム(if Partition then Consistency, else Consistency)はどんなシステムでしょうか?何回か例に出てきた、2つのノードが同期的にレプリケーションを行っているリレーショナルDBなどが考えられます。このシステムは、ネットワーク分断が発生すると、生きていてもリクエストを処理できないノードができてしまいます(if P then Availability喪失)。普段も、2ノードでWriteが完了しないとリクエストが完了しないので、Latencyも悪いです(elseの時、 Latency無し)。その代わり、普段はConsistencyが守られていますし( else Consistency)、Pが起きてもConsistencyを守り続けます。つまり、if P then Consistency (and not Availability), else Consistency (and not Latency)、よってPCECです。

 PAELシステムはどうでしょうか?これも前出てきたDNSが当てはまります。普段はそのDNSサーバのテーブルがアップデートされてさえいれば、他のDNSサーバがアップデートされているかなど考えずに応答を返すので、Latencyはいいです(else Latency)。DNSサーバ間で通信が途絶えて、同期が行えなくなっても各自応答は返し続けるので、if P then Availabilityとなっています。その代わり、要求を処理するDNSサーバによって結果が変わることがありえるので、Consistencyは失われています。

 他の組み合わせはどうでしょうか?Abadi氏は、PCELなシステムの例としてYahoo!のPNUTSを挙げています。PNUTSは、普段Latencyを得るため伝統的なリレーショナルDBなどに比べて弱いConsistencyを使っていますが、Pが発生すると、Availabilityを犠牲にしてでも、そのレベルのConsistencyを守り続けます。(Consistencyといっても様々なレベルがあるので、ここは分かりにくいですね。。)

 PAECシステムだと、普段はConsistencyを維持していますが、Pが起きるとAvailabilityを守るためにConsistencyを犠牲にし始めます。たまにConsistencyが破られる可能性があるというのは、Consistencyがないのに近いので、PAECなシステムが本当にあったとしたら、非常に使いにくそうですが、後からある程度のコストでコンフリクトを解消できるような局面では、あり得ない選択ではないかもしれません。

 以上、PACELCで理解するCAPの定理でした!
Recent articles on 分散システム
posted by 塩路慧能 (Enno Shioji) at 11:53 | Comment(1) | TrackBack(0) | 分散システム | Subscribe to this blog | Check for updates

2011/Jul/10

PACELCで理解するCAPの定理(1)

 CAPの定理が何故最近話題になっているかについての説明などは他サイトに任せることにして、このエントリではCAP定理の技術的な解説、および、CAP定理をより分かりやすく、かつ(簡単に言えば)より正確にした「PACELC定理」の解説を行います。
 PACELCの定理とは、エール大学のAbadi氏が提唱しているもので、CAPの定理のいわば発展形です。CAPの定理の「難しい版」と思われていることがあるようですが、そんな事はありません。むしろ、多くの学生がCAPの定理で苦労するのを見たAbadi氏が、学生に分かりやすく説明するために考えたものなので、CAPの定理をより分かりやすくしたものと言えます。ただ、それだけでなく、CAPの定理には入っていなかったLatency(≒レスポンス時間)の概念も合わせて捉えることで、より正確に分散データベースの特性を考えられるようになっています。

 ではまず、第一回目はCAPの定理をおさらいしましょう。

CAPの定理:次の3つの特性を、全て同時に示す分散システムを実現することはできない。
  1. Consistency(一貫性, C)
  2. Availability(可用性, A)
  3. Partition-tolerance(分断耐性, P)
すなわち、どのような方法をもってしても、ある時点でシステムが示すことができる特性は、C, A, Pのうち任意の2つまでである。

Consistencyとは
 ここでは広義に使われており、ACIDのCと同義ではありません。説明をわかりやすくするため、ここでは単純な分散Hash mapを具体例として見ていきましょう。
 最も単純なConsistencyには、AtomicityとVisibilityがあります。Atomicityのうち、最も単純なのは、「全てのReadは、Write処理が完了する前の状態か、または完了した後の状態のいずれかしか見ることがない」という性質です。プログラミングで例えるなら、map.get(key)を呼んだら、値が返ってくるか返ってこないかの2択しかしかなく、「値が半分だけ」返ってくることが絶対にないということです。「半分だけ」値が返ってくるってどういうことよと言われてしまいそうですが、この例では例えば正しくメモリ上に書き込みが終わっていない値が返ってくることが想定できます。イメージとしては、本当は10100010が返されなければいけないのに、まだ書き込みが終わっていないために00000010が返ってしまう感じです。この単純なAtomicityが実現されているということは、これが起こることがないという保証があるということです。※一般的にアトミックと言うとリレーショナルDBでいうAtomicityを考えると思いますが、今回の説明ではこの最低限のAtomicityで十分なので、単純化しています。

 Visibilityとは、全てのユーザ(例えばスレッド)が同じ状態を「見る」ことができることです。もう少し具体的に言うと、あるWriteが完了したなら、その後に発行されたReadは全て「最悪でも」このWriteの結果を返すという約束です。当たり前に聞こえますが、後述するように、パフォーマンスなどを考えて、「Writeが起こった直後は書いた人にしか見えないが、じわじわと他の人にも見えていくようにする」ことを許す場合があります。これがよく分散データベース("NoSQL")で言われる"Eventual Consistency"の一種です。すぐみんなが同じ状態を見れはしないけど、いずれは見ることができるようになる、という意味で"Eventual"なのですね。意図せずにこれを許してしまうのがVisibilityバグで、マルチスレッドプログラミングでよく起こるバグの一つです。
 ところで「最悪でも」ってなんやねんという話なのですが、これは、Write1→Read1→Write2の順にオペレーションが発行されたときに、Read1がWrite2の結果を見るのはOKということです。別にWrite2の結果が見えたほうがいいということではないので「最悪でも」という言い方は誤解を招くかもしれませんが、「少なくともWrite1までのアップデートを逃すことだけはNGにしましょうね」ということです。「Read1が、Write2ではなく、必ずWrite1の結果を受け取らなければいけない」というのはもう一つさらにきついレベルのConsistencyです。ここでは最低限のConsistencyについて考えたいので、よりゆるい、Write2の結果が返ってしまうことを許すvisibilityを考えます。

 「Writeの結果がすぐに全員に見える」のと、「ちょっと後に全員に見えるようになる」のと何か本質的な違いはあるのでしょうか?これについて考えるために、「同じ値(value)を許さない」Hash mapベースのアプリを考えてみましょう。どのスレッドも、一旦挿入したい(kx,vx)を挿入してしまってから(map.put(kx, vx))、map.values (v0 ... vx ... vn)を1つ1つイテレートしてvxと比べ、vx以外にvxと同じvyを発見したら、重複の疑いがあるケースとしてマークし、後で調査して削除するためにどこか(delete_list)に書き残します(次のinsert関数参照)。
 これをたくさんの(k,v)ペアに対して行い、最終的にユニーク、つまりどの値(value)も1回しか入っていないマップを得ることがアプリの目的です。



 さて、今まさに2つのスレッド(t1, t2)がそれぞれ(k1, v1), (k2, v1)を登録しようとしているとしましょう。同じv1なので、少なくとも1回重複疑いのアラートがあげられなければいけません(2回以上あがってしまうのはOK)。

 このHash mapは最低限のAtomicityしか備えていない設定ですので、put, removeはそれぞれアトミックですが、insert自体はアトミックではありません(これは、insert処理の途中の状態を、他のスレッドに見られたり、書き換えられたりする可能性がある、ということ)。なので、考えられる処理の順番としては、次の3つが考えられます(t1とt2を入れ替えたものは同じと考える。t1、t2それぞれの中ではput→readの順番は保たれる)。
    1. t1のput(k1,v1)
    2. t1のread(他のv1無し)
    3. t2のput(k2,v1)
    4. t2のread(他のv1有り)

    1. t1のput(k1,v1)
    2. t2のput(k2,v1)
    3. t1のread(他のv1有り)
    4. t2のread(他のv1有り)

    1. t1のput(k1,v1)
    2. t2のput(k2,v1)
    3. t2のread(他のv1有り)
    4. t1のread(他のv1有り)

 これが、「すぐに結果が全員に見える」という保証がなかったらどうなるでしょうか?タイミングによってこういうことがあり得てしまいます。
    1. t1のput(k1,v1)
    2. t1のread(他のv1無し)
    3. t2のput(k2,v1)
    4. t2のread(他のv1無し(まだ見えなかった))

    1. t1のput(k1,v1)
    2. t2のput(k2,v1)
    3. t1のread(他のv1無し(まだ見えなかった))
    4. t2のread(他のv1無し(まだ見えなかった))

    1. t1のput(k1,v1)
    2. t2のput(k2,v1)
    3. t2のread(他のv1無し(まだ見えなかった))
    4. t1のread(他のv1無し(まだ見えなかった))

正確には、先述のvisibilityの仮定から、ReadがWriteの結果を「先取り」してしまうことはあり得る。「アラートがあがらない保証がある」ではなく、あくまで「アラートが必ずあがる保証がない」ということに注意


 これで、visibilityがあるのとないのとでは本質な違いがあることが分かっていただけたかと思います。この違いは性能面でも顕著に現れます。visibilityを担保しなくてはいけない処理系は、visibilityを担保しなくても良い処理系に比べて性能が低くなります。CAPの定理を説明する上では、とりあえずこのようなVisibility(と最低限のatomicity)を備えたものを「Cのある分散DB」、備えていないものを「Cのない分散DB」と考えておけば良いです。

Availabilityとは
 Availabilityとは、「failしていないノードに発行した(発行できた)リクエストは、必ずレスポンスを返さなければならない」ということです。リクエストが発行できたなら、必ず結果が返ってこなければいけないということですね。

Partition Toleranceとは
 Partition Toleranceとは、分散システムにおいて、ノード間で通信ができなくなってもAvailabilityまたはConsistency(のどちらか1個)が保持されることです。「通信分断耐性」とでも訳せるでしょうか。分散システムなので当然複数のノード(サーバ)があるわけですが、これらノード間で通信ができなくなっても、システムが機能を維持できる特性のことです。機能といってもAvailabilityを保証するシステムもあれば、Consistencyを保証するシステムもあるので、「Pを備える」という言葉は、システムによって違う意味を持ちます。Availabilityを保証するシステムでは、ネットワークの分断が起きてもAvailabilityが保証され続けることを意味し、Consistencyを保証するシステムでは、ネットワークの分断が起きてもConsistencyが保証され続けることを意味します。

※正確な定義、CAPの定理の証明についてはLynch & Gilbertの論文を参照

具体例
 ちょっと分かりにくくなってきたかもしれないので、具体例で説明しましょう。例えば、DNSは典型的なAP型システムです。DNSでは、DNSサーバ1に問い合わせると「見つからない」というレスポンスが返ってくるが、DNSサーバ2に問い合わせるとちゃんとIPが返ってくる、ということが日頃起きています。そのうちどちらのサーバも最新に更新されていくわけですが、ラグがあったりして、少なくともある時点に着目したときに、問い合わせるサーバによって結果が異なることがあり得る、つまり、「全員が同じ状態を見るという保証がない」ため、Consistencyが破られていることになります。
 さて、ある日ルータの故障か何かでDNSサーバ1と2の間の通信が不能になったとします(Partitioningの発生)。DNSでいうPartition Toleranceとは、この状況でAvailabilityを保証し続けること、すなわち、DNSサーバ1へのリクエストも、DNSサーバ2へのリクエストも、必ずレスポンスが返ることを保証することです。DNSサーバ1,2の間で通信ができなくなったところで、更新が遅れるだけで、引き続きどちらのサーバに対してもRead/Writeは実行できるため、Availabilityは達成されていることになります。もし、通信ができないからと言って問い合わせやレコードの追加を拒否する状態になればPartition Toleranceが満たされていないことになります。


 逆にCP型のシステムにはどんなものがあるでしょうか?最も分かりやすいのは、2つのノードで同期的にレプリケーションを行っているリレーショナルDBです。どのWriteも、両方のノードのディスクに書き終わらない限り完了しないという設定で組んでいるとしましょう。もし2つのノード間で通信が途絶えてしまったら、両方のディスクに書けないのでシステムは機能を失ってしまいます。それでは不便なので、「通信が途絶えたら、IPの若い(順番が)方が自分のディスクだけをつかってオペレーションを継続する」という約束をすれば、通信が途絶えてもIPの若いほうが生きてさえいればオペレーションを続けることができます。しかし、IPが古い方のノードは「failしていないのに応答できない(正確には『リクエストを実行できない』というエラー応答となるわけですが)」ことになり、「failしていないノードはリクエストに応答しなくてはならない」というAvailabilityの条件を満たさなくなります。もし両方ともオペレーションを続けたら、Consistencyを保てなくなる点に注意してください。例えば、ノード1にWriteした結果が、ノード2からは見えないわけですので、こういう約束をしていなければシステムを止めなければいけなくなります。
 ノードが1つだけオペレーションを続けたときは、当然従来通りConsistencyが保たれます。

 CAPの定理に従えば、残るはAC型のシステムですが、これがCAP定理を理解しにくくしている原因の1つであるとAbadi氏は主張します。AC型のシステムとは、Partitioningが起きない限りはAvailableかつConsistentなシステムですが、CP型のシステムだって、Partitioningが起きない限りはAvailableなわけです。逆に、AC型のシステムだって、Partitioningが起きてしまったら結局Availabilityを失います。つまり、AC型のシステムと、CP型のシステムは、事実上同一なのです。CAPの定理では「3つのシステム」が作れると説明しますので、AC型のシステムとCP型のシステムは別物と考える人が多く、混乱を招いていると氏は主張します。


と、とりあえず今日はここまでとして、次回はこれを踏まえてAbadi氏が提唱するPACELCについて解説していきたいと思います。


posted by 塩路慧能 (Enno Shioji) at 14:35 | Comment(0) | TrackBack(0) | 分散システム | Subscribe to this blog | Check for updates

2011/Jun/19

世界は再び小さな村へ・・・ 匿名社会の終焉

少し前に、カナダのバンクーバーで市民の一部などが暴徒化する事件がありました(記事)。バンクーバーでそのような事件が起こったの事自体も驚きでしたが、もう一つ、非常に面白い社会現象が見られました。

reddit.com、facebook、youtubeなどのソーシャルメディアに、現場の写真・映像が次々と投稿され、違法行為に参加していた人々が次々と特定されていったのです。あるカナダのブログは、これらをまとめて、違法行為を働いていた人をいわば晒し者にする"Hall of Shame"キャンペーンを始めました(ブログ)。普通の大学生やスポーツ選手などが、違法行為の瞬間を捉えた写真や映像と共に、実名を掲載されています。誤認もあり得ますし、このようなことをすること自体は批判されるべきでしょう(警察などに情報提供するべき)。しかし、この出来事は、これからは「匿名」であることが、オンラインだけでなくオフラインでも難しくなっていることを示しています。

まだ実用化には至っていないものの、普通のスナップ写真から顔を特定し、顔認証で映っている人物を特定する研究が各地で進んでいます。、画像だけでなく、動画からリアルタイムで人物を探す技術の研究もされています。このような技術が実用化されるのは、時間の問題と言えるでしょう。

このような技術の発達で、世界は再び「ムラ社会」に回帰しようとしているのではないでしょうか。技術は新しいものですが、その技術がもたらす世界は、案外かつての田舎に近いものなのかもしれません。だとすれば、大都会に慣れた人は、新しい世界の秩序の中でどううまく生きて行くべきか、田舎に学ぶ必要があるかもしれませんね。

EDIT:カナダの警察に、100万枚以上の写真と1000時間分以上の映像が寄せられ、警察では顔認証技術も使用してこれらを解析するとのことです。現在の顔認証技術ではどこまで有効かわかませんが、本当に象徴的な事件となりました。
posted by 塩路慧能 (Enno Shioji) at 15:09 | Comment(0) | TrackBack(0) | 世の中 | Subscribe to this blog | Check for updates

2011/Jun/14

Eclipseが遅くて腹たってる人中心に、Intellijのススメ

どうやら日本語化されていないようなので日本で使っている人は少数派かもしれませんが、グーグルやRedHatのOSS開発者の間などで大人気、人気急上昇中のIntellij IDEAというJava IDEがあります。

チェコの数学者が集まって作ったIDEで、有料版しかなかった頃から大変評判が良く、割と出来のよいeclipseが無料で誰でも使えるという市場環境にも関わらず大いに健闘していました。この会社は、.netの分野でもいくつか人気の高い開発ツールを出しています。本当にすごくいい噂を聞くIDEなので気にはなっていたのですが、貧乏の身なので試すこともできず...ご多分に漏れず僕もeclipseをずっと使っていました。

が、2009年にベンダが"Community Edition"型のビジネスモデルに移行。要するに、基本機能を備えた版が無料で使えることになったのです!しかもCommunity EditionはApacheライセンスのオープンソースに。太っ腹です。採算は、より高機能のUltimate Editionを販売することでとろうとしているようです(※ちなみにOSSの開発に使う場合は、Ultimate Editionも無料とのことです)。

てなわけで当時早速使ってみたのですが、結論から言うと、もう絶対離せません(笑) 最初はeclipseとの違いに戸惑うことも少なからずありましたが、取りあえず速い。これホント重要。もうこれだけで頑張って乗り換えようという気になります(笑) 次に、Mavenとの連携がマジいい!今は改善されたのかもしれませんが、eclipseのmavenプラグインは大分不安定だったので、かなり違いを感じました。他のプラグイン・機能の類も概して非常に安定して動作し、性能も良いです。オートコンプリートもeclipseより若干かしこく、本当にサクサクプログラミングができます。

他にはリファクタリングがeclipseよりも高機能、なかなか良いコードの静的解析が標準装備されている、すごく高機能のコード変更履歴機能がついている、あたりが思いつく利点でしょうか。ただ、何よりも速い。これにつきます(少なくともeclipse galileo当時はかなりの差があった)。

eclipseに劣るところといえばプラグインの少なさですが、それでもIDEがオープンソース化される前から、プラグインを開発するためのAPIと、そのプラグインを公開するためのマーケットプレースのようなものが提供されていたので、そこそこの数のプラグインは提供されています。今回オープンソースになったので、さらにプラグイン開発の裾野が広がってくれそうです。

商売道具ですから、長年eclipseを使っていると移行の際のストレスはかなりありますが、それを我慢する価値アリです。


とは言えeclipseほどたくさんの言語をサポートしているIDEはないので、今もpython, erlangの開発にはeclipseベースのIDEを使っています。特に、AptanaというところがWeb開発者向けの多言語対応型eclipseパッケージを提供してまして、これがWeb開発には本当に便利です。やっぱ、悪くはないですよね、eclipse。それでも、もっと速いIDEが欲しい。。。と思っている人は、是非Intellijを試してみてください。


posted by 塩路慧能 (Enno Shioji) at 00:31 | Comment(0) | TrackBack(0) | プログラミング | Subscribe to this blog | Check for updates

2011/Jun/11

Servlet 3.0の「非同期」サポート -- 何がうれしいのか?


マニアックすぎる話ですが(^^;)
ひとことで言うと、「これまでは、リクエスト処理時間が長めで、同時接続数が非常に多いサービスをJavaで作る場合はマニアックな方法を使うか、我慢して高いHWコストを払わなければいけなかったが、これからはプログラマが親しんだJavaEEを使ってHW効率のいい実装ができるようになった」ということになります。

じゃぁ「リクエスト処理時間が長めで、同時接続数が非常に多い」って具体的にどんな基準よって話なのですが、僕の超独断と偏見に基づく、超ざっくりな基準はコレです:

  • ビジネスロジック(コンテナからリクエストを受け取ってから、レスポンスをコンテナに渡すまで)部の実行時間が、3-5秒以上
  • 実行時間が長くなる原因が、ビジネスロジック内で行うIO(ディスク・ネットワーク)
  • ピーク同時接続数が500-1000以上


一応経験に基づいた値ではありますが、当然こんな画一的な基準を提示するのは無謀と言ってもいいくらいなので、ご了承お願いいたします(^^;) さらに、新しいアプリか既存アプリかによって次のようなことを考える必要があります:

  • 新しいアプリの場合
    • そもそもそういうリクエスト処理時間・同時接続数になるのは適切なのか?
    • 本当に、従来の方式で実装したら問題が出ると予測されるか?
    • そもそも同期のインターフェースにすることは適切?メッセージキューなどの非同期型インターフェースの方が適切だということはないか?
    • 使えなくなってしまうフレームワークなどが出るが、それを補う案はあるか?
    • JavaEEとはいえ、従来のプログラミングモデルとはかなり違ってしまうが、それにうまく適応できそうなチームか?
    • 枯れた技術から新しい技術へ移行する以上、安定性などの面でリスクがあるが、それを凌ぐメリットがあるか?
  • 既存アプリの場合
    • 症状の原因は、本当にこの特定の並行性能問題か?
      1. Connection RefusedやRead timeout、異常に処理時間が長いなどの問題がクライアント側で確かに出ていて、サーバ側の問題に間違いない
        • 負荷バランサのせいだったりすることも。。
      2. 異常ではない
        • 外部のWebサービスなど外部のサービスに異常が生じて、異常にリクエスト処理時間が長くなっていることも
        • インフラの異常ではない?サーバ仮想化を使っている場合、仮想マシンを上げすぎて性能が極端に低下しているなどということはないか?(実話(^^;)ネットワーク・DB・ストレージは?他のプロセスがサーバのリソースを圧迫していないか?
        • バグではない?クライアント側が異常にリクエストを繰り返しているということはないか?サーバ側のバグで余計な処理をしていたり、DB処理が無駄に長く掛かっていることはないか?メモリリークではないか?など
      3. ビジネスロジック部の実行時間が確かに長い
        • コンテナによっては統計を出してくれるかも。プロファイラでスレッドの状況を見るのが一番良いでしょう。
        • 「ビジネスロジック部は短いが同時接続数が非常に多い」というシチュエーションなら、NIOコネクタの使用で解決される可能性も。最近のメジャーなコンテナならまず使えるはず
      4. 単にコンテナの設定が悪いわけではない
        • ドキュメントを参考にチューニングしても症状が持続するかチェックする。実は誰かがデフォルトの設定に余計なことをして問題が起きていることも往々にしてある
      5. スレッドリソースが確かにビジネスロジック部で浪費されている。具体的には、多くのWorkerスレッドがビジネスロジック内で待ち状態か?
        • たくさんのWorkerスレッドが実行可能状態の場合は、コンテキストスイッチスラッシングが起きている可能性があり、むしろmax-threadsを下げると解決する可能性も
        • これもコンテナによっては統計を出してくれるかもしれませんが、僕はスレッドダンプを打ち出して集計してます(^^;)
      6. 設計やデータ量・処理量などから考えて、多くのWorkerスレッドが待ち状態におかれるということが確かにありそうなことか?
    • 以下の選択肢を全て検討したが、ざっくりとした改造コストとテンビンにかけても、なお改造の方が良い可能性がある
      1. もっといいハードウェアを買う
        • 最も容易だが、金がかかる上すぐに限界が訪れやすい
      2. HWを足して、負荷バランスする
        • まともな高可用性を備えたアプリなら簡単にできるはずだが、コストパフォーマンスがかなり悪くなる場合も。サーバは当然のことながら、負荷バランサでもコストがかかることがある
      3. 他のところやビジネスロジックで最適化などを行い、Workerスレッドが待ちになる時間・リクエスト処理時間を減らす
        • どのくらい「贅肉」があるかによる。基本的に、アプリ・外部サービスが新しかったり、今回ほどの性能要件でテストされたことがなかったり、担当したチームの能力が低かった場合などは、「必要なインデックスが張られていない」などの「削ぎやすい贅肉」が見つかる可能性が高い。そうでない場合は効果の予測が困難
    • 「新しいアプリ」編のチェック項目全て


ここまで検討しても、なお「非同期処理」がベストの選択肢であると考えられるなら、真剣に検討してみる価値がありそうです。次回は、実際にServlet 3.0でのWebサービスを作ってみます(あくまで予定ですが(^^;))
posted by 塩路慧能 (Enno Shioji) at 17:47 | Comment(0) | TrackBack(0) | プログラミング | Subscribe to this blog | Check for updates