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
このツールは次を実行します:
-
✅ テーブルが存在することを検証
-
✅ スキャンしてスキーマを推論 (すべてのカラムと型を自動検出)
-
✅ Lambda コネクタ関数を作成
-
✅ Athena データカタログを作成
-
✅ 検出されたスキーマで Glue テーブルを作成
-
✅ 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>
コマンドオプション
| オプション | 説明 | 必須 | デフォルト |
--table | DynamoDB テーブル名 | はい | - |
--glue-database | 既存 Glue データベース名 | いいえ | 新しいデータベースを作成 |
--catalog-name | カスタム Athena カタログ名 | いいえ | サニタイズされたテーブル名 |
--column-mapping | 混合ケースのカラムをマッピング | いいえ | - |
--product | 製品名 (unit/spring/plants) | いいえ | unit または $PRODUCT_NAME |
--env | 環境 (dev/stg/prod) | いいえ | dev または $ENV_NAME |
--region | AWS リージョン | いいえ | 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-spill | unit-dev-dynamodb-spill |
| 結果バケット | {product}-{env}-athena-results | unit-dev-athena-results |
| ワークグループ | {product}-{env}-analytics | unit-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 権限を追加するステップ
-
IAM コンソール → ポリシー に移動
-
ポリシーを作成 → JSON タブをクリック
-
上記のポリシーを貼り付け (必要に応じてリージョン/アカウント ID を調整)
-
名前をつけます:
RedashDynamoDBConnectorAccess -
ポリシーを作成
-
Redash IAM ユーザー → 権限 → ポリシーをアタッチ に移動
-
RedashDynamoDBConnectorAccessを検索してアタッチ
自動スキーマ推論
このツールは以下の方法で DynamoDB テーブルスキーマを自動的に検出します:
-
サンプルアイテムをスキャン — テーブルから 10 アイテムを読み込み (設定可能)
-
型を検出 — JavaScript 型を Athena/Glue 型にマッピング:
string→stringnumber(整数) →intnumber(小数) →doubleboolean→booleanarray→array<type>object→string(JSON として保存)
-
スキーマを作成 — 検出されたカラムで Glue テーブルを生成
-
混合型を処理 — カラムがアイテム全体で複数の型を持つ場合、デフォルトは
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) │
└─────────────────────────────┘
データフロー:
-
Redash が SQL クエリを Athena に送信
-
Athena は Glue カタログでテーブルメタデータを検索
-
Athena は特定のカタログの Lambda コネクタを呼び出します
-
Lambda は DynamoDB からデータを読み込みます
-
Lambda は結果を返します (データが大きい場合は S3 spill バケットを使用)
-
Athena は最終結果を S3 結果バケットに書き込みます
-
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 ワークグループを使用してコストを追跡および制限
制限事項
-
書き込み操作なし — ツールは読み取りクエリ (SELECT) のみをサポート
-
スキャンパフォーマンス — 大きなテーブルでの完全テーブルスキャンは遅くなる可能性があります
-
型推論 — サンプルデータに基づいています。すべてのエッジケースをキャプチャしない可能性があります
-
更新なし — 既存のコネクタ設定を変更できません。削除して再作成する必要があります
-
Lambda 並行性 — AWS Lambda 並行性制限の対象
-
結果サイズ — 大きな結果セットは 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 を歓迎します! 送信前にコードがリンターに合格していることを確認してください。
サポート
問題または質問がある場合:
-
上記のトラブルシューティングセクションを確認
-
AWS CloudFormation スタックイベントをデプロイメントエラーで確認
-
CloudWatch で Lambda 関数ログを確認
-
このリポジトリで Issue を開く