

Amazon Timestream for LiveAnalytics に類似した機能をご希望の場合は Amazon Timestream for InfluxDB をご検討ください。リアルタイム分析に適した、シンプルなデータインジェストと 1 桁ミリ秒のクエリ応答時間を特徴としています。詳細については、[こちら](https://docs.aws.amazon.com//timestream/latest/developerguide/timestream-for-influxdb.html)を参照してください。

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# データモデリング
<a name="data-modeling"></a>

Amazon Timestream for LiveAnalytics は、タイムスタンプ付きの一連のデータを出力するアプリケーションやデバイスから、時系列データを収集、保存、分析するように設計されています。最適なパフォーマンスを実現するには、Timestream for LiveAnalytics に送信されるデータに時間特性が必要であり、時間はデータの本質的要素でなければなりません。

Timestream for LiveAnalytics では、アプリケーションの要件に合わせてさまざまな方法で柔軟にデータをモデリングできます。このセクションでは、こうしたパターンのいくつかを取り上げ、コストとパフォーマンスを最適化するための指針について説明します。ディメンションやメジャーなど、主要な [Amazon Timestream for LiveAnalytics の概念](concepts.md)を理解します。このセクションでは、データを保存するために単一のテーブルを作成するか複数のテーブルを作成するかを決める際の、以下の問題について詳しく解説します。
+ どのデータを同じテーブルに入れるか、あるいはいつデータを複数のテーブルおよびデータベースに分けるか。
+ Timestream for LiveAnalytics のマルチメジャーレコードと単一メジャーレコードの選び方。および、特にアプリケーションで複数の測定値を同時に追跡している場合の、マルチメジャーレコードを使用したモデリングの利点。
+ どの属性をディメンションまたはメジャーとしてモデリングするか。
+ メジャー名属性を効果的に使用してクエリのレイテンシーを最適化する方法。

**Topics**
+ [単一テーブルとマルチテーブル](#data-modeling-singleVmultitable)
+ [マルチメジャーレコードと単一メジャーレコード](#data-modeling-multiVsinglerecords)
+ [ディメンションとメジャー](#data-modeling-dimensionsmeasures)
+ [マルチメジャーレコードを用いたメジャー名の使用](#data-modeling-measurenamemulti)
+ [マルチメジャーレコードのパーティショニングに関する推奨事項](#data-modeling-multi-measure-partitioning)

## 単一テーブルとマルチテーブル
<a name="data-modeling-singleVmultitable"></a>

アプリケーションでデータをモデリングする際の重要な要素には、データをテーブルとデータベースにモデリングする方法もあります。Timestream for LiveAnalytics のデータベースとテーブルはアクセス制御の抽象化であり、KMS キーや保持期間などを指定します。Timestream for LiveAnalytics は、データを自動的にパーティショニングし、アプリケーションの取り込み、保存、クエリ負荷、要件に合わせてリソースをスケールするように設計されています。

Timestream for LiveAnalytics のテーブルは、保存済みのペタバイト規模のデータ、および 1 秒あたり数十ギガバイトのデータ書き込みにスケールできます。クエリは 1 時間あたり数百テラバイトを処理できます。Timestream for LiveAnalytics のクエリは複数のテーブルとデータベースにまたがることができ、結合とユニオンを提供して複数のテーブルとデータベースのデータにシームレスにアクセスすることを可能にします。したがって通常は、Timestream for LiveAnalytics でデータを整理する方法を決める際に、データまたはリクエストボリュームのスケールを最優先する必要はありません。以下は、異なるテーブル、または異なるデータベースにあるテーブルを使用する場合と比較して、同じテーブルにどのデータを一緒に入れるかを決める際に考慮すべき重要な点です。
+ データ保持ポリシー (メモリストアの保持、マグネティックストアの保持など) は、テーブルの詳細度によってサポートされます。したがって、必要とする保持ポリシーが異なるデータは、異なるテーブルに配置する必要があります。
+ AWS KMS データの暗号化に使用される キーは、データベースレベルで設定されます。したがって、暗号化キーの要件が異なるデータは、異なるデータベースに入れる必要があることを意味します。
+ Timestream for LiveAnalytics は、リソースベースのアクセス制御を、テーブルおよびデータベースの詳細度に応じてサポートします。どのデータを同じテーブルに書き込み、どのデータを異なるデータに書き込むかを決める際は、アクセス制御の要件を考慮に入れます。
+ どのデータをどのテーブルに保存するかを決める際は、ディメンション数、メジャー名、マルチメジャーの属性名の各[制限](https://docs.aws.amazon.com/timestream/latest/developerguide/ts-limits.html)に注意します。
+ データの整理方法を決める際はクエリのワークロードとアクセスパターンを考慮に入れます。これは、クエリのレイテンシーと書き込みの容易さがそれらに依存しているためです。
  + 頻繁にクエリするデータを同じテーブルに保存すると、通常はクエリの書き込みが容易になり、結合、ユニオン、あるいは一般的なテーブル式などを書き込む必要がなくなることが多いです。また、クエリのレイテンシーも通常は低くなります。ディメンションとメジャー名に述語を使用すれば、クエリに関連するデータをフィルタリングできます。

    例えば、6 つの大陸にあるデバイスからデータを保存するケースを考えてみましょう。クエリを使って、大陸をまたいで頻繁にデータにアクセスし、グローバルな集約ビューを取得する場合は、これらの大陸にあるデータを同じテーブルに保存すれば、クエリの記述が容易に行えます。一方、データを異なるテーブルに保存した場合、同じクエリ内のデータは引き続き結合できますが、テーブル間でデータを結合するためのクエリを書くことが必要になります。
  + Timestream for LiveAnalytics では、データにアダプティブパーティショニングとインデックス作成を使用するため、使用するクエリに関連するデータに対してのみクエリ料金が発生します。例えば、6 つの大陸にある 100 万台のデバイスのデータを保存しているテーブルがあり、クエリの述語が `WHERE device_id = 'abcdef'` または `WHERE continent = 'North America'` の形式をとる場合、そのデバイスまたは大陸のデータに対してのみクエリの料金が発生します。
  + 可能な限り、メジャー名を使用して、同じテーブル内の同時に出力されないまたは頻繁にクエリされないデータを分けて `WHERE measure_name = 'cpu'` などの述語を使用すると、メータリング上有利であるだけでなく、Timestream for LiveAnalytics が、クエリの述語で使用されるメジャー名を持たないパーティションを効果的に取り除くことも可能になります。それにより、クエリのレイテンシーやコストに影響を与えることなく、同じテーブルに、異なるメジャー名を持つ関連データを保存することができ、データが複数のテーブルに分散することを避けられます。メジャー名は、基本的にデータをパーティショニングし、クエリに無関係なパーティションを取り除くために使用されます。

## マルチメジャーレコードと単一メジャーレコード
<a name="data-modeling-multiVsinglerecords"></a>

Timestream for LiveAnalytics では、レコードごとに複数のメジャーを書き込む (マルチメジャー) か 1 つのメジャーを書き込む (単一メジャー) ことができます。

**マルチメジャーレコード**

多くの場合、追跡しているデバイスまたはアプリケーションは、複数のメトリクスまたはイベントを同じタイムスタンプで出力します。このような場合、同じタイムスタンプで出力されたすべてのメトリクスは、同じマルチメジャーレコードに保存できます。つまり、同じマルチメジャーレコードに保存されているすべてのメジャーは、同じデータ行内の異なる列として表示されます。

アプリケーションが、同時に測定されたデバイスから cpu、memory、disk\$1iops などのメトリクスを出力しているケースを考えてみましょう。以下は、同時に出力された複数のメトリクスが同じ行に保存されているテーブルの例です。2 つのホストが、1 秒に 1 回、メトリクスを出力していることがわかります。


| Hostname | measure\$1name | Time | cpu | メモリ | disk\$1iops | 
| --- | --- | --- | --- | --- | --- | 
| host-24Gju | metrics | 2021-12-01 19:00:00 | 35 | 54.9 | 38.2 | 
| host-24Gju | metrics | 2021-12-01 19:00:01 | 36 | 58 | 39 | 
| host-28Gju | metrics | 2021-12-01 19:00:00 | 15 | 55 | 92 | 
| host-28Gju | metrics | 2021-12-01 19:00:01 | 16 | 50 | 40 | 

**単一メジャーレコード**

単一メジャーレコードは、デバイスが異なる期間に異なるメトリクスを出力する場合や、異なる期間にメトリクス/イベントを出力するカスタムの処理ロジックを使用している場合 (デバイスの測定値/状態が変化した場合など) に適しています。各メジャーはそれぞれ異なるタイムスタンプを持っているため、Timestream for LiveAnalytics にある各自のレコードに保存することができます。例えば、土壌の温度と湿度を追跡している IoT センサーの例を考えてみます。この IoT センサーは、以前に報告されたエントリからの変更を検出した場合にのみ、レコードを出力します。以下は、単一メジャーレコードを使用して出力されるデータの例を示したものです。


| device\$1id | measure\$1name | Time | measure\$1value::double | measure\$1value::bigint | 
| --- | --- | --- | --- | --- | 
| sensor-sea478 | 温度 |  2021-12-01 19:22:32 | 35 | NULL | 
| sensor-sea478 | 温度 |  2021-12-01 18:07:51 | 36 | NULL | 
| sensor-sea478 | moisture |  2021-12-01 19:05:30 | NULL | 21 | 
| sensor-sea478 | moisture |  2021-12-01 19:00:01 | NULL | 23 | 

**単一メジャーレコードとマルチメジャーレコードの比較**

Timestream for LiveAnalytics を使用すると、アプリケーションの要件と特性に応じて、データを単一メジャーレコードまたはマルチメジャーレコードとして臨機応変にモデリングできます。単一のテーブルに、アプリケーションの要件に応じて、単一メジャーレコードとマルチメジャーレコードの両方を保存できます。一般的に、アプリケーションが複数のメジャー/イベントを同時に出力する場合は、パフォーマンスの高いデータアクセスと費用対効果の高いデータストレージのため、データをマルチメジャーレコードとしてモデリングすることが推奨されます。

例えば、数十万台のサーバーの[メトリクスとイベントを追跡する DevOps のユースケース](https://github.com/awslabs/amazon-timestream-tools/tree/mainline/tools/python/perf-scale-workload)を考えてみましょう。各サーバーは定期的に 20 のメトリクスと 5 つのイベントを出力し、これらのイベントとメトリクスは同時に出力されます。このデータは、単一メジャーレコードまたはマルチメジャーレコードを使用してモデリングできます (結果のスキーマについては[オープンソースのデータジェネレーター](https://github.com/awslabs/amazon-timestream-tools/tree/mainline/tools/python/perf-scale-workload)を参照してください)。このユースケースでは、マルチメジャーレコードと単一メジャーレコードを使用してデータをモデリングした場合、以下のような結果になります。
+ *取り込みメータリング* – マルチメジャーレコードでは、書き込まれる*取り込みバイトは約 40% 削減*されます。
+ *取り込みバッチ処理* – マルチメジャーレコードでは、送信されるデータのバッチが大きくなります。つまり、取り込みを処理するためにクライアントに必要になるスレッドと CPU が減少します。
+ *ストレージのメータリング* – マルチメジャーレコードでは、*ストレージは約 8 分の 1* まで減るため、メモリとマグネティックストアの両方でストレージを大幅に節約できます。
+ *クエリのレイテンシー* – マルチメジャーレコードでは、単一メジャーレコードに比べ、ほとんどのクエリタイプでクエリレイテンシーが低くなります。
+ *クエリの測定されたバイト数* – 10 MB 未満のデータをスキャンするクエリの場合、単一メジャーレコードとマルチメジャーレコードの結果は同等です。単一メジャーレコードにアクセスして 10 MB より大きいデータをスキャンするクエリの場合、単一メジャーレコードは通常、測定されるバイト数は低くなります。3 つ以上のメジャーを参照するクエリの場合、マルチメジャーレコードは測定されるバイト数が低くなります。
+ *マルチメジャークエリは記述が簡単* – クエリが複数のメジャーを参照する場合、マルチメジャーレコードを使用してデータをモデリングすると、より簡単に記述でき、クエリがよりコンパクトになります。

上記の要因は、追跡するメトリクスの数やデータに含まれるディメンションの数などによって変わってきます。前述の例では 1 つの例に対して複数の具体的なデータを示していますが、多くのアプリケーションシナリオおよびユースケースにおいて、アプリケーションが同時に複数のメジャーを出力する場合、マルチメジャーレコードとしてデータを保存する方が効果的であることが示されています。さらに、マルチメジャーレコードにはデータ型を選択したり他の複数の値をコンテキストとして保存したりできる柔軟性があります (リクエスト ID の保存や追加のタイムスタンプなど。詳細は後述)。

マルチメジャーレコードでは、前述の単一メジャーレコードの例で示したような少量のメジャーをモデリングすることもできます。`measure_name` を使用してメジャーの名前を保存したり、汎用のマルチメジャー属性名 (`DOUBLE` メジャー保存用の `value_double`、`BIGINT` メジャー保存用の `value_bigint`、追加の `TIMESTAMP` 値の保存用の `value_timestamp` など) を使用したりできます。

## ディメンションとメジャー
<a name="data-modeling-dimensionsmeasures"></a>

Timestream for LiveAnalytics のテーブルでは、*ディメンション* (保存しているデバイス/データの属性を識別する) と*メジャー* (追跡しているメトリクス/値) を保存できます。詳細については、「[Amazon Timestream for LiveAnalytics の概念](concepts.md)」を参照してください。Timestream for LiveAnalytics でアプリケーションをモデリングする場合、データをディメンションとメジャーにマッピングする方法は、取り込みとクエリのレイテンシーに影響します。以下は、データをディメンションおよびメジャーとしてモデリングする方法に関するガイドラインです。お使いのユースケースに応用できます。

**ディメンションの選択**

時系列データを送信しているソースを識別するデータは、時間が経過しても変化しない属性であるディメンションにぴったりです。例えば、メトリクスを出力するサーバーを使用している場合、サーバーを識別する属性 (ホスト名、リージョン、ラック、アベイラビリティーゾーンなど) がディメンションの候補になります。同様に、時系列データを報告する複数のセンサーを持つ IoT デバイスの場合、デバイス ID やセンサー ID などの属性がディメンションの候補になります。

データをマルチメジャーレコードとして書き込む場合、ディメンションとマルチメジャー属性は、テーブルで `DESCRIBE` を実行するか、`SELECT` ステートメントを実行すると、テーブルに列として表示されます。したがって、クエリを記述するときは、ディメンションとメジャーを同じクエリで自由に使用できます。ただし、データを取り込むための書き込みレコードを作成するときは、どの属性をディメンションとして指定し、どの属性をメジャー値として指定するかを選択する際に、次の点に注意する必要があります。
+ ディメンション名、ディメンション値、メジャー名、タイムスタンプは、時系列データを一意に識別します。Timestream for LiveAnalytics は、この一意の識別子を使用して重複するデータを削除します。つまり、Timestream for LiveAnalytics が、ディメンション名の値、ディメンション値、メジャー名の値、タイムスタンプ値が同じである 2 つのデータポイントを受信し、値のバージョン番号が同じである場合、Timestream for LiveAnalytics は重複するデータを削除します。新しい書き込みリクエストのバージョンが、Timestream for LiveAnalytics に既に存在するデータより前のものである場合、その書き込みリクエストは拒否されます。新しい書き込みリクエストのバージョンが後のものである場合、古い値は新しい値によって上書きされます。したがって、ディメンションの値の選択方法は、この重複排除の動作に影響します。
+ ディメンションの名前と値は更新することができませんが、メジャーの値は更新することができます。したがって、更新が必要になる可能性のあるデータは、メジャー値としてモデリングするのが最適です。例えば、工場の現場に色を変更できる機械があった場合、重複排除に必要な識別属性として色も選択する場合を除いて、色をメジャー値としてモデリングすることができます。つまり、メジャー値を使用すれば、時間とともに徐々に変化する属性のみを保存できます。

Timestream for LiveAnalytics のテーブルでは、ディメンションの名前と値からなる一意の組み合わせの数に制限がないことに注意してください。例えば、上記のような一意の値の組み合わせを単一のテーブルに数十億件保存することが可能です。ただし、次の例に示すように、ディメンションとメジャーを慎重に選択すれば、特にクエリの場合は、リクエストのレイテンシーを大幅に最適化することができます。

**ディメンション内の一意の ID**

アプリケーションのシナリオで、すべてのデータポイントに一意の識別子を保存する必要がある場合 (リクエスト ID、トランザクション ID、相関 ID など)、ID 属性をメジャー値としてモデリングすると、クエリのレイテンシーを大幅に改善できます。マルチメジャーレコードを使用してデータをモデリングする場合、ID は他のディメンションおよび時系列データとの関係に応じて同じ行に表示されるため、クエリでそれらを引き続き効果的に使用することができます。例えば、サーバーが出力するすべてのデータポイントに一意のリクエスト ID 属性がある [DevOps のユースケース](https://github.com/awslabs/amazon-timestream-tools/tree/mainline/tools/python/perf-scale-workload)では、リクエスト ID をメジャー値としてモデリングすると、一意のリクエスト ID をディメンションとしてモデリングする場合に比べて、異なるクエリタイプ間でのクエリのレイテンシーは、最大で 4 分の 1 まで減少します。

すべてのデータポイントで完全に一意ではないものの数十万から数百万の一意の値を持つ属性についても、同様の例で説明できます。これらの属性は共に、ディメンションまたはメジャー値としてモデリングできます。前述したように、書き込みパスでの重複排除に値が必要である場合、またはクエリで値を頻繁に述語として使用する場合 (アプリケーションを使って数百万台のデバイスを追跡している `device_id = 'abcde'` など、その属性の値に関する等価述語を含む `WHERE` 句などで) は、ディメンションとしてモデリングする必要があります。

**マルチメジャーレコードを持つデータ型のリッチさ**

マルチメジャーレコードは柔軟性が高く、データを効果的にモデリングすることができます。マルチメジャーレコードに保存されデータは、ディメンションと同様にテーブルに列として表示されるため、ディメンション値とメジャー値のクエリを同様の容易さで行うことができます。これらのパターンの一部は、前述の例で既に紹介しました。以下は、アプリケーションのユースケースに合わせてマルチメジャーレコードを効果的に使用するための他のパターンです。

マルチメジャーレコードは、データ型 `DOUBLE`、`BIGINT`、`VARCHAR`、`BOOLEAN`、`TIMESTAMP` の属性をサポートしています。したがって、さまざまな型の属性に自然に適合します。
+ *位置情報*: 例えば場所 (緯度と経度で表示される) を追跡する場合、これをマルチメジャー属性としてモデリングすると、特に緯度と経度に関する述語がある場合、それらを `VARCHAR` ディメンションとして保存する場合に比べてクエリのレイテンシーは低下します。
+ *単一レコード内の複数のタイムスタンプ*: アプリケーションのシナリオで、時系列レコード用に複数のタイムスタンプを追跡する必要がある場合、マルチメジャーレコードの追加属性としてそれらをモデリングできます。このパターンを使用すれば、データを将来のタイムスタンプまたは過去のタイムスタンプと共に保存することができます。すべてのレコードには、レコードをパーティショニング、インデックス化、一意に識別するため時間列のタイムスタンプが引き続き使用されることに注意してください。

特に、クエリで述語を使用している数値データまたはタイムスタンプがある場合、それらの属性をディメンションではなくマルチメジャー属性としてモデリングすると、クエリのレイテンシーが低下します。これは、マルチメジャーレコードでサポートされているリッチなデータ型を使用してデータをモデリングするときに、データをディメンションとしてモデリングして `VARCHAR` から別のデータ型に値をキャストするのではなく、ネイティブなデータ型を使って述語を表現できるためです。

## マルチメジャーレコードを用いたメジャー名の使用
<a name="data-modeling-measurenamemulti"></a>

Timestream for LiveAnalytics のテーブルは、*メジャー名*と呼ばれる特別な属性 (または列) をサポートしています。この属性の値は、Timestream for LiveAnalytics にレコードを書き込むたびに指定します。単一メジャーレコードの場合、メトリクスの名前 (サーバーメトリクスでは CPU や memory、センサーメトリクスでは temperature や pressure など) を使用するのが自然です。マルチメジャーレコードを使用する場合、マルチメジャーレコードの属性には名前が付けられ、それらの名前がテーブルの列名になります。したがって、cpu、memory、temperature、pressure などが、マルチメジャー属性の名前になります。自然な質問を用いると、メジャー名を効果的に使用できます。

Timestream for LiveAnalytics は、メジャー名属性の値を使用してデータをパーティショニングし、インデックス化します。したがって、テーブルに異なる複数のメジャー名があり、クエリがそれらの値をクエリ述語として使用している場合、Timestream for LiveAnalytics はカスタムパーティショニングとインデックス作成を使用することでクエリとは無関係のデータを削除できます。例えば、テーブルに `cpu` と `memory` のメジャー名があり、クエリに述語 `WHERE measure_name = 'cpu'` がある場合、Timestream for LiveAnalytics はクエリとは無関係のメジャー名のデータを効果的に削除できます。例えばこちらの例ではメジャー名のメモリを含む行です。この削除は、マルチメジャーレコードでメジャー名を使用する場合にも適用されます。メジャー名属性は、テーブルのパーティショニング属性として効果的に使用できます。メジャー名は、ディメンションの名前と値、ならびに時間と併せて、Timestream for LiveAnalytics テーブル内のデータのパーティショニングに使用されます。Timestream for LiveAnalytics テーブルで許可されている一意のメジャー名の数には[制限](https://docs.aws.amazon.com/timestream/latest/developerguide/ts-limits.html)がありますのでご注意ください。また、メジャー名がメジャー値のデータ型に関連付けられていることにも注意が必要です。例えば、あるメジャー名は 1 つの型のメジャー値にのみ関連付けることができます。この型は、`DOUBLE`、`BIGINT`、`BOOLEAN`、`VARCHAR`、`MULTI` のいずれかになります。メジャー名で保存されたマルチメジャーレコードのデータ型は `MULTI` になります。異なるデータ型 (`DOUBLE`、`BIGINT`、`VARCHAR`、`BOOLEAN`、`TIMESTAMP`) を持つ複数のメトリクスを単一のマルチメジャーレコードに保存できるため、異なる型のデータをマルチメジャーレコードに関連付けることが可能です。

以下のセクションでは、メジャー名の属性を効果的に使用して、同じテーブル内のさまざまな型のデータをグループ化する方法について、いくつかの異なる例を用いて説明します。

**品質と価値を報告する IoT センサー**

IoT センサーのデータをモニタリングするアプリケーションを使用している場合を想定してみましょう。各センサーは温度や圧力などのさまざまな測定値を追跡します。センサーは、実際の測定値の他、測定の品質、つまり測定値や測定の単位がどの程度正確であるかの評価も報告しています。品質、単位、値は一緒に出力されるため、以下のデータ例に示すように、マルチメジャーレコードとしてモデリングできます。そこでは `device_id` はディメンション、`quality`、`value`、`unit` はマルチメジャー属性です。


| device\$1id | measure\$1name | Time | Quality | 値 | Unit | 
| --- | --- | --- | --- | --- | --- | 
| sensor-sea478 | 温度 | 2021-12-01 19:22:32 | 92 | 35 | c | 
| sensor-sea478 | 温度 | 2021-12-01 18:07:51 | 93 | 34 | c | 
| sensor-sea478 | pressure | 2021-12-01 19:05:30 | 98 | 31 | psi | 
| sensor-sea478 | pressure | 2021-12-01 19:00:01 | 24 | 132 | psi | 

この方法を使うことで、マルチメジャーレコードの利点と、メジャー名の値を使用したデータのパーティショニングおよび削除とを組み合わせることができます。クエリが温度などの単一のメジャーを参照している場合、クエリに述語 `measure_name` を含めることができます。以下は、このようなクエリの一例です。このクエリでは、品質が 90 を超える測定値の単位も反映されます。

```
SELECT device_id, time, value AS temperature, unit
FROM db.table
WHERE time > ago(1h)
    AND measure_name = 'temperature'
    AND quality > 90
```

クエリで述語 `measure_name` を使用すると、Timestream for LiveAnalytics はクエリとは無関係のパーティションとデータを効果的に取り除けるため、クエリのレイテンシーが改善します。

また、すべてのメトリクスが同じタイムスタンプで出力されている場合、もしくは複数のメトリクスが同じクエリで一緒にクエリされている場合は、すべてのメトリクスを同じマルチメジャーレコードに保存することもできます。例えば、temperature\$1quality、temperature\$1value、temperature\$1unit、pressure\$1quality、pressure\$1value、pressure\$1unit などの属性を持つマルチメジャーレコードを作成することが可能です。単一メジャーレコードとマルチメジャーレコードをそれぞれ使用したデータのモデリングについては既に解説済みですが、そのポイントの多くは、データをモデリングする方法の決定に適用されます。コスト、取り込みおよびクエリのレイテンシー、クエリの記述のしやすさを最適化できるモデルを選ぶときは、クエリのアクセスパターンとデータの生成方法を考慮します。

**同一テーブルにあるさまざまな型のメトリクス**

マルチメジャーレコードとメジャー名の値とを組み合わせることができるもう 1 つのユースケースが、同じデバイスから個別に出力されるさまざまな型のデータをモデリングする方法です。定期的に出力されるメトリクスと不規則なイベントという 2 種類のデータをサーバーが出力している、DevOps モニタリングのユースケースを考えてみましょう。このアプローチの一例が、「[Sample Ingestion and Query Workload Generator for Amazon Timestream](https://github.com/awslabs/amazon-timestream-tools/tree/mainline/tools/python/perf-scale-workload)」で解説されているスキーマです。このケースでは、同じサーバーから出力される異なるタイプのデータを、異なるメジャー名を使用して、同じテーブルに保存します。例えば、同時に出力されるすべてのメトリクスは、メジャー名メトリクスと共に保存されます。このメトリクスから別の時点で出力されるすべてのイベントは、メジャー名イベントと共に保存されます。テーブルのメジャースキーマ (`SHOW MEASURES` クエリの出力など) は次のとおりです。


| measure\$1name | data\$1type | ディメンション | 
| --- | --- | --- | 
| イベント | multi | [\$1"data\$1type":"varchar","dimension\$1name":"availability\$1zone"\$1,\$1"data\$1type":"varchar","dimension\$1name":"microservice\$1name"\$1,\$1"data\$1type":"varchar","dimension\$1name":"instance\$1name"\$1,\$1"data\$1type":"varchar","dimension\$1name":"process\$1name"\$1,\$1"data\$1type":"varchar","dimension\$1name":"jdk\$1version"\$1,\$1"data\$1type":"varchar","dimension\$1name":"cell"\$1,\$1"data\$1type":"varchar","dimension\$1name":"region"\$1,\$1"data\$1type":"varchar","dimension\$1name":"silo"\$1] | 
| metrics | multi | [\$1"data\$1type":"varchar","dimension\$1name":"availability\$1zone"\$1,\$1"data\$1type":"varchar","dimension\$1name":"microservice\$1name"\$1,\$1"data\$1type":"varchar","dimension\$1name":"instance\$1name"\$1,\$1"data\$1type":"varchar","dimension\$1name":"os\$1version"\$1,\$1"data\$1type":"varchar","dimension\$1name":"cell"\$1,\$1"data\$1type":"varchar","dimension\$1name":"region"\$1,\$1"data\$1type":"varchar","dimension\$1name":"silo"\$1,\$1"data\$1type":"varchar","dimension\$1name":"instance\$1type"\$1] | 

このケースでは、イベントとメトリクスではディメンションのセットも異なることがわかります。イベントには異なるディメンション `jdk_version` と `process_name` があり、メトリクスにはディメンション `instance_type` と `os_version` があります。

異なるメジャー名を使用すると、`WHERE measure_name = 'metrics'` などの述語を使用してクエリを記述し、メトリクスのみを取得することができます。また、同じテーブル内の同じインスタンスから出力されるすべてのデータがある場合、述語 `instance_name` を使用してより単純なクエリを記述し、そのインスタンスのデータすべてを取得することもできます。例えば、述語 `measure_name` を持たないフォーム `WHERE instance_name = 'instance-1234'` の述語は、特定のサーバーインスタンスのデータすべてを返します。

## マルチメジャーレコードのパーティショニングに関する推奨事項
<a name="data-modeling-multi-measure-partitioning"></a>

**重要**  
**このセクションは廃止されました。**  
これらの推奨事項は古いバージョンです。パーティショニングは、[ユーザー定義のパーティションキー](customer-defined-partition-keys.md)を使用してより適切に管理できるようになりました。

時系列のエコシステムでは、大量のデータを取り込み保存する必要がある、ワークロードの数が増えていると同時に、高いカーディナリティのディメンション値のセットでデータにアクセスするときに、低レイテンシーのクエリレスポンスが必要になることが明らかになっています。

このような特性のため、このセクションの推奨事項は、次のようなユーザーのワークロードに有用です。
+ マルチメジャーレコードを採用している、または採用したいと考えている。
+ 長期保存される大量のデータがシステムに追加されることが予想される。
+ メインアクセス (クエリ) パターンにレイテンシーの低い応答時間が必要である。
+ 最も重要なクエリパターンでは、述語に何らかのフィルタリング条件が必要になることがわかっている。このフィルタリング条件は、高いカーディナリティのディメンションに基づいている。例えば、UserId、DeviceId、ServerID、host-name などのイベントや集計を考慮する。

以上のようなケースでは、エンジンはマルチメジャー名を使用してデータをパーティショニングし、単一の値を指定することでパーティションの利点が制限されるため、すべてのマルチメジャーの単一名は役に立ちません。これらのレコードのパーティショニングは、主に 2 つのディメンションに基づいています。時間を x 軸、ディメンション名のハッシュと `measure_name` が y 軸にあるとします。このようなケースでは、`measure_name` はパーティショニングキーとほぼ同等に機能します。

推奨事項は次のとおりです。
+ 前述のようなユースケースでデータをモデリングする場合は、メインクエリのアクセスパターンの直接的な派生である `measure_name` を使用します。例えば、次のようになります。
  + このユースケースでは、エンドユーザーの観点からアプリケーションのパフォーマンスと QoE を追跡する必要があります。これは、単一のサーバーまたは IoT デバイスの測定値を追跡することにもなります。
  + UserId でクエリとフィルタリングを行う場合は、取り込み時に、`measure_name` を UserId に関連付ける最適な方法を見つける必要があります。
  + マルチメジャーテーブルには 8,192 件の異なるメジャー名しか保持できないため、どの式を使用する場合でも 8,192 件を超える値は生成しないでください。
+ 文字列値に適用して成功した方法の 1 つが、文字列値にハッシュアルゴリズムを適用する方法です。次に、ハッシュ結果の絶対値と 8,192 を使ってモジュロ演算を実行します。

  ```
  measure_name = getMeasureName(UserId)
  int getMeasureName(value) {
      hash_value =  abs(hash(value))
      return hash_value % 8192
  }
  ```
+ また、値の範囲が –8,192～8,192 である可能性を排除するため、`abs()` も追加しました。これはモジュロ演算の前に実行する必要があります。
+ この方法を使用すると、パーティショニングされていないデータモデルで実行する場合に比べ何分の一かの時間でクエリを実行できます。
+ データをクエリするときは、新たに派生した値 `measure_name` を使用するフィルタリング条件を述語に含めるようにします。例えば、次のようになります。
  + 

    ```
    SELECT * FROM your_database.your_table 
    WHERE host_name = 'Host-1235' time BETWEEN '2022-09-01' 
        AND '2022-09-18' 
        AND measure_name = (SELECT cast(abs(from_big_endian_64(xxhash64(CAST('HOST-1235' AS varbinary))))%8192 AS varchar))
    ```
  + これにより、スキャンされるパーティションの合計数を最小限に抑えられ、クエリに要する時間を徐々に高速化できます。

このパーティションスキーマの利点を生かしたい場合は、ハッシュをクライアント側で計算し、クエリエンジンへの静的値として Timestream for LiveAnalytics に渡す必要があります。前述の例で紹介しているのは、生成されたハッシュを必要に応じてエンジンで解決できることを検証する方法です。


| 時間 | host\$1name | location | server\$1type | cpu\$1usage | available\$1memory | cpu\$1temp | 
| --- | --- | --- | --- | --- | --- | --- | 
|  2022-09-07 21:48:44 .000000000  |  host-1235  |  us-east1  |  5.8xl  |  55  |  16.2  |  78  | 
|  R2022-09-07 21:48:44 .000000000  |  host-3587  |  us-west1  |  5.8xl  |  62  |  18.1  |  81  | 
|  2022-09-07 21:48:45.000000000  |  host-258743  |  eu-central  |  5.8xl  |  88  |  9.4  |  91  | 
|  2022-09-07 21:48:45 .000000000  |  host-35654  |  us-east2  |  5.8xl  |  29  |  24  |  54  | 
|  R2022-09-07 21:48:45 .000000000  |  host-254  |  us-west1  |  5.8xl  |  44  |  32  |  48  | 

推奨事項に従って関連する `measure_name` を生成する方法として、取り込みパターンに依存する 2 つのパスがあります。

1. *履歴データのバッチ取り込みの場合* – バッチプロセスに独自のコードを使用する場合は、書き込みコードに変換を追加できます。

   前述の例を基に構築します。

   ```
           List<String> hosts = new ArrayList<>();
   
           hosts.add("host-1235");
           hosts.add("host-3587");
           hosts.add("host-258743");
           hosts.add("host-35654");
           hosts.add("host-254");
   
           for (String h: hosts){
               ByteBuffer buf2 = ByteBuffer.wrap(h.getBytes());
               partition = abs(hasher.hash(buf2, 0L)) % 8192;
               System.out.println(h + " - " + partition);
   
           }
   ```

   Output

   ```
   host-1235 - 6445
   host-3587 - 6399
   host-258743 - 640
   host-35654 - 2093
   host-254 - 7051
   ```

   結果のデータセット    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/ja_jp/timestream/latest/developerguide/data-modeling.html)

1. *リアルタイム取り込み* – データの受信時に実施中の `measure_name` を生成する必要があります。

どちらの場合も、ハッシュ生成アルゴリズムを両端 (取り込みとクエリ) でテストし、同じ結果が得られることを確認することが推奨されます。

こちらは、`host_name` に基づいてハッシュ化した値を生成するコードの一例です。

**Example Python**  

```
>>> import xxhash
>>> from bitstring import BitArray
>>> b=xxhash.xxh64('HOST-ID-1235').digest()
>>> BitArray(b).int % 8192
### 3195
```

**Example Go**  

```
package main

import (
    "bytes"
    "fmt"
    "github.com/cespare/xxhash"
)

func main() {
    buf := bytes.NewBufferString("HOST-ID-1235")
    x := xxhash.New()
    x.Write(buf.Bytes())
    // convert unsigned integer to signed integer before taking mod
    fmt.Printf("%f\n", abs(int64(x.Sum64())) % 8192)
}

func abs(x int64) int64 {
    if (x < 0) {
        return -x
    }
    return x
}
```

**Example Java**  

```
import java.nio.ByteBuffer;

import net.jpountz.xxhash.XXHash64;

public class test {
    public static void main(String[] args) {
        XXHash64 hasher = net.jpountz.xxhash.XXHashFactory.fastestInstance().hash64();

        String host = "HOST-ID-1235";
        ByteBuffer buf = ByteBuffer.wrap(host.getBytes());

        Long result = Math.abs(hasher.hash(buf, 0L));
        Long partition = result % 8192;

        System.out.println(result);
        System.out.println(partition);
    }
}
```

**Example Maven の依存関係**  

```
        <dependency>
            <groupId>net.jpountz.lz4</groupId>
            <artifactId>lz4</artifactId>
            <version>1.3.0</version>
        </dependency>
```