Amazon SimpleDB の高速で信頼性が高い使い方

Building for Performance and Reliability with Amazon SimpleDB」を読んだ。いつものようにメモ。

所感

読んでいて意外に思ったのが、Getのパフォーマンスを上げるためにDomainを水平分割する必要はないらしいってこと。同じDomainに並列にクエリを投げるだけでGetのパフォーマンスは向上するようだ。データを自動的に複製しているからできることなのだろうか。

対照的に、Putのスループットを上げるにはDomainを分割した上で並列化しなければならない。どんなDBMSでもこれは仕方のないことなのだろう。

2009年4月の記事だからなのか、整合性オプションと性能の関係については書かれていなかった。時期的に、この記事は Eventual consistency を前提として書かれていると思われる。

SimpleDBがエラーを返すことはたぶん珍しくない。数回ほど試しに使ったことしかないが、何度か503が返ってきたことがある。SimpleDBを使うなら、必ずリトライ処理を入れておかなければならないだろう。

BatchPutAttributesを使う

一つずつPutするより、まとめて一回Putする方が高速である、とのこと。
ネットワーク経由の呼び出しが一回になるのだから、それはそうだろう。
複数ItemのBatchPutAttributesが部分的に失敗することはないので、性能向上と同時にデータの整合性を維持する目的でも有用だ。

クエリを並列化する

SimpleDBはクエリを並列化することで実行時間を短縮できる。一つのDomainであっても、複数のクエリを並列でリクエストすることで高速化できる。
例として挙げられているクエリは、6万件近いItemを持つ Clickstream という Domain中で、HitsというAttributeが 0〜1000 のものを検索するというものだ。
(一般的に、DomainはRDBのテーブル、AttributeはRDBの列のようなものとたとえられている。)

select * from Clickstream where Hits between '0000' and '1000'

よりも、

select * from Clickstream where Hits between '0000' and '0200'
select * from Clickstream where Hits between '0200' and '0400'
select * from Clickstream where Hits between '0400' and '0600'
select * from Clickstream where Hits between '0600' and '0800'
select * from Clickstream where Hits between '0800' and '1000'

のそれぞれを並列で実行することで実行時間が5分の1になるらしい。ただし、並列化した5つのクエリが同じ量の結果を返すという仮定つきではある。実際にはきれいに5倍というわけにはいかないだろう。CPU時間に課金されるSimpleDBでは割高になったりしないのか? と疑問に思ったりもする。それでも、Webアプリの性能が向上するなら検討してみる価値はあるのではないか。

水平分割でスループットを向上する

水平分割とは、同じスキーマのデータを複数のテーブルに格納することだ。分割したテーブルを物理的に違う場所に配置すれば、アクセスを物理的に分散することによってスループットを向上できる。

SimpleDBでは、水平分割とは複数のDomainにデータを分けて格納することである。この記事では、Domainを分割する際の判断基準を3つ示している。

  • 一つのDomainでは不可能なほど高いPut性能が必要な場合。
    • 毎秒500 ItemをPutしなければならないのに、Domain一つ当たり毎秒50 ItemしかPutできないなら、Domainを10分割する。
  • RDBMSで言うテーブルスキャン*1が発生するようなクエリを実行する場合。Domainが小さければ大したことはないが。
  • データ量がDomainのサイズ制限を超える場合。

分割の方針として、特定のAttribute値ごとにDomainを用意する方法、ハッシュ値を使う方法が紹介されている。この辺はRDBMSと変わらない。

クエリパフォーマンスの最適化

SimpleDBを使う上でのいくつかのTipsが載っている。

  • nullの比較をしない。nullはインデックスに含まれないからだ。nullを検索に使いたければ、nullを意味する値を入れておくこと。
  • クエリ中で条件を減らすために複合Attributeを用意する。
    • クエリの検索条件で複数のAttributeを使っている場合、それらをまとめたAttributeを作っておいて検索条件を一つにすれば、速くなるということらしい。どれだけ速くなるかは不明。
    • これはアプリケーションの性質にあわせて検討する高度なトピックだ。もしやるなら本当に速くなったのか、検証しなければならないだろう。

アプリケーションをEC2で実行する

EC2のインスタンスは同じ「クラウド」内で実行されるので、ネットワークレイテンシが超短い、らしい。

エラーに対するリトライ

SimpleDBはエラーを返すことがある。I/Oがあるところには必ずエラーがあるので、当たり前ではある。エラーの理由によってはリトライするだけで正常に実行できることがある。安直にエラーを表示するよりは、リトライするように作ると運用コストを減らせる。確かに、エンドユーザがエラー画面の問い合わせをするような事態は皆を少しずつ不幸にする。

この記事では、リトライ時間を「Exponential Backoff」アルゴリズムで決定することを推奨している。要するに、SimpleDBがエラーを返すたびにリトライの間隔を2倍にしていくのがおススメということらしい。

*1:超意訳だが、このエントリは翻訳じゃないので。