Aerospikeを使う前におさえておきたい5つのこと

Posted by hiroki.kana on December 24, 2015
このエントリーをはてなブックマークに追加

はじめに

この記事は ドワンゴ Advent Calendar 2015 の24日目の記事です。

みなさん、クリスマス・イヴをどのように過ごす予定でしょうか。「世の中にはこんなにカップルというものが存在したのか」というほどカップルで溢れかえる街をを通り過ぎ、帰宅した後に読むと心のイルミネーションが灯るような話をクリスマス・イヴの今日はみなさんにお届けしたいと考え、高速な分散KVSであるAerospikeの仕組みと使う前に把握しておいた方が良さそうな点をお届けします。

これは私からのみなさんへのクリスマスプレゼントだと思っていただいても構いません。「もらってばかりでは悪いのでお返しをしたい」と万が一感じてしまったら個人的に連絡をください、Amazonのwishlistを送ります。

Aerospikeとは

Aerospikeとは高速な処理を得意とする分散KVSです。2014年6月24日にCommunity Editionとしてオープンソース化され,githubを通じて公開されています。最近WEB+DB PRESS Vol.87で紹介されてご存知の方も多いかとは思います。

Aerospikeはデータをメモリ上にに保持するmemcachedのような形態から、ディスクやSSDなどに永続化するような形態、さらに2つを組み合わせたような形態を取ることができます。大量のデータを保持するようなケースでは、高速に処理を行うとともにメモリより容量単価が安いSSDなどの領域を利用することができます。

Aerospikeの特長

Aerospikeは高速性を売りにしています。同等のNoSQLと比較し10倍のパフォーマンス、そしてデータの99%を1ms以下、99.9%を5ms以下で処理することができるとしています。パフォーマンスに関する事例の一例として、Google Cloud Platform Japan Blog内のこの記事によると、Google Cloud Platform 仮想マシン 20ノードで400万TPS(トランザクション/秒)を実現しています。また、その性能はマシンを増やせばリニアにスケールするとされています。

さらにサービスを止めることなく、ノードの追加・削除・アップデートを行い、クラスタを拡張することができます.その際のデータの配置し直し(Aerospikeではmigrationと呼びます)も自動で行われるうえ、migrationをサービスに影響が無いような速度で行うことができます。このような仕組みのおかげで、Aerospike社が扱っているシステムでは3年間無停止で稼働し続けることができているとされ、安定性の高さをうたっています。

Aerospike大まかな仕組み

次にAerospikeの仕組みを簡単に紹介します。

Aerospikeクラスタの構成

Aerospikeは複数のノードで構成されたクラスタを組むことができます。クラスタには 、各ノードに同一のマルチキャストアドレスと同一の設定がされていれば自動的にクラスタに参加することができます。クラスタ構成後はそれぞれのノード同士がメッシュ構造で接続し、ハートビートで生存確認をしています。特定のノードが消失した場合には自動的にクラスタの再構成とmigrationが行われる仕組みになっています。それはノードが追加された時も同様です。

Aerospikeのデータ配置

Aerospikeではデータをクラスタ内のノードに分散して保持するだけでなく、データを冗長化することも可能です。読み書きに利用されるデータをmaster、バックアップとして利用されるデータをreplicaと呼びます。replicaの数はreplication factorで設定し、2の場合はmasterとreplicaが1つづつ、3の場合はmasterが1つ、replicaが2つという意味になります。

データは4096個に分け、クラスタの各ノードに均等に配置されます。この4096個に分かれた単位をパーティションと呼び、それぞれにパーティションID(0〜4095)が割り振られています。パーティションIDはRIPEMD-160でハッシュ化されたハッシュ値のうちの12bitを利用がパーティションIDとして利用しています。これによりデータが一部のパーティションに偏らないように工夫されています。

それぞれのノードはパーティション単位でデータを受け持ちます。どのノードがどのパーティションのmasterを持つべきであるか、またreplicaを持つべきであるかという情報はパーティションマップという表のようなもので管理されています。パーティションマップはPAXOSアルゴリズムと呼ばれるゴシッププロトコルを用いてすべてのノードで同期され、ノードの追加・削除時にクラスタの再構成が行われパーティションマップが変更されても同じパーティションマップを持ち続けています。

Aerospikeへのデータの配置と読み書き

AerospikeクライアントからAerospikeクラスタにデータを読み書きする流れを紹介します。

クライアントはまずクラスタの任意のノードに接続し、クラスタの構成情報とパーティションマップを取得します。リクエスト時にはキーのパーティションIDを計算し、取得したパーティションマップから、どのノードがmasterのデータを持つノードか把握し、クライアントから1ホップでデータの読み書きすることができます。パーティションマップは各ノードと同様、クライアントとクラスタの間でも同期が取られ、更新があった場合にはクライアントが保持しているパーティションマップも更新されます。

読み込みの際、前述の通り直接masterノードへクライアントが直接アクセスし、replicaノードのデータは利用されません。

書き込みの際には、まずmasterとなるノードに直接クライアントからmasterとなるノードにデータが届き、メモリ上に書き込まれます。masterとなるノードはこのデータをreplicaとなるノードに転送し、そのノードのメモリ上に書き込みます。master / replicaとなるノードのメモリ上に書き込みが完了してからクライアントに書き込み成功のレスポンスをします。このメモリ上に書き込まれたデータは非同期でSSDなどのディスク上に書き込まれます。これがAerospikeの書き込み処理を高速化している一因とも言えるでしょう。

Aerospikeを使う前におさえておきたい5つのこと

少々前置きが長くなりましたがここからが本題です.ここからはAerospikeを使う前におさえておきたいことを5つ紹介します。

1.保存されたデータからキーを参照することはできない

各データはrecordと呼ばれる単位で保存されます。recordは下記のものから構成されています。

  • key
    • レコードを一意に識別するキー。ユーザーが指定したキーをRIPEMD-160によって出されたハッシュ値が入る
  • metadata
    • recordの編集回数(generation)とTTL(生存時間)
  • bins(fields)
    • Aerospikeでは単純な値ではなく、値に名前をつけて複数のデータを格納することができる。それぞれをbinと呼び、binの集合をbinsと呼んでいる

これらを見てわかるようにkeyはハッシュ化されてしまっているため、キーを指定するのではなく特定領域のすべてのデータを取り出した場合(例えばScanやバックアップ)は上記のデータしか存在しないため元のキーを確認することはできません。

これを回避するためには、冗長ではあるうえ余分に領域を消費しますがbinsにキーを格納しておくことで回避することができます。

[2015/12/24 追記]

はてブコメントにてクライアントのWritePolicyでキーを値として保存するオプションがあると教えていただきました。ありがとうございます!Cクライアントではas_policy_keyにAS_POLICY_KEY_SENDを指定、JavaクライアントではPolicyクラスのsendKeyをtrueにすることでkeyという名前でbinを新たに作成し、その値に元のキーを入れるようです。

  • http://www.aerospike.com/apidocs/java/com/aerospike/client/policy/Policy.html#sendKey
  • http://www.aerospike.com/apidocs/c/db/d65/group__client__policies.html

アップデートが継続的にされているクライアントには概ねこの機能があるのを見かけた(ソースをgrepしただけなんですが…)んですが、どうやらErlangとPerlのクライアントではこの機能が利用できないようでした…。

2. バックアップをスナップショットで取ることはできない

Aerospikeでバックアップにはasbackupコマンドを利用します。asbackupコマンドは、バックアップクライアントで実行すると各ノードにmasterデータの取得リクエストが送られます。リクエストを受けた各ノードでは、それらのデータをクライアントに転送しますが、仕組み上データの量に比例してレスポンスが完了するのに時間がかかってしまいます。そのため、サービスを停止せずにバックアップを行うとバックアップ開始時点とバックアップ終了時点で全体でデータの差が生じ、クラスタ全体でのデータの一貫性は崩れてしまいます。

RDBMSでは当たり前のようにMVCC(MultiVersion Concurrency Control)の構造が取られており、サービスを停止せずにバックアップを行っても全体のデータの一貫性が崩れることはありません。そのため、当たり前にできると思ってしまいがちである(かもしれません)が、バックアップの際には注意が必要です。

3. データ個数に比例して再起動に時間がかかる

Aerospikeでは特定のデータにアクセスする際に、SSDなどのストレージ上のどこに目的のデータが保存されているかを示すPrimary Indexを利用します。Primary Indexはメモリ上に作られ、新たにデータが作成されるたびに増加していきます。

ノードでAerospikeプロセスを起動する際、SSDなどのストレージにデータが存在している場合は、そのデータを読み込みPrimary Indexをメモリ上に作成します。Primary Indexの作成が終了するまでは、そのノードでサービスを開始することはできません。そのため、非常に多くのデータがある場合はPrimary Indexの作成に時間がかかり、すぐに起動することができません。このようなPrimary Indexを作り直しながら起動することをcold startと呼んでいます。

有償版であるEnterprise Editionでは、この問題を解決するためにFast Restartという機能が備わっています。このFast Restartは、Primary Indexをshared memoryに作成することでAerospikeプロセスを起動し直した場合でもPrimary Indexの再作成を省略することができる機能です。しかしOSの再起動などを伴ったAerospikeプロセスの再起動の場合はcold startになり、Primary Indexの再作成が必要になります。

4. 削除したデータが削除できていないように見えることがある

Aerospikeでデータの削除は該当データのPrimary Indexを削除することで実現しています。実際にSSDなどの永続化された内容が削除されるのはEvictionと呼ばれる処理が行われた段階であり、少量のデータを扱っており容量にかなりの余裕がある場合にはこの処理が開始されません。そのため、例えば削除したデータがあるノードでAerospikeプロセスをcold startした場合、残っているデータからPrimary Indexを再度作成してしまい、データを再び読むことができるようになってしまいます。

このような現象が発生するのは条件があります。有効期限を非常に長く、もしくは有効期限を無しとしてデータを扱った時です。有効期限はrecordに保存されているためcold start時に有効期限を確認し、有効期限が切れていたデータであった場合にはPrimary Indexを作成しません。

これを回避するためには削除時にクライアント側で非常に短い有効期限を設定し、その後に削除のリクエストを行うようにする必要があります。

5. ホットキーが存在する場合は特定ノードに負荷が集中する

前述の通り、Aerospikeでは読み書きの際にmasterのノードしか利用されません。Aerospikeのクラスタではデータの偏りが発生しないようにし考慮されていますが、リクエストが大量に集中するようなホットキーがある場合は、ホットキーのmasterとなるノードに負荷が集中します。

読み込みの際にreplicaからも読み込みを行うような機能が将来的に実装されれば、この問題は多少改善されるかもしれませんが、現状ではホットキーが発生しないように設計を行う必要があるでしょう。

まとめ

今日は私がAerospikeを見てきた中で「ここは利用前に抑えておいたほうが良い」と思ういうのを5点厳選して紹介しました。

Aerospikeは素晴らしい分散KVSです。非常に高速に処理をすることができるだけではなく、今日ここで紹介しきれていない機能がたくさんあります。しかし、どのようなミドルウェアでも世の中すべての要求のすべてを満たすことができるものはなく今の要求に合っているのか、仕様として妥協できるのかというのを見極めて利用していくべきであると思います。

この記事がこれからAerospikeを利用しようと考えている方々が今の目の前にある要求に合っているかどうかを判断する際の助けになれば幸いです。

参考リンク

  • http://www.aerospike.com/docs/architecture/data-model.html
  • http://www.aerospike.com/docs/architecture/primary-index.html
このエントリーをはてなブックマークに追加