メインコンテンツまでスキップ
最新4mo ago

Athena DynamoDB コネクタ

自動スキーマ検出により DynamoDB テーブルを Redash/Athena に接続 — 1つのコマンドですべてをデプロイします。


機能

  • 自動スキーマ推論 — DynamoDB テーブルをスキャンして、すべてのカラムと型を自動的に検出します

  • ワンコマンドデプロイ — 単一の CLI コマンドで Lambda コネクタ、Athena カタログ、Glue テーブルを作成します

  • SQL でクエリ — Redash または Athena コンソールで標準 SQL を使用して DynamoDB テーブルをすぐにクエリできます

  • 複数テーブルのサポート — 同じ Glue データベースに無制限のテーブルを追加できます

  • クリーンなアーキテクチャ — 各テーブルは独自の Lambda コネクタを持ち、適切な IAM 分離が行われます


クイックスタート

cd tool/redash-dynamodb-connector

# DynamoDB テーブルを追加
pnpm exec tsx src/index.ts add \
--table pascal-alert-history \
--glue-database unit-dev-analytics-database

このツールは次を実行します:

  1. ✅ テーブルが存在することを検証

  2. ✅ スキャンしてスキーマを推論 (すべてのカラムと型を自動検出)

  3. ✅ Lambda コネクタ関数を作成

  4. ✅ Athena データカタログを作成

  5. ✅ 検出されたスキーマで Glue テーブルを作成

  6. ✅ CDK を使用してインフラストラクチャをデプロイ

これでテーブルがクエリ可能になります!


前提条件

  • AWS 認証情報が設定されている (AWS_PROFILE またはデフォルト認証情報)

  • Node.js と pnpm がインストールされている

  • CDK CLI が利用可能 (npx cdk が自動的に使用されます)


使用方法

基本コマンド

pnpm exec tsx src/index.ts add --table <table-name> --glue-database <database-name>

コマンドオプション

オプション説明必須デフォルト
--tableDynamoDB テーブル名はい-
--glue-database既存 Glue データベース名いいえ新しいデータベースを作成
--catalog-nameカスタム Athena カタログ名いいえサニタイズされたテーブル名
--column-mapping混合ケースのカラムをマッピングいいえ-
--product製品名 (unit/spring/plants)いいえunit または $PRODUCT_NAME
--env環境 (dev/stg/prod)いいえdev または $ENV_NAME
--regionAWS リージョンいいえap-northeast-1

自動スキーマ検出を使用してテーブルを追加

pnpm exec tsx src/index.ts add \
--table pascal-alert-history \
--glue-database unit-dev-analytics-database

出力:

✓ Table found
Inferring table schema...
✓ Inferred 12 columns from 10 sample items
- alert_id: string
- code: string
- created_at: string
- level: int
- status: string
...
Deploying connector...
✓ Deployment complete!

同じデータベースに複数のテーブルを追加


# テーブル 1
pnpm exec tsx src/index.ts add \
--table pascal-alert-history \
--glue-database unit-dev-analytics-database

# テーブル 2
pnpm exec tsx src/index.ts add \
--table device-metrics \
--glue-database unit-dev-analytics-database

# テーブル 3
pnpm exec tsx src/index.ts add \
--table user-events \
--glue-database unit-dev-analytics-database

すべてのテーブルが同じ Glue データベースを共有しますが、独立した Lambda コネクタを持っています。

カラムマッピング付き (混合ケースのカラム)

pnpm exec tsx src/index.ts add \
--table MyDynamoDBTable \
--column-mapping "deviceid=DeviceId,timestamp=TimeStamp" \
--glue-database unit-dev-analytics-database


作成されるもの

テーブルごと

Lambda 関数: {product}-{env}-dynamodb-{table_name}

  • 例: unit-dev-dynamodb-pascal_alert_history

  • Athena から DynamoDB へのフェデレーテッドクエリを処理します

  • 正しい IAM 権限で自動的にプロビジョニングされます

Athena データカタログ: Lambda 関数と同じ名前

  • Lambda コネクタを指します

  • Athena を通じた SQL クエリを有効にします

Glue テーブル: 指定されたデータベース内の {table_name}

  • 自動的に推論されたカラムスキーマを含みます

  • Athena がテーブル構造を理解するためのメタデータ

共有リソース (スタックごとに1回作成)

S3 Spill Bucket: {product}-{env}-dynamodb-spill

  • クエリ結果がメモリを超える場合に Lambda が使用します

  • 1日後に自動クリーンアップ

S3 結果バケット: {product}-{env}-athena-results

  • Athena クエリ結果を保存します

  • 30日後に自動クリーンアップ

Athena ワークグループ: {product}-{env}-analytics

  • クエリ実行用に構成されます

  • CloudWatch メトリクスが有効です


命名規則

このツールは一貫した命名パターンに従います:

リソースパターン
Lambda 関数{product}-{env}-dynamodb-{table}unit-dev-dynamodb-pascal_alert_history
Athena カタログLambda と同じunit-dev-dynamodb-pascal_alert_history
Glue テーブル{table} (サニタイズ済み)pascal_alert_history
Spill Bucket{product}-{env}-dynamodb-spillunit-dev-dynamodb-spill
結果バケット{product}-{env}-athena-resultsunit-dev-athena-results
ワークグループ{product}-{env}-analyticsunit-dev-analytics

注: テーブル名は AWS の命名要件を満たすために小文字に変換され、ハイフンはアンダースコアに置き換えられます。


Redash でのクエリ

1. Redash データソースを作成

Redash で Athena データソースを構成します:

設定
名前dynamodb-connector-dev
タイプAmazon Athena
AWS リージョンap-northeast-1
AWS アクセスキーIAM ユーザーのアクセスキー
AWS シークレットキーIAM ユーザーのシークレットキー
S3 ステージングパスs3://unit-dev-athena-results/
スキーマ名unit-dev-analytics-database
Athena ワークグループunit-dev-analytics

追加設定 (セクションを展開):

  • "Glue データカタログを使用" をチェック

  • Glue データカタログ ID: unit-dev-dynamodb-pascal_alert_history,unit-dev-dynamodb-device_metrics (すべてのカタログ名をコンマで区切ったリスト)

2. データをクエリ

3 部構成の命名形式を使用します:

SELECT *
FROM "unit-dev-dynamodb-pascal_alert_history"."unit-dev-analytics-database"."pascal_alert_history"
LIMIT 10;

形式: "catalog_name"."database_name"."table_name"

クエリの例

ステータスでフィルタリング:

SELECT alert_id, code, status, created_at, level
FROM "unit-dev-dynamodb-pascal_alert_history"."unit-dev-analytics-database"."pascal_alert_history"
WHERE status = 'active'
ORDER BY created_at DESC
LIMIT 100;

データを集計:

SELECT
code,
COUNT(*) as alert_count,
AVG(duration_seconds) as avg_duration
FROM "unit-dev-dynamodb-pascal_alert_history"."unit-dev-analytics-database"."pascal_alert_history"
WHERE level > 1
GROUP BY code
ORDER BY alert_count DESC;

他のテーブルと結合:

SELECT
a.alert_id,
a.code,
d.device_name,
a.created_at
FROM "unit-dev-dynamodb-pascal_alert_history"."unit-dev-analytics-database"."pascal_alert_history" a
JOIN "unit-dev-dynamodb-devices"."unit-dev-analytics-database"."devices" d
ON a.device_id = d.device_id
WHERE a.status = 'active'
LIMIT 50;

3. 新しいテーブルの追加

新しいテーブルを追加する場合:

ステップ 1: ツールでデプロイ:

pnpm exec tsx src/index.ts add --table new-table --glue-database unit-dev-analytics-database

ステップ 2: Redash データソースを更新:

  • データソース設定に移動

  • Glue データカタログ ID フィールドに新しいカタログ ID を追加:

unit-dev-dynamodb-pascal_alert_history,unit-dev-dynamodb-device_metrics,unit-dev-dynamodb-new_table


**ステップ 3:** すぐにクエリ:

```sql
SELECT * FROM "unit-dev-dynamodb-new_table"."unit-dev-analytics-database"."new_table" LIMIT 10;


必要な IAM 権限

デプロイ用 (開発者/管理者)

デプロイを実行する AWS 認証情報には以下が必要です:

  • スタック上の cloudformation:*

  • コネクタ作成用の lambda:*

  • カタログとワークグループ作成用の athena:*

  • データベースとテーブル作成用の glue:*

  • バケット作成用の s3:*

  • Lambda 実行ロール用の iam:CreateRole, iam:AttachRolePolicy

  • SAR からのコネクタデプロイ用の serverlessrepo:CreateCloudFormationTemplate

クエリ用 (Redash IAM ユーザー)

これらの権限を持つカスタマーマネージド IAM ポリシーを作成:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:AbortMultipartUpload"
],
"Resource": [
"arn:aws:s3:::unit-dev-dynamodb-spill/*",
"arn:aws:s3:::unit-dev-athena-results",
"arn:aws:s3:::unit-dev-athena-results/*"
]
},
{
"Effect": "Allow",
"Action": [
"athena:StartQueryExecution",
"athena:GetQueryExecution",
"athena:GetQueryResults",
"athena:StopQueryExecution",
"athena:GetWorkGroup",
"athena:GetDataCatalog",
"athena:GetDatabase",
"athena:GetTableMetadata",
"athena:ListDataCatalogs",
"athena:ListDatabases",
"athena:ListTableMetadata"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"glue:GetDatabase",
"glue:GetTable",
"glue:GetTables",
"glue:GetPartition",
"glue:GetPartitions",
"glue:BatchGetPartition"
],
"Resource": [
"arn:aws:glue:ap-northeast-1:404232320784:catalog",
"arn:aws:glue:ap-northeast-1:404232320784:database/unit-dev-analytics-database",
"arn:aws:glue:ap-northeast-1:404232320784:table/unit-dev-analytics-database/*"
]
},
{
"Effect": "Allow",
"Action": ["lambda:InvokeFunction"],
"Resource": [
"arn:aws:lambda:ap-northeast-1:404232320784:function:unit-dev-dynamodb-*"
]
},
{
"Effect": "Allow",
"Action": [
"dynamodb:List*",
"dynamodb:DescribeStream",
"dynamodb:DescribeTable",
"dynamodb:Get*",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "arn:aws:dynamodb:ap-northeast-1:404232320784:table/*"
}
]
}

注: ワイルドカードパターン (unit-dev-dynamodb-*, table/*) により、毎回 IAM 権限を変更することなく、現在および将来のすべてのテーブルをクエリできます。

IAM 権限を追加するステップ

  1. IAM コンソールポリシー に移動

  2. ポリシーを作成JSON タブをクリック

  3. 上記のポリシーを貼り付け (必要に応じてリージョン/アカウント ID を調整)

  4. 名前をつけます: RedashDynamoDBConnectorAccess

  5. ポリシーを作成

  6. Redash IAM ユーザー → 権限ポリシーをアタッチ に移動

  7. RedashDynamoDBConnectorAccess を検索してアタッチ


自動スキーマ推論

このツールは以下の方法で DynamoDB テーブルスキーマを自動的に検出します:

  1. サンプルアイテムをスキャン — テーブルから 10 アイテムを読み込み (設定可能)

  2. 型を検出 — JavaScript 型を Athena/Glue 型にマッピング:

    • stringstring
    • number (整数) → int
    • number (小数) → double
    • booleanboolean
    • arrayarray<type>
    • objectstring (JSON として保存)
  3. スキーマを作成 — 検出されたカラムで Glue テーブルを生成

  4. 混合型を処理 — カラムがアイテム全体で複数の型を持つ場合、デフォルトは string

出力例:

Inferring table schema...
Scanning 10 items to infer schema...
✓ Inferred 12 columns from 10 sample items
- alert_data: string
- alert_id: string
- clear_reason: string
- cleared_at: string
- code: string
- created_at: string
- created_month: string
- device_id: string
- duration_seconds: int
- level: int
- severity: string
- status: string

制限事項

  • 空のテーブル — テーブルにアイテムがない場合、スキーマ推論は警告しますがデプロイは空のスキーマで続行します

  • ネストされたオブジェクト — 複雑にネストされた構造は JSON 文字列として表現されます

  • 型の一貫性 — スキーマはサンプルデータに基づいています。データの型が一貫していない場合、一部のクエリが失敗する可能性があります


アーキテクチャ

┌─────────────────┐
│ Redash Query │
└────────┬────────┘
│ SQL Query

┌─────────────────────────────┐
│ Amazon Athena │
│ (unit-dev-analytics) │
└────────┬────────────────────┘


┌─────────────────────────────┐
│ Athena Data Catalog │
│ (Lambda Connector) │
└────────┬────────────────────┘
│ Invoke

┌─────────────────────────────┐
│ Lambda Function │
│ (AthenaDynamoDBConnector) │
└────────┬────────────────────┘
│ Read

┌─────────────────────────────┐
│ DynamoDB Table │
│ (Your Data) │
└─────────────────────────────┘


┌─────────────────────────────┐
│ S3 Spill Bucket │
│ (Large Result Sets) │
└─────────────────────────────┘


┌─────────────────────────────┐
│ S3 Results Bucket │
│ (Query Results) │
└─────────────────────────────┘

データフロー:

  1. Redash が SQL クエリを Athena に送信

  2. Athena は Glue カタログでテーブルメタデータを検索

  3. Athena は特定のカタログの Lambda コネクタを呼び出します

  4. Lambda は DynamoDB からデータを読み込みます

  5. Lambda は結果を返します (データが大きい場合は S3 spill バケットを使用)

  6. Athena は最終結果を S3 結果バケットに書き込みます

  7. Redash は結果を取得して表示します


トラブルシューティング

接続テスト失敗: S3 へのアクセス拒否

問題: IAM ユーザーが結果バケットに書き込めません

解決策: IAM ポリシーに S3 権限を追加 (IAM 権限セクションを参照)

クエリ実行に必要な権限不足

問題: Athena/Lambda/Glue/DynamoDB の権限がありません

解決策: IAM ユーザーに完全な RedashDynamoDBConnectorAccess ポリシーをアタッチ

SCHEMA_NOT_FOUND エラー

問題: カタログ名を指定せずにクエリしています

解決策: 完全修飾テーブル名を使用: "catalog"."database"."table"

COLUMN_NOT_FOUND エラー

問題: Glue テーブルにカラム定義がありません (自動スキーマ推論では発生しないはず)

解決策: テーブルを再デプロイ — ツールはスキーマを再推論します

Redash スキーマブラウザにテーブルが表示されない

問題: Redash データソースにカタログ ID が追加されていません

解決策: データソース設定の "Glue Data Catalog IDs" フィールドにカタログ ID を追加

クエリが遅い / タイムアウト

問題: 大きな DynamoDB テーブルの完全スキャン

解決策:

  • 一般的なクエリパターンのインデックスを DynamoDB テーブルに追加

  • クエリで LIMIT 句を使用

  • WHERE 条件を追加してスキャンサイズを削減

  • ツール設定で Lambda メモリを増やすことを検討


高度な設定

カスタムコネクタバージョン

src/types.ts を編集して AWS SAR コネクタのバージョンを変更:

connectorVersion: z.string().default('2022.47.1'); // このバージョンを変更

カスタム Lambda メモリ/タイムアウト

src/types.ts を編集:

lambdaMemory: z.number().min(512).max(10240).default(3008); // メモリを MB で変更

lambdaTimeout: z.number().min(60).max(900).default(900); // タイムアウトを秒で変更

カスタムライフサイクルポリシー

src/types.ts を編集:

spillBucketLifecycleDays: z.number().min(1).default(1); // spill データが削除されるまでの日数

resultsBucketLifecycleDays: z.number().min(1).default(30); // クエリ結果が削除されるまでの日数


開発

プロジェクト構造

redash-dynamodb-connector/
├── src/
│ ├── index.ts # CLI エントリポイント
│ ├── types.ts # 型定義とスキーマ
│ ├── aws/
│ │ ├── credentials.ts # AWS 認証情報プロバイダー
│ │ └── dynamodb.ts # DynamoDB 操作とスキーマ推論
│ ├── utils/
│ │ └── shared-arg.ts # 共有引数ユーティリティ
│ └── cdk/
│ ├── app.ts # CDK アプリケーションファクトリ
│ ├── stacks/
│ │ └── connector-stack.ts # メインスタック
│ └── constructs/
│ └── athena-connector.ts # コネクタコンストラクト
├── package.json
├── cdk.json # CDK 設定
├── tsconfig.json
└── README.md

ローカルで実行


# 依存関係をインストール
pnpm install

# リンターを実行
pnpm lint

# 型チェック
pnpm tsc

# テーブルをデプロイ
pnpm exec tsx src/index.ts add --table my-table --glue-database my-db

テスト

このツールは入力を検証しますが、自動化されたテストはまだ含まれていません。テストは実際のデプロイを通じて行われます。


コスト検討

クエリごとのコスト

  • Athena: スキャンされたデータ 1 TB あたり $5

  • Lambda: 100 万リクエストあたり $0.20 + 計算時間

  • DynamoDB: 消費されたリード容量ユニット

  • S3: ストレージ + リクエスト (最小限)

月額コスト (見積もり)

月に 1000 クエリ、1GB テーブルの場合:

  • Athena: 約 $0.005 (1GB × 1000 クエリ = 1TB × $5)

  • Lambda: 約 $0.20

  • DynamoDB: テーブルの RCU 構成に依存

  • S3: 1 ドル未満

合計: 通常使用で月額約 $1-5

コスト最適化のヒント

  • LIMIT 句を使用してスキャンデータを削減

  • SELECT * の代わりに特定のカラムをクエリ

  • よくクエリされるパターンのために DynamoDB インデックスを追加

  • S3 バケットに適切なライフサイクルポリシーを設定

  • Athena ワークグループを使用してコストを追跡および制限


制限事項

  1. 書き込み操作なし — ツールは読み取りクエリ (SELECT) のみをサポート

  2. スキャンパフォーマンス — 大きなテーブルでの完全テーブルスキャンは遅くなる可能性があります

  3. 型推論 — サンプルデータに基づいています。すべてのエッジケースをキャプチャしない可能性があります

  4. 更新なし — 既存のコネクタ設定を変更できません。削除して再作成する必要があります

  5. Lambda 並行性 — AWS Lambda 並行性制限の対象

  6. 結果サイズ — 大きな結果セットは S3 spill が必要 (自動的に処理)


よくある質問

Q: 1 つのクエリで複数の DynamoDB テーブルをクエリできますか? A: はい! 各テーブルの完全修飾テーブル名で JOIN 句を使用します。

Q: DynamoDB スキーマが変更された場合、コネクタを再作成する必要がありますか? A: はい、スキーマを再推論して Glue テーブルを更新するために add コマンドを再実行します。

Q: DynamoDB グローバルテーブルで使用できますか? A: はい、リージョン内のテーブルを指すだけです。各リージョンには独自のコネクタが必要です。

Q: コネクタを削除するにはどうすればよいですか? A: CloudFormation スタックを削除: aws cloudformation delete-stack --stack-name unit-dev-dynamodb-connector

Q: DynamoDB ストリームをクエリできますか? A: いいえ、このツールはストリームではなくテーブル自体のみをクエリします。

Q: DynamoDB DAX で使用できますか? A: いいえ、Lambda コネクタは DAX ではなく DynamoDB を直接クエリします。

Q: LocalStack または DynamoDB Local で動作しますか? A: いいえ、AWS Serverless Application Repository のコネクタに依存しているため動作しません。


リソース


ライセンス

ISC


貢献

Issue と Pull Request を歓迎します! 送信前にコードがリンターに合格していることを確認してください。


サポート

問題または質問がある場合:

  1. 上記のトラブルシューティングセクションを確認

  2. AWS CloudFormation スタックイベントをデプロイメントエラーで確認

  3. CloudWatch で Lambda 関数ログを確認

  4. このリポジトリで Issue を開く

Related Articles