設計文書の管理

IBM Cloudant のスケーラブルな JSON データ・ストアには複数の照会メカニズムがあります。どのメカニズムでも索引が生成されますが、索引はコア・データとは別個に作成および維持されます。

IBM Cloudant の Developer Advocate である Glynn Bird (glynn@cloudant.com) によって寄稿された記事。

索引の作成は、文書が保存されたときに即時に実行されるわけではありません。 そうではなく、 後で実行するように索引の作成をスケジュールすることで、 より高速で、ブロッキングしない書き込みスループットを実現しています。

  • MapReduce ビューは、キーまたはキー範囲による効率的な 検索のために B ツリー内に保管されるキー/値ペアを持つデータ・セットの索引です。
  • 検索索引は、フリー・テキスト検索、ファセット化、および複雑な随時照会を可能にするために、Apache Lucene を使用して作成されます。

IBM® Cloudant® for IBM Cloud® の検索索引MapReduce ビューは、 設計文書をデータベースに追加して構成されます。 設計文書は、ビューまたは索引の作成方法に関する指示を含む JSON 文書です。 単純な例を見てみましょう。 以下の例のような、単純なデータ文書コレクションが あると仮定します。

単純なデータ文書の例を次に示します。

{
    "_id": "23966717-5A6F-E581-AF79-BB55D6BBB613",
    "_rev": "1-96daf2e7c7c0c277d0a63c49b57919bc",
    "doc_name": "Markdown Reference",
    "body": "Lorem Ipsum",
    "ts": 1422358827
}

各データ文書には、名前、本文、およびタイム・スタンプが含まれています。 MapReduce ビューを作成すると、タイム・スタンプで文書をソートできます。

マップ関数を作成すると、タイム・スタンプで文書をソートできます。

文書のタイム・スタンプ・フィールド (ある場合) を返すマップ関数の例を次に示します。

function(doc) {
    if (doc.ts) {
        emit( doc.ts, null);
    }
}

この関数は、索引のキーとして使用できるように文書のタイム・スタンプを排出します。 インデックスの値には興味がないので、 null が発行されます。 この結果として、時刻で順序付けされた索引が文書セットに提供されます。

このビューを by_ts と呼び、 fetch というデザイン・ドキュメントに入れます。

マップ関数を使用してビューを定義する設計文書の例を次に示します。

{
    "_id": "_design/fetch",
    "views": {
      "by_ts": {
        "map": "function(doc) {
          if (doc.ts) {
            emit( doc.ts, null);
          }
        }"
      }
    },
    "language": "javascript"
}

この結果として、マップ・コードは JSON 準拠のストリングに変換され、設計文書に組み込まれます。

設計文書が保存されると、次のようになります。 IBM Cloudant は、fetch/by_tsビューを作成するためにサーバー・サイド・プロセスをトリガーします。 このビューの作成は、データベース内のすべての文書に対して反復処理を行い、それぞれを JavaScript のマップ関数に送信することによって行われます。 この関数は、排出された key-value ペアを返します。 反復が継続されるにつれて、各 key-value ペアが B ツリー索引に保管されます。 初めて索引が作成された後、後続の再索引付けは、新規文書および更新された文書に対してのみ行われます。 削除された文書は、索引付けから除外されます。 以下の図に示すように、この時間節約プロセスは「増分 MapReduce」と呼ばれます。

1000 個の文書を持つデータベースが作成されます。 デザイン・ドキュメントが追加され、1つまたは複数のビューが構築されます。 ビューは完成するまで非同期に構築される。 さらに 250 個の文書が到着すると、索引は再度不完全になります。 ビューは、バックグラウンドで、または照会時に自動的に作成されます。
インクリメンタルの図解 MapReduce

以下の事項を覚えておくと有益です。

  • 索引の作成は非同期で行われる。 IBM Cloudant は、設計文書が保存されたことを確認する。 索引作成の進行状況を確認するには、IBM Cloudant の _active_tasks エンドポイントをポーリングする必要があります。
  • データ量が多いほど、索引の準備ができるまでにかかる時間は長くなる。
  • 初回の索引作成の進行中は、索引に対して行われた照会はすべてブロックされる。
  • ビューを照会すると、増分で索引付けされていない文書の「マッピング」がトリガーされる。 この方法により、データの最新ビューを確実に取得できます。 この規則の例外については、以下の stale パラメーターの 説明を参照してください。

同じ設計文書内の複数のビュー

同じ設計文書に複数のビューを定義すると、 それらは効率的に同時に作成されます。 各文書の読み取りは 1 回のみ行われ、各ビューのマップ関数を通じて渡されます。 この方法を使用する場合は、設計文書を変更すると、その文書で定義されているすべての既存の MapReduce ビューが無効化されることに注意してください。 このプロセスでは、未変更のビューが存在している場合でも、MapReduce ビューが無効化されます。

MapReduce ビューを互いに独立して変更する必要がある場合は、それらの定義を別々の設計文書に入れます。

この動作は、Lucene 検索索引には適用されません。 それらの索引は、同じ設計文書内の他の未変更の索引を無効化せずに、同じ文書内で変更できます。

1000 個の文書を持つデータベースが作成されます。 2 つのビューと 2 つの検索索引の作成をトリガーする設計文書が追加されます。 ビューは完成するまで非同期に構築される。 設計文書の 2 番目のバージョンが到着すると、文書内のすべての MapReduce ビューおよび変更された検索索引が無効になります。
設計文書のバージョン変更

設計文書に対する変更の管理

将来のある時点でビューの設計の変更を決断すると想像してください。 その時、 実際のタイム・スタンプ結果を返す代わりに、関心があるのは、 何個の文書が基準に一致するかというカウントだけです。 このカウントを作成するには、マップ関数は同じままで、 reduce_count を使用します。

reduce 関数を使用する設計文書の例を次に示します。

{
    "_id": "_design/fetch",
    "_rev": "2-a2324c9e74a76d2a16179c56f5315dba",
    "views": {
        "by_ts": {
            "map": "function(doc) {
                if (doc.ts) {
                  emit( doc.ts, null);
                }
            }
        }",
        "reduce": "_count"
    },
    "language": "javascript"
}

この設計文書が保存されると、以下のようになります。 IBM Cloudant は、古い索引を完全に無効にし、新しい索引の作成を最初から開始して、すべての文書を順に繰り返します。 元の作成と同様に、かかる時間はデータベースに含まれている文書の数によって決まります。 また、作成が完了するまでは、そのビューでの着信照会はブロックされます。

ただし、問題があります。

このビューにリアルタイムでアクセスしているアプリケーションがある場合、次のようなデプロイメント・ジレンマに遭遇する可能性があります。

  • 古いビューが無効化されているため、元の設計文書に依存しているバージョン 1 のコードはもう機能しない可能性があります。
  • バージョン 2 のコードは新しい設計文書を使用します。 新しいビューの作成がまだ完了していないため、このバージョンをすぐにリリースすることはできません。 データベースに含まれている文書が多い場合は作成処理に時間がかかります。
  • コードに影響を与えるより微妙な問題は、バージョン 1 と 2 ではビューとは異なる結果データが予期されることです。 バージョン 1 では一致する文書のリストが予期されていますが、バージョン 2 では結果の数が「減少」することが予期されています。

設計文書に対する変更の調整

この変更管理の問題を扱う方法は 2 つあります。

バージョン管理された設計文書

1 つの解決策は、以下のようにバージョン管理された設計文書名を使用することです。

  • コードは、最初、_design/fetchv1 という名前のビューを使用するように作成されます。
  • 新しいバージョンをリリースするときに、_design/fetchv2 という名前の新しいビューを作成し、そのビューを照会して確実に作成されるようにします。
  • IBM Cloudant は、新しい索引の作成処理が完了するまで _active_tasks をポーリングします。
  • これで、2 番目のビューに依存するコードをリリースする準備ができました。
  • もう必要ないことを確認したら _design/fetchv1 を削除します。

バージョン管理された設計文書を使用すると、設計文書での変更管理が簡潔ですが、後で古いバージョンを必ず削除してください。

Move and switch設計文書

もう 1 つの方法は、IBM Cloudant は同じ設計文書が 2 つある場合にそれを認識し、既にあるビューの再作成に時間とリソースを浪費しないという事実に依存します。 つまり、設計文書 _design/fetch を使用し、完全な重複である _design/fetch_OLD を作成すると、再索引付けはトリガーされずに、両方のエンドポイントが交換可能な状態で機能します。

新しいビューに切り替えるには、以下のステップに従います。

  1. 変更する設計文書の複製コピーを作成します。例えば、名前に_OLDを追加します。 _design/fetch_OLD.
  2. _NEW という接尾部を持つ名前 (_design/fetch_NEW) を使用して、新規または「後任」の設計文書をデータベースに入れます。
  3. fetch_NEW ビューを照会して、 作成が開始されることを確認します。
  4. _active_tasks エンドポイントをポーリングし、索引の作成が終了するまで待ちます。
  5. 新しい設計文書の重複コピーを _design/fetch に入れます。
  6. 設計文書 _design/fetch_NEW を削除します。
  7. 設計文書 _design/fetch_OLD を削除します。

Move and switch ツール

Node.js のコマンド・ライン・スクリプト couchmigrate は、「Move and switch」の手順を自動化します。 次のコマンドを使用してこれをインストールできます。

npm install -g couchmigrate

couchmigrate 、 まず、 COUCH_URL という環境変数を設定して、 CouchDB/{{site. data.keyword.cloudant_short_notm }} インスタンスの URL を定義します。 IBM Cloudant インスタンスの URL を定義するには、次のコマンドを実行します。

export COUCH_URL=https://127.0.0.1:5984

URL は https:// で始まらなければならず、認証情報を含めることができる。 認証資格情報が含まれた IBM Cloudant インスタンスの URL を定義するには、次のコマンドを実行します。

export COUCH_URL="https://$ACCOUNT:$PASSWORD@$HOST.cloudant.com"

ファイルに保管されている、JSON フォーマットの設計文書があると仮定した場合、マイグレーション・コマンドを実行できます。

この例では、次のようになっています。 db は変更するデータベース名を指定します、 には変更するデータベースの名前を指定し、 dd にはデザイン・ドキュメント・ファイルへのパスを指定します。 couchmigrate コマンドを実行します。

couchmigrate --db mydb --dd /path/to/my/dd.json

このスクリプトは、「Move and switch」手順を調整し、ビューが作成されるまで待ってから戻ります。 後任の設計文書が、現在の文書と同じ場合、スクリプトはほぼすぐに戻ります。

スクリプトのソース・コードは、以下の場所にあります。 couchmigrate.

stale」パラメーター

索引は完了したが、新しいレコードがデータベースに追加された場合、索引はバックグラウンドで更新するようスケジュールされます。 データベースの状態は以下の図のようになります。

ビューが完了しました。 さらに 250 個の文書が到着すると、索引は再度不完全になります。
インデックス更新予定

ビューを照会するときには、次の選択肢があります。

  • デフォルト動作は、応答を返す前に、索引が最新であり、データベース内に最新文書が含まれていることを確認します。 ビューを照会すると、以下のようになります。 IBM Cloudant は、最初に 250 個の新規文書に索引を付けてから、応答を返します。
  • 代替方法は、API 呼び出しに stale=ok パラメーターを追加することです。 このパラメーターの意味は、 return me the data that is already indexed. I don't care about the latest updates. つまり を使用してビューにクエリを実行すると、 stale=ok、 IBM Cloudant は、追加の再索引付けを行わずに、即時に応答を返します。
  • 2 番目の代替方法は、stale=update_after パラメーターを API 呼び出しに追加するというものです。 このパラメーターの意味は、 return me the data that is already indexed, and then reindex any new documents. つまり を使用してビューにクエリを実行すると、 stale=update_after、 IBM Cloudant は即時に応答を返し、新規データに索引を付けるためのバックグラウンド・タスクをスケジュールします。

stale=ok または stale=update_after を追加することは、ビューからより速く応答を得るための良い方法ではありますが、最新性は保証されなくなります。

デフォルト動作は、IBM Cloudant クラスター内のノードにロードを均等に分散します。 代替方法の stale=ok または stale=update_after オプションを使用すると、結果整合性セット全体から整合する結果を返すために、クラスター・ノードのいずれかのサブセットが優遇される可能性があります。 stale パラメーターは、すべてのユース・ケースに最適な解決策ではないということです。 ただし、失効した結果をアプリケーションが問題なく許容できるのであれば、変更が速いデータ・セットでタイムリーな応答を提供できます。 データの変更率が低い場合は、stale=ok または stale=update_after を追加してもパフォーマンス上のメリットはなく、大規模なクラスターではロードが不均等に分散される可能性があります。

デフォルトの動作では最新のデータが提供されてクラスター内でデータが分散されるので、できる限り stale=ok または stale=update_after は避けてください。 大規模なデータ処理タスク (例えば、定期的なデータの一括更新など) が進行中のときには、一時的に stale=ok に切り替えることで、クライアント・アプリに対応させることができます。 アプリは後でデフォルト動作に戻ることができます。

stale オプションはまだ使用可能ですが、より有用なオプションである stable および update が使用可能なため、それらを代わりに使用する必要があります。 詳しくは、失効したビューへのアクセスを参照してください。