Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【R&D DevOps通信】DocumentDBを導入した話

こんにちは、R&D Architectグループの辻田です。 とあるシステムのデータストアにAmazon DocumentDBを採用したので、その経緯やDocumentDBの特徴について紹介したいと思います。

DocumentDBを選定した経緯

今回保存したかったデータには以下のような特徴がありました。

  • 主要となるテーブルは1つ
  • 概念モデル間の依存は無し
  • データ数は数十万ほど(今後増える可能性あり)
  • Readの頻度:不定期でバッチで参照(今後サービスからリアルタイムで参照する可能性あり)
  • Writeの頻度:不定期でバッチで書き込み

上記のデータの特徴に加えて、以下のシステム要件がありました。

  • ページングが必要
  • さまざまな検索条件での検索が必要

これらを踏まえていくつかマネージドなデータベースサービス(Aurora, DynamoDB, DocumentDB)を比較検討した結果、最もデータの特徴にマッチしていて、システム要件を満たせるDocumentDBを採用することにしました。

DocumentDBの概要

aws.amazon.com

大規模な JSON データ管理のためのデータベースサービスで、AWS で完全に管理・統合されており、高い耐久性を備えたエンタープライズ対応のサービス

で、MongoDB3.6または4.0 のドライバーやツールと互換性があります。

MongoDBの特徴としては以下のような特徴があげられます。

  • NoSQL
  • ドキュメント指向
    • JSON形式のデータ構成
    • 内部的にはBSON(バイナリ形式のJSON)で保存
  • スキーマレス
    • スキーマ定義なしでデータを保存できる
    • スキーマ定義することもできる(MongoDB3.6以降)
  • シャーディング, レプリケーション
    • 高い性能、スケーラビリティ、可用性を実現

詳細についてはドキュメントを参照ください。

www.mongodb.com

一つ注意点として、MongoDB3.6, 4.0でサポートされている機能が全てDocumentDBでもサポートされているわけではないという点があります。こちらについては後述します。

DocumentDBの良かった点

DocumentDBを使ってみて良かったと思う点を紹介します。

柔軟な検索クエリ

まず1点目が柔軟にクエリが書ける点です。わたしはNoSQLだとDynamoDBの経験くらいしかなかったため、indexに依存せずさまざまな条件で検索ができることはとても素晴らしい点だと感じました。
例えば以下のようにskip(MySQLでいうoffset), limitメソッドを使用してページングを行うことも可能です。

db.collection.find().skip(0).limit(2)

以下はPythonで、ドライバーにpymongoを使用した実装例です。
この例では指定したフィールドのみ、nameが一致、ページング指定、nameの降順で検索を行うことができます。

results = db.collection.find(
    projection={"_id": False, "name": True, "email": True, "address": True},
    filter={"name": "hoge"},
    skip=0,
    limit=2,
    sort=[("name", DESCENDING)],
)

シンプルな検索クエリはだいたい書ける印象です。詳細について以下を参照ください。

サポートされている MongoDB API、オペレーション、およびデータ型 - Amazon DocumentDB

インデックスのサポート

2点目が幅広いインデックスをサポートしている点です。ドキュメントのあらゆるフィールドやネストしたフィールドに対してもインデックスを貼ることができ、単一キーインデックス、複合キーインデックスのほかにマルチキー、地理空間(2dsphere)などのインデックスがサポートされています。

サポートされている MongoDB API、オペレーション、およびデータ型 - Amazon DocumentDB

扱うデータの特性や検索したい要件に合わせたインデックスを作成することで高速な読み取りが実現できそうです。例えば複合キーインデックスの場合、以下のような定義で作成することが可能です。

db.collection.createIndexes(
    [{ name: 1, email: 1 }],
    { unique: true, name: "unique_name_email"}
)

name, emailで指定している1は昇降順です。1が昇順のインデックス、-1が降順のインデックスとなります。
uniqueをtrueにすることでインデックスに一意性を持たせることができます。これによりインデックス対象のフィールドで重複した値を拒否できるようになります。

トランザクションのサポート

最後にトランザクションがサポートされている点です。MongoDB4.0互換から複数のドキュメント、ステートメント、コレクション、データベース間でACIDトランザクションがサポートされるようになりました。 以下はPython(pymongo)でのトランザクション処理の実装例です。

def insert_many_with_transaction(self, documents: list[dict[str, Any]]) -> None:
        with self._client.start_session() as session:
            with session.start_transaction():
                self._collection_with_options.insert_many(documents, session=session)

pymongoではsession.start_transaction()ブロックが正常に終了すると、トランザクションは自動的にClientSession.commit_transaction()を呼び出しコミットされます。ブロックが例外で終了した場合、トランザクションは自動的にClientSession.abort_transaction()を呼び出しロールバックします。

トランザクション設計のベストプラクティスとしては、実行するクエリ数を最小限に抑え、できるだけ小さな単位のトランザクションに分割することだそうです。

DocumentDBの注意点

最後にDocumentDBの注意点についてです。DocumentDBはMongoDB3.6または4.0互換をサポートしていますが、すべての機能がサポートされているわけではありません。

サポートされている MongoDB API、オペレーション、およびデータ型 - Amazon DocumentDB

例えばMongoDBでは評価クエリ演算子の$jsonSchemaを使用してスキーマ定義することができますが、DoumentDBではできません。わたしは選定当初$jsonSchemaでスキーマ定義をしてより堅牢にデータを管理しようとしていましたが、事前の確認不足で実装段階でできないことが判明してしまいました。。

他にもテキスト、ハッシュなどのインデックスや、トランザクションについてもいくつかサポートされていない機能があります。

導入する際は使いたいMongoDBの機能がDocumentDBでも使えるかはしっかり確認しておくと良いです。

まとめ

今回はじめてDocumentDBを触りましたが、フルマネージドで柔軟性と堅牢さを兼ね備えたとても良いデータベースだと思いました。
スキーマレスでさくっと使い始められ、さまざまな検索クエリが書けて、インデックスにより検索性能や一意性を担保でき、トランザクションでデータの一貫性を守ることもできます。(スキーマレスも魅力的ですが個人的には$jsonSchemaによるスキーマ定義がサポートされたらもっと嬉しいです。)

RDB, NoSQLでさまざまな選択肢がありデータベース選定はつい使い慣れたものを選びがちになるかもしれませんが、この記事がDocumentDBを選定するきっかけになれば幸いです。

© Sansan, Inc.