RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

MongoDBについて調査・検証しました

f:id:syoneshin:20211001110053p:plain こんにちは、株式会社ラクスで先行技術検証を行っている技術推進課の@t_okkanです。 技術推進課では、新サービス立ち上げ時の開発速度アップを目的に、現在ラクスでは採用されていない新しい技術の検証を行う、技術推進プロジェクトがあります。 今回はその技術推進プロジェクトで、ドキュメントDBであるMongoDBについて検証を行いましたので、その結果の報告を行います。

なお、別テーマの取り組みや、過去の取り組みに関しては、こちらからご覧ください。

tech-blog.rakus.co.jp

ドキュメントDB

ドキュメントDBとは、半構造データを管理するNoSQLなデータベースです。半構造データとは、ある程度データ構造が決まっているが構造が柔軟に変更できる、もしくは変更されるデータのことで、JSONXML形式で表されます。 ドキュメントDBの特徴としては以下のようなことが挙げられます。

  • スキーマレスで動的なデータを保存できる
  • 膨大なデータを高速に処理することができる
  • アプリケーションとデータベースのインピーダンスミスマッチを解消できる

リレーショナルデータベースとドキュメントDBなどの非リレーショナルデータベースを併用し、データの特性ごとに適材適所のデータベースを利用する考え方をポリグロット・パーシステンス(複数モデルによる永続化)と呼びます。 今回はドキュメントDBの製品であるMongoDBについて調査し、実際にポリグロット・パーシステンスを実装してみました。

MongoDB

MongoDB Incが開発とサポートをしているドキュメントDBです。データをBSONというJSONのバイナリ版の形式で保存することができます。CouchDBやRavenDBといった、同じドキュメントDBの中では最も人気のあるデータベースとなっています。 それではMongoDBの基礎的な機能などについて説明します。

トランザクション

MongoDBではバージョン4.0からマルチドキュメントACIDトランザクションをサポートしています。(レプリカセット構成でWidredTiger利用時)デーベース、コレクション、ドキュメント単位からトランザクションを開始できます。以下のようなトランザクションレベルが提供されています。

  • 書き込みレベル

    • w:1:プライマリにコミットされるとコミット完了とする
    • w:"majority":レプリカセットの過半数にコミットされると完了とする
  • 読み込みレベル

    • local:プライマリの最新データを取得する
    • majority:書き込みレベルがw:"majority"の場合、レプリカセットの過半数にコミットされているデータを取得する
    • snapshot:レプリカセットの過半数で反映されているデータから取得する

docs.mongodb.com

WiredTigerストレージエンジン

バージョン3.2からMongoDBのストレージエンジンのデフォルトがWiredTigerに変更されています。 WiredTigerはMVCCアーキテクチャを採用しており、ドキュメントの書き込みの同時実行が可能になります。 これまでのMMAPv1と比べ、メモリ制限や、データとインデックスの圧縮が可能になっています。 WiredTigerの詳しい動作の説明はこちらのスライドで詳しく説明されています。

www.slideshare.net

水平方向と垂直方向へのスケーリング

MongoDBではシャーディングとレプリケーションを組み合わせた、シャードレプリカを構築することで水平方向と垂直方向のスケーリングを行うことができます。これにより、性能と可用性の両面を向上させることができます。

gihyo.jp

Aggregation Pipline

1つのコレクション内の複数のドキュメントをグループ化し、複数の処理を実行するパイプラインを実装することができます。データのフィルターや集約、ソート、変換などが可能となります。SQLgroup bysum関数といった処理を再現することができます。

docs.mongodb.com

MongDBのスキーマ設計

MongoDBのドキュメントのスキーマを設計するには、ドキュメントに対するアクセスパターンとクエリを理解する必要があるとされています。WiredTigerのメモリに検索結果をキャッシュするため、必要のないデータをクエリするとメモリの無駄遣いになってしまいます。 そこでMongoDBでは、スキーマ設計パターンが提供されており、データの特性やアクセスパターンから最適な設計パターンを選択することが推奨されています。

www.mongodb.com

Polymorphic patterAttribute patternSubset patterの設計パターンが参考になるのではないかと思います。

PostgreSQLjson型、jsonb型との比較

PostgreSQLでは9.2からjson型が、9.4からJSON形式のデータをバイナリ解析して保存するjsonb型が提供されています。 そのため、PostgreSQLでも動的なカラムの増減に対応することができます。 以下、MongoDBとPostgreSQLjson型とjsonb型について比較しました。

比較項目 PostgreSQL json PostgreSQL jsob型 MongoDB
インデックスの設定 不可 可能 可能
トランザクション 対応 対応 対応
データ型 JSON型に準拠 一部のデータ型に制限あり BSON型に準拠
全文検索 対応 対応 日本語未対応

MongoDBはPostgreSQLJSON型と比べ、豊富なデータ型に対応しています。しかし、MongoDBは日本語での全文検索に対応していません。(他の言語には対応)

Javaアプリケーションでの実装例

今回は以下のようなマスターデータをリレーショナルDB(PostgreSQL)に、カスタム項目などのユーザーごとに動的に変更される定義情報をMongoDBに登録するポリグロット・パーシステンスなシステムを実装してみました。

f:id:ot14nano:20210930174752p:plain

MongoDBのJava Driverは以下のようにbuild.gradleに設定しています。

dependencies {
    implementation group: 'org.mongodb', name: 'mongodb-driver-sync', version: '4.2.3'
}
MongoDBとの接続

JavaアプリケーションからMongoDBへ接続するには、RDB同様にURLを利用して接続します。 レプリカ構成のMongoDBを利用している場合は、メンバーのホスト名とレプリカセット名を指定して接続します。

MongoClient mongoClient = MongoClients.create("mongodb://ユーザー名:パスワード@ホスト名");
// レプリカ構成の場合
MongoClient mongoClient = MongoClients.create("mongodb://ユーザー名:パスワード@プライマリホスト,セカンダリホスト,ターシャリホスト?replicaSet=レプリカ名");
定義情報のCRUD処理

JavaアプリケーションからのCRUD処理の一例を紹介します。前提条件としてユーザーの定義情報はMap<String, Object>型のデータとしてやりとりされる前提とします。

// 定義情報
Map<String, Object> property1 = new HashMap<>(){
    {
        put("設定1", "オプション1");
        put("設定2", 3);
        put("設定3", true);
    }    
};
Map<String, Object> property2 = new HashMap<>(){
    {
        put("設定1", 1);
        put("設定4", false);
        put("設定5", "オプション5");
    }    
};

// コレクションの取得
MongoCollection<Document> collection = mongoClient.getDatabase("データベース名").getCollection("コレクション名");

// ユーザーの定義項目の登録
Document registorData = new Document();
property1.entrySet().stream().forEach(e -> registorData.append(e.getKey(), e.getValue()));
registorData.append("userId", "user1");
collection.insertOne(registorData);

// ユーザーの定義項目の検索
Document query = new Document("userId", "user1");
Document result = collection.find(query).first();

// ユーザーの定義項目の更新(上書き + 追記)
Document query = new Document("userId", "user1");
Document updateData = new Document();
property2.entrySet().stream().forEach(e -> updateData.append(e.getKey(), e.getValue()));
collection.updateOne(query, new Document("$set", updateData));

// ユーザーの定義項目の削除
Document query = new Document("userId", "user1");
DeleteResult deleteResult = collection.deleteMany(query);

その他、CRUD処理については以下のチュートリアルで詳しい実装例が紹介されています。

mongodb.github.io

RDBとMongoDBのトランザクション

MongoDBでのトランザクションの実装は以下のようになります。

  • ClinetSessionstartTransactionメソッドでトランザクション開始
  • ClinetSessioncloseメソッドでトランザクションを終了
  • MongoDBの処理に失敗するとMongoCommandExceptionがthrowされるのでcatch節でClinetSessionabortTransactionメソッドを実行しロールバッ
// MongoCommandExceptionをロールバック対象の例外に設定
@Transactional(rollbackOn = MongoCommandException.class)
public void transactionA() throws MongoCommandException {
    // MongoDB Sessionの取得
    ClientSession session = mongoClient.startSession();
    try {
         // MongoDB トランザクションの開始
        session.startTransaction();
        // PostgreSQLの処理
        // MongoDBの処理 
    } catch (MongoCommandException e) {
        // ロールバック処理
        session.abortTransaction();
        throw e;
    } finally {
        // MongoDB トランザクションクローズ
        session.close();
    }
}

今回のようにマスターデータはRDBに設定情報はMongoDBに保存されている場合、マスターデータを変更した場合にMongoDBのデータも変更する可能性が出てきます。 上記の実装のように、PostgreSQLトランザクション処理の中にMongoDBのトランザクション処理を実装することで、どちらかのデータ保存に失敗した場合、両方のDBをロールバックすることができるようになります。

まとめ

MongoDBの機能の解説とJavaアプリケーションでのサンプルについて解説しました。 日本語での全文検索に対応していないものの、トランザクションへの対応など豊富な機能が提供されており、データ形式によってはMongoDBが最適解となることもあるのではないでしょうか。 もしMongoDBの導入を検討されている方がいましたら参考にしていただければと思います。


  • エンジニア中途採用サイト
    ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
    ご興味ありましたら是非ご確認をお願いします。
    20210916153018
    https://career-recruit.rakus.co.jp/career_engineer/

  • カジュアル面談お申込みフォーム
    どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
    以下フォームよりお申込みください。
    forms.gle

  • イベント情報
    会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.