クリアフィルター
記事
Toshihiko Minamoto · 2021年9月30日
## はじめに
Caché 2016.2のフィールドテストはかなり前から利用可能ですので、このバージョンで新しく追加されたドキュメントデータモデルという重要な機能に焦点を当てたいと思います。 このモデルは、オブジェクト、テーブル、および多次元配列など、データ処理をサポートするさまざまな方法として自然に追加されました。 プラットフォームがより柔軟になるため、さらに多くのユースケースに適したものになります。
いくつかのコンテキストから始めましょう。 NoSQLムーブメントの傘下にあるデータベースシステムを少なくとも1つは知っているかもしれません。 これにはかなりたくさんのデータベースがあり、いくつかのカテゴリにグループ化することができます。 Key/Valueは非常に単純なデータモデルです。 値をデータベースに格納し、それにキーを関連付けることができます。 値を取得する場合は、キーを介してそれにアクセスする必要があります。 適切なキーを選択によってソートが決まり、キーの一部であるものでグループ化する場合に単純な集計に使用できるようになるため、キーの選択が重要な鍵となります。 ただし、値は値にすぎません。 値内の特定のサブ要素にアクセスしたり、それらにインデックスを作成したりすることはできません。 値をさらに活用するには、アプリケーションロジックを書く必要があります。 Key/Valueは、大規模なデータセットと非常に単純な値を操作する必要がある場合に最適ですが、より複雑なレコードを扱う場合には価値が劣ります。
ドキュメントデータモデルはKey/Valueにとてもよく似ていますが、値はより複雑です。 値はキーに関連付けられたままになりますが、さらに、値のサブ要素にアクセスして特定の要素にインデックスを作成することができます。 つまり、いくつかのサブ要素が制限を満たす特定のドキュメントを検索することもできるということです。 明らかに、NoSQLの世界にはGraphのような他のモデルもさらに存在しますが、ここでは、ドキュメントに焦点を置くことにします。
###
## そもそもドキュメントとは?
一部の読者を混乱させる傾向があるため、まず最初に、1つ明確にしておきましょう。この記事で「ドキュメント」と言った場合、PDFファイルやWordドキュメントといった物理的なドキュメントを指してはいません。
この文脈でのドキュメントとは、サブ値を特定のパスと関連付けることのできる構造を指しています。 ドキュメントを記述できるシリアル化形式には、JSONやXMLなどのよく知られたものが様々あります。 通常こういった形式には、共通して次のような構造とデータ型があります。
1. 順序付けされていないKey/Valueペアの構造
2. 順序付けされた値のリスト
3. スカラー値
1つ目は、XMLの属性要素とJSONのオブジェクトにマッピングされます。 2つ目の構造は、XMLのサブ要素とJSONの配列を使ったリストによって導入されています。 3つ目は、単に、文字列、数値、ブール値といったネイティブのデータ型を利用できるようしています。
JSONのようなシリアル化された形式でドキュメントを視覚化することは一般的ですが、これは、ドキュメントを表現できる一方法にすぎないことに注意してください。 この記事では、JSONを主なシリアル化形式として使用することにします。JSONサポートの改善機能をうまく利用できるでしょう。この改善についてまだ読んでいない方は[こちら](https://community.intersystems.com/post/introducing-new-json-capabilities-cach%C3%A9-20161)をご覧ください。
ドキュメントはコレクションにグループ化されます。 セマンティックと潜在的に共通の構造を持つドキュメントは同じコレクションに保存する必要があります。 コレクションはその場で作成できるため、事前にスキーマ情報を用意しておく必要はありません。
コレクションにアクセスするには、データベースハンドルを最初に取得しておく必要があります。 データベースハンドルはサーバーへの接続として機能し、コレクションへの単純なアクセスを提供しますが、分散環境の場合にはさらに複雑なシナリオを処理することもできます。
###
## 基本
まず、Caché Object Scriptで単純なドキュメントを挿入する方法を見てみましょう。
USER>set db = ##class(%DataModel.Document.Database).$getDatabase()
USER>set superheroes = db.$getCollection("superheroes")
USER>set hero1 = {"name":"Superman","specialPower":"laser eyes"}
USER>set hero2 = {"name":"Hulk","specialPower":"super strong"}
USER>do superheroes.$insert(hero1)
USER>do superheroes.$insert(hero2)
USER>write superheroes.$size()
2
上記のコードサンプルでは、まずデータベースハンドルが取得されて、「superheroes」というコレクションが取得されます。 コレクションは明示的に作成されるため、事前に設定する必要はありません。 新しいコレクションにアクセスできるようになったら、ヒーローのSupermanとHulkを表す非常に単純なドキュメントを2つ作成します。 これらは$insert(<document>)への呼び出しでコレクションに保存され、コレクションサイズの最終チェックで、2つのドキュメントを報告します。これは、以前にコレクションが存在していなかったためです。
$insert()呼び出しは、成功すると、挿入されたドキュメントを返します。 このため、ドキュメントの操作を続行する場合に、自動的に割り当てられたIDを取得することができます。 また、チェーンメソッドも可能になります。
USER>set hero3 = {"name":"AntMan","specialPower":"can shrink and become super strong"}
USER>write superheroes.$insert(hero3).$getDocumentID()
3
このコードスニペットは、別のヒーローオブジェクトを作成し、superheroesコレクションに永続させます。 今回は、メソッド呼び出しの$getDocumentID()を$insert()呼び出しに連鎖させ、システムがこのドキュメントに割り当てたIDを取得します。 $insert()は必ず自動的にIDを割り当てます。 独自のIDを割り当てる必要がある場合は、$insertAt(<User-ID>,<document>)呼び出しを利用できます。
特定のIDでドキュメントを取得する場合は、コレクションに対して$get(<ID>)メソッドを呼び出すことができます。
USER>set antMan = superHeroes.$get(3)
USER>write antMan.$toJSON()
{"name":"AntMan","specialPower":"can shrink and become super strong"}
Supermanとそのhometownを表すドキュメントを更新するとしましょう。 この場合、$upsert(<ID>,<document>)呼び出しを使用して、既存のドキュメントを簡単に更新することができます。
USER>set hero1.hometown = "Metropolis"
USER>do superheroes.$upsert(1,hero1)
USER>write superheroes.$get(1).$toJSON()
{"name":"Superman","specialPower":"laser eyes","hometown":"Metropolis"}
$upsert()は、IDがまだほかに使用されていない場合にドキュメントを挿入するか、そうでない場合に既存のドキュメントを更新します。
もちろん、$toJSON()を呼び出すだけで、コレクションの全コンテンツをJSONにシリアル化することも可能です。
USER>write superheroes.$toJSON()
[
{"documentID":1,"documentVersion":4,"content":{"name":"Superman","specialPower":"laser eyes","hometown":"Metropolis"}},
{"documentID":2,"documentVersion":2,"content":{"name":"Hulk","specialPower":"super strong"}},
{"documentID":3,"documentVersion":3,"content":{"name":"AntMan","specialPower":"can shrink and become super strong"}}
]
コレクションがドキュメントの配列として表されていることがわかります。 各ドキュメントは、ドキュメントIDとドキュメントバージョンでラップされており、同時実行を適切に処理するために使用されます。 実際のドキュメントコンテンツは、ラッパーのプロパティコンテンツに格納されます。 これは、コレクションの完全なスナップショットを取得して移動できるようにするために必要な表現です。
また、これはモデルの非常に重要な側面をカバーしており、特殊プロパティを予約してドキュメントデータに挿入することはありません。 ドキュメントを適切に処理するためのエンジンが必要とする情報は、ドキュメントの外部に保存されます。 さらに、ドキュメントはオブジェクトまたは配列のいずれかです。 その他の多くのドキュメントストアは、オブジェクトを最上位の要素としてのみ許可しています。
コレクションでドキュメントを変更するためのAPI呼び出しには他にもたくさんあり、基本的な演算をいくつか見てきました。 ドキュメントの挿入と変更は楽しい作業ですが、実際にデータセットを分析したり、特定の制限を満たすドキュメントを取得したりすると、さらに興味深くなります。
##
## クエリ
すべてのデータモデルが有用とみなされるには、何らかのクエリ機能が必要です。 動的スキーマを使用してドキュメントをクエリできるようにするには、2つの潜在的な方法があります。
1. 動的なドキュメントの性質に対処できる独自のクエリ言語を設計して実装する
2. 定着している構造化されたクエリ言語にクエリを統合する
この記事の後の方で説明するいくつかの理由により、コレクションをSQLエンジンに公開することにしました。 SQLの知識を引き続き活用できるというメリットがあります。また、クエリ方言の別のフレーバーを作成しているところでもあります。 実際、SQL ANSI委員会は、JSONの標準拡張機能を提案しており、それに準拠しています。 まとめれば、これらの拡張機能には、JSON関数の2つのカテゴリが含まれています。
1. リレーションコンテンツからJSONコンテンツに公開するための関数セット
2. 動的JSONコンテンツをクエリするための関数セット
この記事の範囲では、2つ目のカテゴリである動的JSONコンテンツのクエリのみを取り上げ、結果をSQLで処理できるようにテーブルとして利用できるようにします。
動的コンテンツ(関連するスキーマのないコンテンツ)を公開し、事前に定義されたスキーマを使用してデータを操作するSQLで利用できるようにする魔法の関数は、JSON_TABLEです。 一般に、この関数は2つの引数を取ります。
1. JSONデータソース
2. 名前と型で列へのJSONパスのマッピングを指定する定義
骨に肉付けした例を見てみましょう。
SELECT name, power FROM JSON_TABLE(
'superheroes',
'$' COLUMNS(
name VARCHAR(100) PATH '$.name',
power VARCHAR(300) PATH '$.specialPower'
)
)
name power
--------- -------------------------------
Superman laser eyes
Hulk super strong
AntMan can shrink and become super strong
JSON_TABLE関数の最初の引数は、それが作成する仮想テーブルのソースを定義します。 この場合、コレクション「superheroes」をクエリします。 このコレクションのドキュメントごとに行が作られます。
2つ目の引数は、ドキュメントの特定の値をテーブルの列として公開することを忘れないでください。 この引数は2つ部分で構成されています。最初のステップとして、次の式のコンテキストを設定します。 ドル記号'$'には特別な意味があり、ドキュメントのルートを指しています。 それ以降のすべての式はこのコンテキストを基準としています。
後に続くのは、COLUMNS句で、カンマ区切りのCOLUMN式のリストです。 COLUMN式ごとに、仮想テーブルの列が作成されます。 「name」と「power」という2つの列をクエリで公開しています。 列「name」はVARCHAR(100)型で定義されていますが、列「power」は300文字に制限されています。 PATH式は、JPL(JSONパス言語)式を使用してドキュメントの特定の値を列に関連付けています。 キー「name」の値は列「name」に公開されますが、キー「specialPower」の値は列「power」にマッピングされます。 JPL式は非常に表現力が高く強力ですが、これについては別のトピックで説明することにします。 このサンプルで使用した式は非常に基本的な式です。
この構文が初めてであれば、理解するのに少し時間が掛かるかもしれませんが、 JSON_TABLE関数を自然に読み取ると理解しやすいでしょう。 例として、上記のクエリを使います。 ここで表現しているのは、基本的に次のことです。
コレクション「superheroes」をクエリし、各ドキュメントのルートに式のコンテキストを設定します。 次の2つの列を公開します。
1. 列「name」を型VARCHAR(100)で公開し、キー「name」の値を挿入します。
2. 列「power」を型VARCHAR(300)で公開し、キー「specialPower」の値を挿入します。
前に述べた通り、JPL式は複雑になりがちであるか、たくさんの列を公開したいだけの場合もあります。 そのため、型定義を参照できる標準への拡張を組み込みました。これは基本的に、事前定義済みのCOLUMNS句です。 このようにして、上記のCOLUMNS句を登録することができます。
do db.$createType("heropower",{"columns":[{"column":"name","type":"VARCHAR(100)","path":"$.name"},{"column":"power","type":"VARCHAR(300)","path":"$.specialPower"}]})
型情報を登録したら、%TYPE式を使って、JSON_TABLE関数でそれを参照することができます。
SELECT name, power FROM JSON_TABLE(
'superheroes',
'$' %TYPE 'heropower'
)
これは明らかに、SQLクエリにドキュメントの一貫したビューを提供し、クエリそのものを大幅に簡略化する上で役立ちます。
## 高度な内容
ここまで説明したことのほぼすべてについて補足することはたくさんありますが、ここでは最も重要なことに焦点を当てたいと思います。 最後のセクションを読みながら、JSON_TABLE関数を非常に強力なポイントとしている手掛かりに気づいたかもしれません。
1. 仮想テーブルを作成する
2. JSONのようなデータをソースデータとして消費できる
最初の項目はそれだけで重要なポイントです。コレクションを簡単にクエリして、別のJSON_TABLE呼び出しまたは正にテーブルと結合することができるからです。 コレクションをテーブルと結合できることは大きなメリットです。要件に応じて、データに完璧なデータモデルを選択できるのです。
型安全、整合性チェックが必要であるのに、モデルがあまり進化していませんか? リレーショナルを使いましょう。 他のソースのデータを処理してそのデータを消費する必要があり、モデルが必ず急速に変化するか、アプリケーションユーザーの影響を受ける可能性のあるモデルを保存することを検討していますか? ドキュメントデータモデルを選択しましょう。 モデルはSQLで一緒にまとめることができるので安心です。
JSON_TABLE関数の2つ目のメリットは、実のところ基盤のデータモデルとは無関係です。 これまでに、JSON_TABLEでコレクションのクエリを説明してきました。 最初の引数は任意の有効なJSON入力にすることもできます。 次の例を考察しましょう。
SELECT name, power FROM JSON_TABLE(
'[
{"name":"Thor","specialPower":"smashing hammer"},
{"name":"Aquaman","specialPower":"can breathe underwater"}
]',
'$' %TYPE 'heropowers'
)
name power
--------- -------------------------------
Thor smashing hammer
Aquaman can breathe underwater
入力は通常のJSON文字列で、オブジェクトの配列を表します。 構造がsuperheroesコレクションと一致するため、保存された型識別子「heropowers」を再利用することができます。
これにより、強力なユースケースが可能になります。 実際にメモリ内のJSONデータをディスクに永続させずにクエリすることができます。 REST呼び出しでJSONデータをリクエストし、クエリを実行してコレクションまたはテーブルを結合できます。 この機能を使用すると、Twitterタイムライン、GitHubリポジトリの統計、株式情報、または単に天気予報のフィードをクエリできます。 この機能は非常に便利であるため、これについては、後日、専用の記事で取り上げたいと思います。
##
## REST対応
ドキュメントデータモデルでは、初期状態でREST対応のインターフェースが備わっています。 すべてのCRUD(作成、読み取り、更新、削除)とクエリ機能はHTTP経由で利用できます。 完全なAPIについては説明しませんが、ネームスペース「USER」内の「superheroes」コレクションのすべてのドキュメントを取得するサンプルcURLを以下に示します。
curl -X GET -H "Accept: application/json" -H "Cache-Control: no-cache" http://localhost:57774/api/document/v1/user/superheroes
###
## 私のユースケースで使用できますか?
ドキュメントデータモデルは、InterSystemsのプラットフォームへの重要な追加機能です。 これは、オブジェクトとテーブルに続く、まったく新しいモデルです。 SQLとうまく統合できるため、既存のアプリケーションで簡単に利用することができます。 Caché 2016.1で導入された新しいJSON機能によって、CachéでのJSONの処理が楽しく簡単になります。
そうは言っても、これは新しいモデルです。 いつ、そしてなぜそれを使用するのかを理解する必要があります。 常に言っていることですが、特定のタスクにはそれに適したツールを選択してください。
このデータモデルは、動的データを処理する必要がある場合に優れています。 以下に、主な技術的メリットをまとめます。
* 柔軟性と使いやすさ
スキーマを予め定義する必要がないため、データの作業環境を素早くセットアップし、データ構造の変更に簡単に適応させることができます。
* スパース性
テーブルに300列があっても、各行が入力できるのはその内の15列であることを覚えていますか? これはスパースなデータセットであり、リレーショナルシステムではそれらを最適に処理にできません。 ドキュメントは設計上スパースであり、効率的に保存と処理を行えるようになっています。
* 階層
配列やオブジェクトなどの構造化型は、任意の深さでネストすることができます。 つまり、ドキュメント内で関連するデータを保存することができるため、そのレコードにアクセスする必要がある場合の読み取りのI/Oを潜在的に縮小することができます。 データは非正規化して保存できますが、リレーショナルモデルではデータは正規化して保存されます。
* 動的な型
特定のキーには、列のように固定されたデータ型がありません。 名前は、あるドキュメントでは文字列であっても、別のドキュメントでは複雑なオブジェクトである可能性があります。 単純なものは単純にしましょう。 複雑になることはありますが、そうなった場合はもう一度単純化しましょう。
上記の項目はそれぞれ重要であり、適切なユースケースには、少なくとも1つが必要ではありますが、すべての項目が一致することは珍しいことではありません。
モバイルアプリケーションのバックエンドを構築しているとしましょう。 クライアント(エンドユーザー)は自由に更新できるため、同時に複数のバージョンのインターフェースをサポートする必要があります。 WebServicesを使用するなど、契約によって開発すると、データインターフェースを素早く適応させる能力が低下する可能性があります(ただし、安定性が増す可能性はあります)。 ドキュメントデータモデルには柔軟性が備わっているため、スキーマを素早く進化させ、特定のレコード型の複数のバージョンを処理し、それでもクエリで相関させることができます。
###
## その他のリソース
この魅力的な新機能についてさらに詳しく知りたい方は、利用可能なフィールドテストバージョン2016.2または2016.3を入手してください。
『ドキュメントデータモデル学習パス』を必ず確認してください。
今年のグローバルサミットのセッションをお見逃しなく。 「データモデリングリソースガイド」には、関連するすべてのセッションが集められています。
最後に、開発者コミュニティに参加し、質問を投稿しましょう。また、フィードバックもお待ちしています。
記事
Tomohiro Iwamoto · 2020年5月7日
Cachéの優れた可用性とスケーリング機能の1つは、エンタープライズキャッシュプロトコル(ECP)です。 アプリケーション開発中に考慮することにより、ECPを使用した分散処理は、Cachéアプリケーションのスケールアウトアーキテクチャを可能にします。 アプリケーション処理は、アプリケーションを変更することなく、単一のアプリケーションサーバーから最大255台といった非常に高いレートにまで、アプリケーションサーバー処理能力を拡張できます。
ECPは、私が関与していたTrakCareのデプロイメントで長年広く使用されていました。 10年前は、主要ベンダーの1つが提供する「大きな」x86サーバーは、合計で8つのコアしか備えていなかったかもしれません。 大規模なデプロイメントの場合、ECPは、高価な大型コンピュータを使う単一のエンタープライズサーバーではなく、コモディティサーバーでの処理をスケールアウトする方法でした。 コア数の多いエンタープライズサーバーでさえ制限があったため、ECPはそれらのサーバーへのデプロイメントのスケーリングにも使用されました。
現在、ほとんどの新しいTrakCareのデプロイメントや主流ハードウェアへのアップグレードは、ECPでのスケーリングを必要としません。 現行の2ソケットx86プロダクションサーバーは、数十のコアと巨大なメモリを持つことができます。 最近のCachéバージョンでは、TrakCare(および他の多くのCachéアプリケーション)は、単一サーバーにおいて、CPUコア数とメモリの増設により、ユーザーとトランザクションの増加をサポートしうる、予測可能な線形スケーリングを備えています。 現場では、ほとんどの新しいデプロイメントが仮想化されています。その場合でも、VMは、必要に応じてホストサーバーのサイズまで拡張できます。 単一の物理ホストが提供できる以上のリソース要件である場合、ECPを使用してスケールアウトします。
ヒント: 管理とデプロイメントを簡略化するため、ECPをデプロイする前に単一サーバー内でスケーリングを実行します。
この投稿では、アーキテクチャの例とECPの基本的な仕組みを示し、ストレージに重点を置いてパフォーマンスの考慮事項を説明します。
ECPとアプリケーション開発の構成に関する特定の情報は、オンラインの「Caché分散データ管理ガイド」で得ることができ、このコミュニティには ECP学習トラックがあります。
ECPの他の主要な機能の1つは、アプリケーションの可用性の向上です。詳細については、 「Caché高可用性ガイド」のECPセクションを参照してください。
このシリーズの他の記事のリストはこちら
ECPアーキテクチャの基本
ECPのアーキテクチャと働きは、概念としては単純です。ECPは、複数のサーバーシステム間でデータ、ロック、実行可能コードを効率的に共有する方法を提供します。 アプリケーションサーバーから見たデータとコードは、リモートにあるデータサーバー に保存されますが、アプリケーションサーバーのローカルメモリにキャッシュされ、最小限のネットワークトラフィックでアクティブなデータへの効率的なアクセスを提供します。
データサーバーはディスク上の永続ストレージへのデータベースの読み取りと書き込みを管理しますが、複数のアプリケーションサーバーはアプリケーション処理のほとんどを実行するソリューションの主力です。
多層アーキテクチャ
ECPは多層アーキテクチャです。 処理層とそれらの役割を説明するにはさまざまな方法があります。以下は、WebブラウザベースのCachéアプリケーションを説明するときに役立つものであり、私の投稿で使用するモデルと用語です。 各層を分解するためのさまざまな方法があるかもしれませんが、ここでは私の方法を使ってみましょう。:)
たとえば、CachéServer Pages(CSP)を使用するブラウザベースのアプリケーションは、プレゼンテーション、アプリケーション処理、およびデータ管理機能が論理的に分離された多層アーキテクチャを使用します。さまざまな役割を持つ「 論理サーバー」が層を構成します。 論理サーバーを個別の物理ホストまたは仮想サーバーに配置する必要はありません。コスト効率と管理性のために、一部またはすべての論理サーバーを単一のホストまたはオペレーティングシステムインスタンスに配置することもできます。 デプロイメントがスケールアップすると、ECPを使用してサーバーを複数の物理ホストまたは仮想ホストに分割できるため、アプリケーションに変更を加えることなく、必要に応じて処理ワークロードを分散できます。
ホストシステムは、容量と可用性の要件に応じて、物理的なもの、または仮想化されたものである場合があります。 以下の層と論理サーバーがデプロイメントを構成します。
プレゼンテーション層: ブラウザベースのクライアントとアプリケーション層の間のゲートウェイとして機能するWebサーバーが含まれます。
アプリケーション層: これは、ECPアプリケーションサーバーが置かれる層です。 上記のように、これは、アプリケーションサーバーがデータサーバーから分離する必要がない論理モデルであり、通常、非常に大きなサイト以外で必要なものではありません。 この層には、レポートサーバーなど、特殊な処理のための他のサーバーも含まれます。
データ層: これは、データサーバーが配置されている層です。 データサーバーはトランザクション処理を実行し、Cachéデータベースに格納されているアプリケーションコードとデータのリポジトリです。 データサーバーは、永続ディスクストレージの読み取りと書き込みを行います。
論理アーキテクチャ
次の図は、3層アーキテクチャーとしてデプロイされたブラウザーベースのアプリケーションの論理図です。
アーキテクチャは一見複雑に見えるかもしれませんが、単一のサーバーにインストールされたCachéシステムと同じコンポーネントで構成されています。ただし、論理コンポーネントは、複数の物理サーバーまたは仮想サーバーにインストールされています。 サーバー間のすべての通信はTCP/IPを介して行われます。
論理ビューでのECPの働き
上の図は、上から順に、複数の負荷分散されたWebサーバーに安全に接続しているユーザーを示しています。 Webサーバーは、クライアントと任意の処理を実行するアプリケーション層(アプリケーションサーバー)の間でCSP Webページ要求を渡し、コンテンツを動的に作成し、完成したページをWebサーバー経由でクライアントに返します。
この3層モデルでは、アプリケーション処理はECPによって複数のアプリケーションサーバーに分散されています。 アプリケーションは、データ(アプリケーションデータベース)をアプリケーションサーバーに対してローカルであるかのようにシンプルに扱います。
アプリケーションサーバーがデータを要求すると、ローカルキャッシュからの要求を満たそうとしますが、それができない場合は、ECPが自身のキャッシュからの要求を満たすことができるデータサーバーに必要なデータを要求します。また、それができない場合は、ディスクからデータをフェッチします。 データサーバーからアプリケーションサーバーへの応答には、そのデータが格納されたデータベースブロックが含まれます。 これらのブロックが使用され、次にアプリケーションサーバーにキャッシュされます。 ECPは、ネットワーク全体のキャッシュの一貫性を自動的に管理し、変更をデータサーバーに反映します。 クライアントはローカルにキャッシュされたデータを頻繁に使用するため、迅速な応答を得ることができます。
データが既にローカルキャッシュにある可能性があるため、デフォルトでは、Webサーバーは優先アプリケーションサーバーと通信し、同じアプリケーションサーバーが関連データに対する後続の要求にサービスを提供することを保証します。
ヒント: Cachéのドキュメントで詳述されているように、アプリケーションサーバーでのキャッシュの利点に影響を与えるラウンドロビンや負荷分散スキームでユーザーをアプリケーションサーバーに接続することは避けてください。 同じユーザーまたはユーザーグループが同じアプリケーションサーバーに接続したままになることが理想的です。
このソリューションは、ユーザーのダウンタイムなしで、Webサーバーを追加することによってプレゼンテーション層を、また、アプリケーションサーバーを追加することによってアプリケーション層を拡張できます。 データ層は、データサーバーのCPUとメモリを増やすことによって拡張されます。
物理アーキテクチャ
次の図は、3層論理アーキテクチャの例と同じ3層デプロイメントで使用される物理ホストの例を示しています。
物理ホストまたは仮想化ホストは、ホスト障害が発生した場合や定期メンテナンスに備えて、100%容量のn + 1またはn + 2モデルを使用して各層にデプロイされることに注意してください。 ユーザーは複数のWebサーバーとアプリケーションサーバーに分散しているため、単一のサーバーの障害は少数のユーザーに影響を与え、ユーザーは残りのサーバーの1つに自動的に再接続します。
データ管理層は、たとえば1つ以上のストレージアレイに接続されたフェイルオーバークラスター(たとえば、 仮想化HA、InterSystemsデータベースミラーリング、または従来のフェイルオーバークラスタリング)によって高い可用性を持ちます。 ハードウェアまたはサービスに障害が発生した場合、クラスタリングは、クラスター内の残りのノードの1つでサービスを再起動します。 その他の利点として、ECPには復旧力が組み込まれており、データベースノードクラスターのフェールオーバーが発生した場合でもトランザクションの整合性が維持されるので、アプリケーションユーザーは、フェールオーバーと自動復旧が完了するまで処理の一時停止を観察しますが、ユーザーは切断されずにシームレスにトランザクションを再開することができます。
同じアーキテクチャを仮想化サーバーにマッピングすることもできます。たとえば、VMware vSphereを使用してアプリケーションサーバーを仮想化できます。
ECPキャパシティプランニング
上述のように、データサーバーはディスク上の永続ストレージへのデータベースの読み取りと書き込みを管理しますが、複数のアプリケーションサーバーはアプリケーション処理のほとんどを実行するソリューションの主力となるものです。 システムリソースのキャパシティプランニングを実施する際の重要な概念の要約は次のとおりです。
データサーバー (データベースサーバーと呼ばれることもあります)は通常、アプリケーション処理をほとんど実行しないため CPU要件は低くなります。ところが、このサーバーはストレージIOの大部分を実行するため、非常に高いストレージIOPSとなる可能性があります。これらは、 データベースの読み取りと書き込み、およびジャーナルの書き込み(ジャーナルIOについては後で詳しく説明します)です。
アプリケーションサーバーはほとんどのアプリケーション処理を実行するため高いCPU要件がありますが、ストレージIOはほとんどありません。
一般的に、ECPサーバーのCPU、メモリ、およびIOの要件は、高可用性に合わせてN + 1またはN + 2サーバーを考慮しながら、非常に大規模な単一サーバーソリューションのサイズを決定する場合と同じルールを使用して決定します。
基本的なCPUおよびストレージのサイジング:
My_Applicationがアプリケーション処理にピーク72 CPUコアを必要とし(ヘッドルームも考慮に入れてください)、書き込みデーモンサイクル中に2万回の書き込みが必要であり、持続的ピーク1万回のランダムデータベース読み取りが必要であると想像してください。
仮想サーバーまたは物理サーバーの、大まかなサイジングは次のとおりです。
4 x 32 CPUアプリケーションサーバー(3サーバー+ HA用に1)。 低いIOPS要件。
2 x 10 CPUデータサーバー(HA用にミラーリングまたはクラスター化)。 低レイテンシIOPS要件は、 2万回の書き込み、1万回の読み取り、およびWIJとジャーナルです。
データサーバーはほとんど処理を実行していませんが、システムプロセスとCachéプロセスを考慮して、8〜10 CPUのサイズに設定されています。 アプリケーションサーバーのサイズは、物理ホストごとのベストの価格性能比に基づいて、および/または可用性を考慮して設定することができます。 スケールアウトすると効率が多少低下しますが、通常はサーバーブロックに処理を追加して、スループットの線形に近い増加を期待できます。 制限は、ストレージIOで最初に生じる可能性が高くなります。
ヒント: HAで通常するように、ホスト、シャーシ、またはラックの障害の影響を考慮します。 VMWareでアプリケーションサーバーとデータサーバーを仮想化する場合、必ずvSphere DRSとアフィニティルールを適用して処理負荷を分散し、可用性を確保します。
ジャーナル同期IO要件
ECPデプロイメントのキャパシティプランニングに関するその他の考慮事項は、より高いIOが必要であり、ジャーナルの同期(別名 ジャーナル同期)のためより高いIOが必要であり、データサーバー上のジャーナリングのスケーラビリティを維持するため、非常に厳格なストレージ応答時間要件を課することです。 同期要求の発行により、ジャーナルの最後のブロックへの書き込みがトリガーされ、データの耐久性が確保されます。
アプリケーション次第ですが、高いトランザクションレートの一般的な顧客サイトでは、ECP以外の構成でのジャーナル書き込みIOPSが毎秒2桁台で見られることがよくあります。 ビジー状態のシステムでECPを使用すると、ECPによってジャーナル同期が強制されるため、ジャーナルディスク上で100〜1,000の書き込みIOPSを確認できます。
ヒント: mgstatを表示するか、 pButtons でmgstatを確認すると、ストレージIOリソース計画で考慮されるJrnwrts(ジャーナル書き込み)が表示されます。 ECPデータサーバーには、mgstatに表示されないジャーナルディスクへのジャーナル同期書き込みもあります。これらを確認するには、iostatなどを使用して、ジャーナルディスクのオペレーティングシステムメトリックを確認する必要があります。
ジャーナル同期とは何ですか?
ジャーナルの同期は次の場合に必要です。
データサーバーで障害が発生した場合のデータの耐久性と回復性を保証します。
また、アプリケーションサーバー間のキャッシュの一貫性を確保するためのトリガーでもあります。
非ECP構成では、Cachéデータベースへの変更はジャーナルバッファ(128 x 64Kバッファ)に書き込まれ、ジャーナルデーモンによって、バッファが一杯になったら、あるいは2秒ごとに、ディスク上のジャーナルファイルに書き込まれます。 Cachéはバッファ全体に64kを割り当て、これらは破棄されたり再作成される事なく常に再利用され、またCachéは単に終了オフセットを追跡します。 ほとんどの場合(大量の更新が一度に行われない限り)、ジャーナルの書き込みは非常に小さなものです。
ECPシステムでは、ジャーナルの同期も行われます。 ジャーナル同期は、現在のジャーナルバッファの関連部分をディスクに再書き込みして、ジャーナルが常にディスク上で常に最新になるようにすることとして定義できます。 そのため、ジャーナル同期リクエストにより、同じジャーナルブロックの一部(サイズが2kから64kの間)が、複数回、再書き込みされる事があります。
ジャーナル同期要求をトリガーできるECPクライアント上のイベントは、更新(SETまたはKILL)またはLOCKです。 たとえば、SETまたはKILLごとに、現在のジャーナルバッファがディスクに書き込まれます(または書き換えられます)。 非常にビジーなシステムでは、ジャーナル同期を1回の同期操作で複数の同期リクエストにバンドルまたは遅延できます。
ジャーナル同期のキャパシティプランニング
スループット維持のために、ジャーナル同期の平均書き込み応答時間は次のようにする必要があります。
<= 0.5 ms、最大<= 1 ms
詳細については、この投稿のIO要件の表を参照してください。パート6-CachéストレージIOプロファイル。
ヒント: CachéデータベースミラーリングをECPで使用する場合、ジャーナル同期は、プライマリミラーノードとバックアップミラーノード・ジャーナルディスクの両方に適用されます。 ミラー構成のルールは、両方のノードのストレージIOを同等に構成することなので、これは問題ではないでしょう。
お使いのシステムの特定のIOメトリックを検証する必要があります。このセクションの目的は、非常に厳密な応答時間の要件があるということを理解し、メトリックを見つける場所を把握することです。
ヒント: Cachéシステムでは、Red Hat LinuxとSuSE XFSファイルシステムが推奨されます。 ただし、テストでは、ext4はジャーナル同期などの小さな書き込みに対してより高いIOPS機能を提供することが明らかになっています。 ジャーナルディスクにはext4の使用を検討してください。
概要
この投稿はECPと、キャパシティプランニング中に考慮するその他のメトリックに関するオリエンテーションです。 近い将来、非常に大規模なシステムで実施されたCachéとECPに関する最新のベンチマークの結果を皆さんと共有できることを願っています。 いつものように、ご質問やご意見がある場合はどうぞお気軽にご連絡ください。 ツイッター @murray_oldfield