【まとめ】 Amazon Timestream について

今回は[Amazon Timestream](https://aws.amazon.com/jp/timestream/)の全体像について説明していきます！
## Amazon Timestreamとは

Amazon Timestream とは、高速かつスケーラブルなサーバレス時系列データベースサービスです。



### 時系列データって何？？

時系列データは、時間経過とともに変化する事象を測定するために、タイムスタンプとともに測定情報や属性情報を記録したデータをシーケンシャルに並べたデータのまとまりのことです。

主な時系列データベースの主なユースケースは以下になる。

- IoT センサーからの時系列データを収集して、分析・可視化
- サーバ機器の CPU・メモリ使用率や IOPS といった運用メトリクスをリアルタイムに分析・可視化して、正常性のモニタリング
- Web システムのクリックストリームデータを収集して、購入経路やショッピングカートの放棄率等のデータを可視化

## Amazon Timestreamの特徴について
- 低コストで高パフォーマンス
- 自動スケーリングでサーバレス
- データのライフサイクル管理
- 時系列専用データベースでSQLを使用して分析ができる
- 常時暗号化　など

## Amazon Timestreamの料金
シングル AZ 配置およびマルチ AZ 配置の料金は両方とも、データベースンスタンスの起動から終了または削除までに消費された DB インスタンス時間単位で計算されます。

| 項目 | メトリクス | 料金 |
|:-----------:|:------------:|:------------:|
| 書き込み           | 1 KB サイズの書き込み 100 万件 | 0.625USD  |
| クエリ             | 1 GB あたりのスキャン量       | 0.0125USD  |
| メモリストア        | 格納された GB 時間あたりの料金  | 0.045USD  |
| マグネティックストア | 格納された GB 月あたりの料金    | 0.0375USD |

※料金は今後変動する可能性あり

## Timestreamのデータベース構成
![スクリーンショット 2024-09-06 11.16.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839390/551d8d3a-b3d6-460a-9429-bc2ed94ef16c.png)

### DimensionsとMeasureの違い
ここではテーブル内でデータを挿入する際のDimensionsとMeasureの違いについて説明します。

- Dimensions（ディメンション）は、データの分類やグループ化に使用します。データの「メタデータ」として機能します
クエリのフィルタリング条件として使います。例えば、「地域が 'us-east-1' のデータのみを取得する」など。

- Measure（メジャー）は、 実際のデータ値を表す。データポイントの「実体」として機能します
データの分析や集計に使用します。例えば、「過去1時間の平均CPU使用率を計算する」など。

DimensionsとMeasureをうまく使い分けることで、Timestreamの時間系列データを効果的に管理し、分析することができます。

### Single-Measure RecordとMulti-Measure Record
- シングルメジャーレコードは、1レコードに1メジャーのみを登録できる形式です
ディメンションは複数登録可能です。
つまり、複数の測定値を同時に取得することは可能ですが、レコードは分かれるということです。

- マルチメジャーレコードは、1レコードに複数のメジャーを登録できる形式です
シングルメジャーレコードと同じく、ディメンションは複数登録可能です。

ユースケースよって使い分ける必要がありますが、マルチメジャーレコードの方が比較的、柔軟にデータを管理、登録ができると思います。

### Time streamのデータ型
Timestreamの型は以下になります。
| Timestream | python | 備考 |
|:-----------:|:------------:|:------------:|
| BIGINT  | int   | 64-bit       |
| BOOLEAN | bool  | true / false |
| DOUBLE  | float | 64-bit       |
| VARCHAR | str   | string       |

### メモリストアとマグネティックストアについて
次にAmazon Timestreamのデータライフサイクル管理について軽く説明します。

![スクリーンショット 2024-11-10 16.39.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839390/61dd61c7-ddc7-7abf-4ea6-55a988be0b02.png)
構成図はこんな感じ↑↑↑↑↑↑↑

データがAmazon Timestreamに送られてくると、まずメモリストアが受け止めそれを書き込み、一定時間後マグネティックストアに移動します。

メモリストアは、新しいデータを扱う領域で、マグネティックストアは、履歴データを扱う領域みたいな感じです。

また、実際にデータライフサイクル管理を設定することで自分なりにカスタマイズすることができます。

## 実際にAmazon Timestreamを作成して説明していきます
※今回はpythonを使用
1. まずはデータベースとテーブルを作成していきます

AWSマネジメントコンソールから作成する場合は以下の資料などで紹介されてます。
https://aws.amazon.com/jp/blogs/news/getting-started-with-timestream/

コマンドで作成する場合は以下になります。

:::note info
・前提条件
AWS CLIがインストールされていること。
AWS CLIが設定済みで、適切なIAM権限があること。

※AWS CLIが必要です。AWS CLIがインストールされていない場合は、AWS [CLIのインストールガイド](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)に従ってインストールしてください。
:::


```
＄aws timestream-write create-database --database-name MyDatabase
```
```
$ aws timestream-write create-table --database-name MyDatabase --table-name MyTable
```

2.実際にレコードを挿入していく

　今回は各地の気温情報をデータとして保存する。
　また今回はDimensionsをdevice_typeとして、その他はMeasureとしている。
　マルチメジャーレコードを採用します。

```python:example.py
import boto3
import time

#Measureとして登録するデータ
measure_data = [
    {
        "Name": "temperature_tokyo", 
        "Value": "25.5",
        "Type": "DOUBLE"
    },
    {
        "Name": "temperature_osaka", 
        "Value": "23.6",
        "Type": "DOUBLE"
    },
    {
        "Name": "temperature_fukuoka", 
        "Value": "21.8",
        "Type": "DOUBLE"
    }
]

#Timestreamに登録するフォーマット
records = [
    {
        "Dimensions": [{
            "Name": "device_type", "Value": "device_101", "DimensionValueType": "VARCHAR"
        }],
        "MeasureName": "temperature",
        "MeasureValueType": "MULTI", #マルチメジャーレコードを指定
        "Time": str(int(time.time() * 1000)), #現在の時間を指定
        "MeasureValues": measure_data
    }
]

#実行
try:
    client = boto3.client('timestream-write', region_name = REGION_NAME)
    response = client.write_records(
        DatabaseName = DATABASE_NAME,
        TableName = TABLE_NAME,
        Records = records
    )
    print(f"成功:{response}")

except Exception as e:
    print(f"失敗:{e}")
```

コードを実行するとTimestreamに無事挿入することができたと思います。
上記の方法で、"Dimensions"のdevice_typeをdevice_201にして別のレコードを作成してみましょう。

3.Timestreamに保存されたデータを実際にクエリ（取得）してみる。

まずは今回作成したレコードを全て取得してみたいと思います。
AWSマネジメントコンソールでTimestreamのクエリエディタからでも実行できます。
```sql
SELECT * FROM "DATABASE_NAME"."TABLE_NAME" ORDER BY time DESC
```

結果：
![スクリーンショット 2024-09-15 17.11.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839390/c4adc0cf-7098-0925-59ed-fba8a11de725.png)

次に"Dimensions"のdevice_typeをフィルターとしてdevice_101のみを取得してみたいと思います。
```sql
SELECT * FROM "DATABASE_NAME"."TABLE_NAME" WHERE "device_type" = 'device_101' ORDER BY time DESC
```
結果：
![スクリーンショット 2024-09-15 17.12.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839390/0314bf2c-7870-b306-fc05-e1c159b61991.png)

無事取得できました。

次にソースコードを実行してクエリしてみる。
```python:query.py
import boto3

client = boto3.client('timestream-query')

#クエリのフォーマット
query = f"""
SELECT * 
FROM "DATABASE_NAME"."TABLE_NAME"
WHERE "device_type" = 'device_101'
ORDER BY time DESC
"""

# クエリの実行
try:
    response = client.query(QueryString = query)
    print(f"response: {response}")
except Exception as e:
    print(f"取得に失敗しました: {e}")
```

結果：
```
response: {'QueryId': 'AEIACANXA5J3DX3V6T7DVROSEFTRGPPGZYNLWLXOO7RBFUNPCHQMLDOECXG6QQI', 'Rows': [{'Data': [{'ScalarValue': 'device_101'}, {'ScalarValue': 'temperature'}, {'ScalarValue': '2024-09-14 14:37:11.037000000'}, {'ScalarValue': '25.5'}, {'ScalarValue': '21.8'}, {'ScalarValue': '23.6'}]}], 'ColumnInfo': [{'Name': 'device_type', 'Type': {'ScalarType': 'VARCHAR'}}, {'Name': 'measure_name', 'Type': {'ScalarType': 'VARCHAR'}}, {'Name': 'time', 'Type': {'ScalarType': 'TIMESTAMP'}}, {'Name': 'temperature_tokyo', 'Type': {'ScalarType': 'DOUBLE'}}, {'Name': 'temperature_fukuoka', 'Type': {'ScalarType': 'DOUBLE'}}, {'Name': 'temperature_osaka', 'Type': {'ScalarType': 'DOUBLE'}}], 'QueryStatus': {'ProgressPercentage': 100.0, 'CumulativeBytesScanned': 42, 'CumulativeBytesMetered': 10000000}, 'ResponseMetadata': {'RequestId': 'E4IRLKLXWZSEYH624LEHXQZ4KM', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': 'E4IRLKLXWZSEYH624LEHXQZ4KM', 'content-type': 'application/x-amz-json-1.0', 'content-length': '736', 'date': 'Sat, 14 Sep 2024 16:19:40 GMT'}, 'RetryAttempts': 0}}
```

無事にdevice_101だけを取得することができました。

クエリをさらにカスタマイズしていくことにより、詳細にまた細かく分析、管理することができます。
→ またDimensionsとMeasureをうまく使い分けることで、データを効果的に管理し、分析することができます。

いかがでしたか？
少しはAmazon Timestreamがどのようなサービスなのか理解できたと思います。