現状のmemcachedのバイナリプロトコルのクライアント（=libmemcached）は、リクエストの順番通りにレスポンスが返ってくることを期待しており、これはmemcachedバイナリプロトコルを「汎用的なkey-valueベースの分散ストレージのためのプロトコル」として考えると、ひどい実装である。

そのような実装は最適化の余地を大幅に制限してしまい、性能とスケーラビリティが悪化する。memcachedの仕様書は、そのようなクライアントの実装はバグであると明示するべきである。

現状のmemcachedクライアントの実装の問題点と、その解決策について述べる。





同期プロトコルと非同期プロトコル

ネットワークプロトコルは以下の2つの種類に分けられる：

同期プロトコル リクエストの順番通りにレスポンスを返す（リクエストの順番とレスポンスの順番が同期している） 非同期プロトコル リクエストした順番通りにレスポンスが返ってくるとは限らない（リクエストの順番とレスポンスの順番が同期していない）

libmemcachedはmemcachedのバイナリプロトコルを同期プロトコルとして扱っており、リクエストした順番通りにレスポンスが返ってくることを期待している。同期プロトコルを採用するとクライアント・サーバーのどちらもその実装方法が制限されてしまい、最適化の余地を減らしてしまう。

同期・非同期の違いは、リクエストをパイプライン化したときに発現する。

パイプライン化とは、リクエストを送ったあと、そのレスポンスを待たずに立て続けに次のリクエストを送ってしまうことである。複数のリクエストを送りたいとき、レスポンスを待たずに一度に送ることできるので、性能が向上する。

以下のようにクライアントがリクエストをパイプライン化して送信するとき、同期プロトコルではレスポンスはリクエストを送った順番通りに返ってくるが、非同期プロトコルでは順不同になる。

Synchronized protocol: Asynchronized protocol: send request 1 send request 1 send request 2 send request 2 send request 3 send request 3 receive response for request 1 receive response for request 2 receive response for request 2 receive response for request 3 receive response for request 3 receive response for request 1 The order of the responses is The order of the responses is NOT syncnhronized with the requests. synchronized with the requests.

この例で、request 1の処理は非常に時間のかかるもので、request 2の処理はすぐに終わるものだったとしよう。

同期プロトコルでパイプライン化を使ってrequest 1とrequest 2を送信したとき、request 1の応答が返ってくるまでrequest 2の応答を得ることができない。つまりアプリケーションはrequest 1の処理が終わるまで長い時間待たないと、request 2に関する処理を継続することができない。

同期プロトコルでこの問題を回避するためには、パイプライン化を使わずに、request 1とrequest 2を別々のコネクションで送れば良い。しかしコネクションを増やすとサーバーの負荷が増加してしまうため、結果としてスケーラビリティを悪化させる。

非同期プロトコルでは、パイプライン化してrequest 1とrequest 2を送信しても、request 1にかかる処理時間にかかわらず、すぐにrequest 2の応答を得ることができる。

次にサーバーを実装することを考えてみる。

マルチコアCPUが当然となっている現在では、マルチスレッドを使って複数のリクエストを同時に処理しなければ性能が向上しない。また1台のサーバーだけでは性能が不足するときは、複数のサーバーを使って複数のリクエストを同時に処理しなければ性能が向上しない。memcachedのバイナリプロトコルを汎用的なストレージプロトコルとして捉えると、memcachedを実装するのに十分であれば良いのではなく、そのような用途にも対応できる拡張性を備えているべきである。

リクエストの内容によってその処理にかかる時間は異なるため、先に受け取ったリクエストよりも、後に受け取ったリクエストの方が（別のスレッド・サーバーで処理された結果）先に終了することは大いに有り得る。

しかし同期プロトコルでは、request 1とrequest 2がパイプライン化されて送られてきたとき、request 2の処理が先に終わったとしても、サーバーはrequest 2に対する応答をrequest 1の処理が終わるまで保留させておかなければならない。これはサーバーの実装を複雑化させる原因となり、結果として性能を悪化させる。

非同期プロトコルでは、単に処理が終わった順に応答を返せばよい。

現状のmemcachedバイナリプロトコルの仕様書では非同期プロトコルであることが示されていないため、クライアント・サーバーの両方で最適化の余地を減らしてしまい、性能が悪くなる。

memcachedバイナリプロトコルは、同期プロトコルを禁止するべきである。



