初めまして。nex8でダイナミックリタゲの配信システムを開発しているyu_ishikawaです。
nex8では、MySQL, MariaDB, MongoDB, Solr, Redis, DynamoDBやTreasureDataなど、様々なデータベースを用途に応じて使い分けていますが、大規模な機能改善に伴い、高いパフォーマンスが要求されるデータの保管先として新たにAerospikeを導入しました。
そこで今回は、nex8でAerospikeを導入してみて学んだAerospikeの特徴や注意点、nex8での使用例などをご紹介したいと思います。
Aerospikeとは?
Aerospikeとは、米Aerospike社によって開発・販売されているNoSQLデータベースです。 主な特徴として、次の点が挙げられます。
- とにかく高速な分散KVS
- 基本的にデータはSSD、インデックスはメモリに持つ
- スケールアップ・スケールアウト両方に対応
- レプリケーションファクター(RF)・バックアップで可用性を確保可能
- GET/SETは驚異的な速度を発揮するが、KVSなのでSCANやQUERYは得意ではない
- オープンソースのCommunity Editionと有償のEnterprise Editionがある
非常に高いパフォーマンスが求められるインターネット広告のシステムと親和性が高く、 米AppNexus社を始めとする多くのアドテク企業で採用されているようです。
とにかく高速
Aerospikeの最大の魅力は、データを永続化しながらもRAMに迫るほどの高パフォーマンスを発揮する点にあります。 平均1ミリ秒以下のレイテンシで読込み/書込みが可能で、スループットは超高性能SSDを使用した場合では、なんと1サーバーで100万TPSにも達するようです。
NoSQL界でのポジションとしてはCassandraが近く、公式ブログではよくCassandraとベンチ比較をしてボコボコにしています高速性をアピールしています。
Aerospike社スタッフの話によると、速さの理由は 「SSDに最適化されているから」 「後出なので他データベースのいいとこ取りをしている」 からとのこと。オープンソース化されているので、実装が気になる方は、GithubでCommunity Editionのソースコードを読むことができます。
データはSSD保存が基本
AerospikeはSSDに最適化されたデータベースであり、基本的にはSSDにデータを保存します。 SSDの代わりにHDDを使うこともできますが、AerospikeのパフォーマンスはSSDの性能に大きく依存するので、あえてHDDにするメリットは殆どありません。
また、より速度が必要な場合は、AerospikeをRedisのようにPure In Memoryで動かすという事も実は可能です。 しかし、当然ながら永続性は失われますし、それをやるなら別にAerospikeじゃなくてもいいかな〜と自分は思っています。
対応言語
現在、Aerospike社は下記言語のクライントを提供しています。
- C / C++
- Java
- Go
- C# / .NET
- Node.js
- C libevent
- PHP
- Erlang
- Python
- Ruby
- Perl
あなたの使っている言語はありましたでしょうか?私はなかったです…
何故かアドテク業界でホットな言語であるScalaに対応していないので、nex8ではJava版クライアントを包む簡易的なScala Wrapperを自前で開発して利用しています。
クライアントは独自プロトコルで実装されている上に、内部でコネクションプーリングをしたりキーから保存先ノードを決定して直接やりとりするなど、かなり複雑な仕様になっています。 ですので自前で実装するのはとても難しく、公式のクライアントが利用できない場合、残念ながら実質利用不可能(に限りなく近い)と言えます。
データ階層
AerospikeはNamespace
, Set
, Record
, Bin
の階層構造になっています。
これはMySQLにおけるDatabase
, Table
, Row
, Column
に似ています。
Namespace
MySQLのDatabaseのようなもの。 Replication Factorや、Storage Engine、ファイルやメモリの最大容量などデータベースの設定はNamespace毎に定義することができます。
通常はサービス名で分けたり、データの保存先(SSD/RAM)で分けたりするようですが、nex8ではデータ毎に最適なサーバスペックでクラスタを構築して、Namespaceは全て1つで運用しています(この方がお財布に優しい)。
Namespaceの追加にはクラスタの再起動が必要ですので、稼働前に使用するNamespaceを全て作成しておくことをおすすめします。
Set
MySQLのTableに似ていますが、AerospikeはKVSなのでスキーマはありません。 SetはNamespace毎に1,023個まで作成することができます。 日毎にSetを作成していると、3年以内にSetが作成できなくなって死ぬので注意しましょう。
Record / Bin
Recordはkey, expiration, generation, binsによって構成されています。
key
ユニークなID。基本的にRecordはKey(のハッシュ値)を使ってアクセスされます。
expiration(TTL = Time to live)
Recordの有効期限。 秒単位で指定することができ、有効期限を超過したRecordはAerospikeによって自動的に削除されます。 有効期限を設定したくない場合は-1、Namespaceのデフォルト値を使いたい場合は0を指定します。
generation
Recordを更新する度に自動的にインクリメントされていく値です。
nex8ではデータの性質上ほとんど使用しておりませんが、GenerationPolicy
を使用することで、Record更新に「generationがNなら」「generationがNより大きい場合のみ」などの条件を設定することができるようです。
bins
Binとは名前と値の組の事で、Recordは必ずBinを1個以上持っています。 Binの値には文字列・バイト配列・整数・浮動小数点数・リスト・マップ等のデータ型を使用することができます。 あとは分析用なのか、GeoJSON(緯度・経度)型も実装されています。 論理型は対応しているクライアントもありますが、クライアント側で整数(1/0)に変換しているだけで、実質的には整数です。
以下は、AQLというツールを使用して1時間で消えるRecordを設定・取得する例です。
aql> SET OUTPUT JSON aql> SET RECORD_PRINT_METADATA true aql> SET RECORD_TTL 3600 aql> INSERT INTO demo.books (PK, name, price) VALUES (4798139017, '楽しいR', 2200) OK, 1 record affected. aql> SELECT * FROM demo.books WHERE PK = 4798139017 [ { "digest": "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "ttl": 3571, "gen": 1, "bins": { "name": "楽しいR", "price": 2200 } } ]
利用する上での注意点
Binの名前は32K種類までしか使うことができない
1つのNamespaceで使用できるBinの名前は32K種類までに制限されており、上限を超えるとエラーが発生します。 一度使用した名前はRecordを削除しても残り続けるので、タイムスタンプやユーザのID等、パターンの多い文字列をBinの名前に使用するのはやめたほうが良いでしょう。
1Record最大1MBまで
Configurationにwrite-block-sizeという項目があり、ここに設定したデータサイズがRecordの最大データサイズとなります。write-block-sizeはデフォルトで最大値の1MBに設定されており、更に上げることはできません。 もし巨大なデータを保存したい場合は、Keyを分割して複数Recordに保存する等、工夫をする必要があります。
Large Data Type (LDT)は使わない方が良い
AerospikeにはLarge Data Type (LDT) という巨大コレクション用のデータ型があり、LargeList,LargeSet,LargeMapの3つの型が提供されています。 これを使うと1MBを超えるコレクションデータを1つのRecordに入れる事ができるのですが、この型を使うとAerospikeの魅力であるパフォーマンスが犠牲となります。
ライブラリには残っているので利用は可能ですが、既にLargeSet, LargeMapは公式ドキュメントから削除されていますし、Aerospike社のスタッフも推奨していないようなので、できる限り使わないほうが良いでしょう。
Setは消せない
Recordの全削除は可能ですが、Set自体を削除するコマンドは存在しません。 あなたがスペルミスをして作ってしまった変な名前のSetはずっと残り続けます。 どうしても消したい場合は、全データを破棄するしかありません。
クライアントはなるべく使いまわす
Aerospikeのクライアントは生成時にクラスタの情報を受け取り、ノードの変更を追跡しています。 リクエストやコマンド実行毎にクライアントを再生成していると、毎回情報を受取る分、オーバーヘッドが発生してしまいます。 言語によっては仕方ない部分もありますが、できるだけ一度生成したクライアントを使いまわす書き方を心がけると良いでしょう。
ネットに情報が少ない
残念ながら、Aerospikeの公式ドキュメントは全ての仕様を網羅しているわけではありませんし、コミュニティも盛り上がっていないのが現状です。
障害が発生したとき、Enterprise Edition
を利用していないとサポートも受けられないため、原因や対策が分からず途方にくれてしまう可能性があります。
nex8での使用例
大人の事情で具体的にどう使っているか説明することはできませんが、nex8では以下のような5つのクラスタを運用しています。
用途 | ノード数 | オブジェクト数 | トランザクション | RF |
---|---|---|---|---|
多目的 | 8 nodes | 0.1 billion objects | 20K TP | 2 |
広告主関連データ | 13 nodes | 0.2 billion objects | 600K TPS | 2 |
配信データ1 | 48 nodes | 1.7 billion objects | 500K TPS | 2 |
配信データ2 (新設) | 48 nodes | 0.1 billion objects | 500K TPS | 2 |
重要データ | 5 nodes | 6K objects | 12K TPS | 5 |
全てまとめて1つのクラスタで運用することも不可能ではないですが、キー数・データサイズ・TPS等に応じてノードのスペックを設計することでインフラコストを削減できるため、本格的にAerospikeを使うのであれば、格納するデータ毎にクラスタを用意するのがおすすめです。
ちなみに、メモリはKey数(* 64 bytes)・SSD容量はデータサイズ・CPUスペックはTPSから計算することができます。
最後に
Aerospikeはとても複雑で罠が多いデータベースです。
High Water Mark
, Hot Key
, UDF
, Secondary Index
など、ここでは紹介していませんが、覚えておくべき仕様や注意点が沢山あります。
もし、本格的にAerospikeを利用されるのであれば、まずは有償のトレーニング(日本語でも受講可能)を受けることをおすすめします。