Aurora DSQL

Amazon Aurora DSQL 総合ガイド — 機能・アーキテクチャ・設定の全容

本ドキュメントは Amazon Aurora DSQL の全体像を俯瞰し、アーキテクチャの深部から運用上の設定例までを網羅的に解説する。対象読者はデータベース技術に一定の知見を持つエンジニアを想定している。


1. はじめに — Aurora DSQL とは何か

1.1 概要

Amazon Aurora DSQL は、2024 年の AWS re:Invent で発表された サーバーレス型の分散 SQL データベースサービス である。「DSQL」は Distributed SQL の略称であり、従来の Aurora が単一リージョンのリレーショナルデータベースとして設計されていたのに対し、Aurora DSQL は マルチリージョンにまたがるアクティブ-アクティブ構成 を前提とした全く新しいアーキテクチャで構築されている。

Aurora DSQL の主要な特徴は以下のとおりである。

  • サーバーレス: インフラストラクチャの管理が不要で、ワークロードに応じて自動的にスケーリングする
  • PostgreSQL 互換: PostgreSQL のワイヤプロトコルに互換性があり、既存の PostgreSQL ドライバやツールがそのまま利用可能
  • マルチリージョン・アクティブ-アクティブ: 複数の AWS リージョンにまたがってデータを分散し、各リージョンで読み書きが可能
  • 強一貫性(Strong Consistency): 分散環境においてもトランザクションの強一貫性を保証
  • 99.999% の可用性 SLA: マルチリージョン構成において 99.999%(ファイブナイン)の可用性を提供
  • ゼロ管理運用: パッチ適用、バックアップ、フェイルオーバーなどの運用タスクが完全に自動化
  • Optimistic Concurrency Control(OCC): ロックフリーのトランザクション制御により高いスループットを実現

1.2 Aurora DSQL 誕生の背景

従来の Amazon Aurora は MySQL および PostgreSQL 互換のリレーショナルデータベースとして広く利用されてきたが、以下のような課題が存在していた。

  1. リージョン間のアクティブ-アクティブ構成の困難さ: Aurora Global Database はリードレプリカをリモートリージョンに配置できるが、書き込みは単一のプライマリリージョンに限定されていた
  2. グローバル分散アプリケーションでのレイテンシ: リモートリージョンからの書き込みはプライマリリージョンへの転送が必要であり、レイテンシが増大した
  3. フェイルオーバー時のデータ損失リスク: 非同期レプリケーションに起因するデータ損失の可能性
  4. 運用の複雑さ: クラスタサイズの決定、レプリケーションの管理、パッチ適用など、運用負荷が高い

Aurora DSQL はこれらの課題を根本的に解決するために、Google Spanner や CockroachDB といった先行する分散 SQL データベースの設計思想を参考にしつつ、AWS のマネージドサービスとしての利便性を最大限に活かす形で設計された。

1.3 Aurora DSQL と他の AWS データベースサービスとの位置づけ

AWS は多数のデータベースサービスを提供しているが、Aurora DSQL は以下のように位置づけられる。

サービスタイプリージョン書き込みモデル一貫性SQL 互換性
Amazon RDSリレーショナルシングルシングルライター強一貫性MySQL/PostgreSQL/Oracle 等
Amazon Auroraリレーショナルシングル(Global DB でマルチ)シングルライター強一貫性MySQL/PostgreSQL
Amazon Aurora DSQL分散 SQLマルチマルチライター強一貫性PostgreSQL
Amazon DynamoDBNoSQL(KVS)マルチ(Global Tables)マルチライター結果整合性/強一貫性独自 API
Amazon DynamoDB Global TablesNoSQL(KVS)マルチマルチライター結果整合性独自 API
Amazon Keyspacesワイドカラムマルチマルチライター結果整合性/強一貫性CQL(Cassandra 互換)

Aurora DSQL は「分散 SQL でありながらサーバーレス」という独自のポジションを占めている。DynamoDB のようなスケーラビリティとサーバーレスの利便性を持ちながら、リレーショナルデータベースの SQL 互換性と強一貫性を兼ね備えている。

1.4 主要な利用シナリオ

Aurora DSQL が特に適しているユースケースは以下のとおりである。

  1. グローバル分散アプリケーション: 世界中のユーザーに低レイテンシのデータアクセスを提供する必要があるアプリケーション(EC サイト、ゲーム、SaaS プラットフォームなど)
  2. 高可用性が必須のミッションクリティカルシステム: 決済処理、金融取引、医療記録管理など、ダウンタイムが許容されないシステム
  3. マルチリージョン DR(災害復旧): リージョン障害時にも自動的にフェイルオーバーし、データ損失なく運用を継続する必要があるシステム
  4. 運用負荷を最小化したいチーム: データベースの運用管理に人的リソースを割けない小規模チームやスタートアップ
  5. 読み書きの地理的分散: 各リージョンのユーザーが地理的に近いエンドポイントで読み書きを行い、低レイテンシを実現したいケース

1.5 制約事項と考慮点

Aurora DSQL には以下の制約事項がある(2025 年時点)。

  • PostgreSQL の完全互換ではない: ストアドプロシージャ、トリガー、外部キー制約、シーケンスなど一部の PostgreSQL 機能はサポートされていない
  • Optimistic Concurrency Control に基づくトランザクション: 高い競合率のワークロードではトランザクションの再試行が頻発する可能性がある
  • 利用可能リージョンの制限: 初期段階では利用可能なリージョンが限定されている
  • 料金体系: サーバーレスの特性上、従量課金であり、ワークロードパターンによってはコストが予測しにくい場合がある

2. アーキテクチャの全体像

2.1 設計思想

Aurora DSQL のアーキテクチャは以下の設計原則に基づいている。

  1. コンピュートとストレージの分離: 処理層とデータ格納層を完全に分離し、各層を独立にスケーリング可能とする
  2. サーバーレスファースト: ユーザーがインフラストラクチャのサイジングや管理を行う必要がないよう設計
  3. マルチリージョン前提: 単一リージョンでの動作を後からマルチリージョンに拡張するのではなく、最初からマルチリージョンを前提として設計
  4. ロックフリー・トランザクション: 従来のペシミスティック・ロックではなく、Optimistic Concurrency Control を採用し、高い並行性を実現
  5. AWS サービスとの深い統合: IAM、CloudWatch、CloudTrail などの AWS サービスとネイティブに統合

2.2 全体アーキテクチャ図

Aurora DSQL のアーキテクチャは大きく 4 つのコンポーネントで構成される。

┌─────────────────────────────────────────────────────────────────┐
│                      Client Application                         │
│              (PostgreSQL ドライバ / psql / ORM)                  │
└──────────────────────────┬──────────────────────────────────────┘
                           │ PostgreSQL Wire Protocol
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Query Processor (QP)                          │
│  ┌─────────┐  ┌──────────────┐  ┌──────────────┐               │
│  │ Parser  │→│  Optimizer    │→│  Executor     │               │
│  └─────────┘  └──────────────┘  └──────────────┘               │
│           サーバーレス・オートスケーリング                          │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                 Transaction Processor (TP)                       │
│  ┌──────────────────┐  ┌────────────────────┐                   │
│  │  OCC Engine      │  │  Conflict Resolver │                   │
│  └──────────────────┘  └────────────────────┘                   │
│         Optimistic Concurrency Control                          │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                   Storage Layer                                  │
│  ┌─────────────────┐  ┌────────────────────┐                    │
│  │ Journal (WAL)   │  │  Distributed       │                    │
│  │                 │  │  Storage            │                    │
│  └─────────────────┘  └────────────────────┘                    │
│          マルチ AZ / マルチリージョン複製                          │
└─────────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌─────────────────────────────────────────────────────────────────┐
│                  Adjudicator                                     │
│  ┌──────────────────────────────────────────┐                   │
│  │  グローバル・トランザクション調停            │                   │
│  │  (書き込み競合の最終判定)                   │                   │
│  └──────────────────────────────────────────┘                   │
│      クロスリージョン・コンセンサス                                │
└─────────────────────────────────────────────────────────────────┘

2.3 Query Processor(クエリプロセッサ)

Query Processor は Aurora DSQL のフロントエンドコンポーネントであり、クライアントからの接続を受け付け、SQL クエリの解析・最適化・実行を担当する。

主要な機能

  • 接続管理: PostgreSQL ワイヤプロトコルによるクライアント接続の管理
  • SQL パース: SQL 文の構文解析と抽象構文木(AST)の生成
  • クエリ最適化: コストベースオプティマイザによる実行計画の最適化
  • クエリ実行: 最適化された実行計画に基づくクエリの実行
  • 結果セットの返却: クライアントへのクエリ結果の返却

サーバーレスの特性

Query Processor はサーバーレスで動作し、接続数やクエリの負荷に応じて自動的にスケーリングする。ユーザーはインスタンスサイズやノード数を指定する必要がない。AWS 内部のリソースプールからオンデマンドでコンピュートリソースが割り当てられる。

2.4 Transaction Processor(トランザクションプロセッサ)

Transaction Processor は Aurora DSQL のトランザクション管理を担当するコンポーネントである。

Optimistic Concurrency Control(OCC)

Aurora DSQL は 楽観的同時実行制御(OCC) を採用している。これは従来のリレーショナルデータベースで一般的な悲観的ロック(Pessimistic Locking)とは根本的に異なるアプローチである。

OCC の動作フロー:

1. BEGIN TRANSACTION
   │
   ▼
2. READ PHASE(読み取りフェーズ)
   - データを読み取り、読み取ったデータのバージョン(タイムスタンプ)を記録
   - ロックは取得しない
   │
   ▼
3. WRITE PHASE(書き込みフェーズ)
   - 変更をローカルバッファに書き込む
   - まだストレージには反映しない
   │
   ▼
4. VALIDATION PHASE(検証フェーズ)
   - トランザクション開始時に読み取ったデータが、
     コミット時点で他のトランザクションによって変更されていないか検証
   │
   ├─→ 競合なし → COMMIT(変更をストレージに反映)
   │
   └─→ 競合あり → ABORT(トランザクションを中断・再試行)

OCC のメリット:

  • ロックを取得しないため、読み取り操作がブロックされない
  • 高い並行性を実現でき、スループットが向上する
  • デッドロックが発生しない

OCC のデメリット:

  • 書き込み競合が頻発するワークロードでは、トランザクションの再試行が多発し性能が低下する
  • アプリケーション側で再試行ロジックの実装が必要

2.5 Storage Layer(ストレージレイヤー)

Aurora DSQL のストレージレイヤーは、以下の 2 つの主要コンポーネントで構成される。

Journal(ジャーナル)

Journal は WAL(Write-Ahead Log)に相当するコンポーネントであり、コミットされたトランザクションのログを永続化する。Journal は複数の AZ にまたがって複製され、高い耐久性を確保する。

Distributed Storage(分散ストレージ)

実際のデータを格納する分散ストレージであり、データは自動的にシャーディングされ、複数の AZ に複製される。マルチリージョン構成の場合、データはリージョン間でも複製される。

2.6 Adjudicator(アジュディケーター)

Adjudicator は Aurora DSQL の最も特徴的なコンポーネントであり、グローバル・トランザクション調停 を担当する。

役割

  • 複数のリージョンから同時に同じデータに対する書き込みが行われた場合、競合を検出し、どのトランザクションをコミットするかを決定する
  • グローバルな一貫性を保証するためのクロスリージョン・コンセンサスを実行する
  • トランザクションのコミット順序(シリアライザビリティ)を保証する

動作メカニズム

Adjudicator は各リージョンの Transaction Processor からのコミットリクエストを受け取り、以下の処理を行う。

  1. 競合検出: 同一データに対する並行書き込みの有無を検出
  2. 順序決定: 競合がある場合、タイムスタンプに基づいてコミット順序を決定
  3. コミット判定: 勝者のトランザクションをコミットし、敗者のトランザクションにアボートを通知
  4. 結果の伝播: コミット結果を各リージョンの Storage Layer に伝播

2.7 マルチリージョンアーキテクチャ

Aurora DSQL のマルチリージョン構成は以下のように動作する。

                    Region A                              Region B
         ┌──────────────────────┐              ┌──────────────────────┐
         │  ┌────────────────┐  │              │  ┌────────────────┐  │
         │  │ Query Processor│  │              │  │ Query Processor│  │
         │  └───────┬────────┘  │              │  └───────┬────────┘  │
         │          │           │              │          │           │
         │  ┌───────▼────────┐  │              │  ┌───────▼────────┐  │
         │  │  Transaction   │  │              │  │  Transaction   │  │
         │  │  Processor     │  │              │  │  Processor     │  │
         │  └───────┬────────┘  │              │  └───────┬────────┘  │
         │          │           │              │          │           │
         │  ┌───────▼────────┐  │              │  ┌───────▼────────┐  │
         │  │  Storage Layer │◄─┼──────────────┼─►│  Storage Layer │  │
         │  └────────────────┘  │              │  └────────────────┘  │
         └──────────┬───────────┘              └──────────┬───────────┘
                    │                                     │
                    └──────────┐       ┌──────────────────┘
                               ▼       ▼
                    ┌──────────────────────┐
                    │    Adjudicator       │
                    │ (グローバル調停)       │
                    └──────────────────────┘

シングルリージョンモード では、Adjudicator は同一リージョン内で動作し、リージョン内での一貫性を保証する。この場合、レイテンシは非常に低く抑えられる。

マルチリージョンモード では、Adjudicator はリージョン間のネットワークを介して通信を行い、グローバルな一貫性を保証する。クロスリージョン通信が発生するため、書き込みレイテンシはシングルリージョンモードより高くなる。

2.8 時刻同期とタイムスタンプ

Aurora DSQL のトランザクション一貫性は、正確な時刻同期に依存する。AWS は Amazon Time Sync Service を提供しており、これは Google Spanner の TrueTime に相当するサービスである。

  • Amazon Time Sync Service: GPS と原子時計を使用した高精度な時刻同期サービス
  • ClockBound: EC2 インスタンス上で時刻の不確実性の範囲(エラーバウンド)を取得可能
  • トランザクションタイムスタンプ: 各トランザクションに一意のタイムスタンプを付与し、グローバルな順序付けに使用

これにより、リージョン間のデータ一貫性がタイムスタンプに基づいて保証される。


3. クラスタの作成と基本設定

3.1 シングルリージョンクラスタの作成

AWS CLI による作成

# シングルリージョンクラスタの作成
aws dsql create-cluster \
  --deletion-protection-enabled \
  --tags Key=Environment,Value=Production Key=Team,Value=Platform

# クラスタの状態確認
aws dsql get-cluster \
  --identifier <cluster-identifier>

レスポンス例:

{
    "identifier": "abc123def456",
    "arn": "arn:aws:dsql:us-east-1:123456789012:cluster/abc123def456",
    "status": "ACTIVE",
    "creationTime": "2025-01-15T10:30:00Z",
    "deletionProtectionEnabled": true
}

AWS CloudFormation による作成

AWSTemplateFormatVersion: '2010-09-09'
Description: Aurora DSQL Single-Region Cluster

Resources:
  DSQLCluster:
    Type: AWS::DSQL::Cluster
    Properties:
      DeletionProtectionEnabled: true
      Tags:
        - Key: Environment
          Value: Production
        - Key: Team
          Value: Platform

Outputs:
  ClusterIdentifier:
    Description: The cluster identifier
    Value: !GetAtt DSQLCluster.Identifier
  ClusterEndpoint:
    Description: The cluster endpoint
    Value: !GetAtt DSQLCluster.Endpoint

Terraform による作成

# Terraform Provider の設定
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = ">= 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# Aurora DSQL クラスタの作成
resource "aws_dsql_cluster" "main" {
  deletion_protection_enabled = true

  tags = {
    Environment = "Production"
    Team        = "Platform"
  }
}

# 出力
output "cluster_identifier" {
  value = aws_dsql_cluster.main.identifier
}

output "cluster_endpoint" {
  value = aws_dsql_cluster.main.endpoint
}

3.2 マルチリージョンクラスタの作成

マルチリージョンクラスタは、リンクされたクラスタ を複数のリージョンに作成することで構成される。各リージョンに 1 つのクラスタが存在し、それらがリンクされることでマルチリージョンクラスタとして動作する。

AWS CLI による作成

# リージョン A(us-east-1)にウィットネスリージョンを指定してクラスタを作成
aws dsql create-multi-region-clusters \
  --linked-region-list us-east-1 us-west-2 \
  --witness-region eu-west-1 \
  --cluster-properties '{"us-east-1": {"deletionProtectionEnabled": true, "tags": {"Environment": "Production"}}, "us-west-2": {"deletionProtectionEnabled": true, "tags": {"Environment": "Production"}}}'

レスポンス例:

{
    "linkedClusterArns": [
        "arn:aws:dsql:us-east-1:123456789012:cluster/abc123",
        "arn:aws:dsql:us-west-2:123456789012:cluster/def456"
    ]
}

マルチリージョン構成の概念図

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   us-east-1     │     │   us-west-2     │     │   eu-west-1     │
│   (Linked)      │◄───►│   (Linked)      │◄───►│  (Witness)      │
│                 │     │                 │     │                 │
│ - Read/Write    │     │ - Read/Write    │     │ - 調停のみ       │
│ - データ格納    │     │ - データ格納    │     │ - データ格納なし  │
└─────────────────┘     └─────────────────┘     └─────────────────┘

Witness リージョン は、データの格納やクエリ処理は行わず、トランザクションの調停(Adjudicator)にのみ参加する軽量なリージョンである。マルチリージョン構成において、2 つのリンクリージョン間の障害時にデータの一貫性を保証するための投票メンバーとして機能する。

Terraform による作成

provider "aws" {
  alias  = "us_east_1"
  region = "us-east-1"
}

provider "aws" {
  alias  = "us_west_2"
  region = "us-west-2"
}

# マルチリージョンクラスタの作成
resource "aws_dsql_cluster" "east" {
  provider = aws.us_east_1

  deletion_protection_enabled = true

  multi_region_cluster_configuration {
    linked_region_list = ["us-east-1", "us-west-2"]
    witness_region     = "eu-west-1"
  }

  tags = {
    Environment = "Production"
    Role        = "Primary"
  }
}

resource "aws_dsql_cluster" "west" {
  provider = aws.us_west_2

  deletion_protection_enabled = true

  multi_region_cluster_configuration {
    linked_region_list = ["us-east-1", "us-west-2"]
    witness_region     = "eu-west-1"
  }

  tags = {
    Environment = "Production"
    Role        = "Secondary"
  }
}

3.3 削除保護とクラスタの管理

削除保護の設定と解除

# 削除保護の有効化
aws dsql update-cluster \
  --identifier <cluster-identifier> \
  --deletion-protection-enabled

# 削除保護の無効化
aws dsql update-cluster \
  --identifier <cluster-identifier> \
  --no-deletion-protection-enabled

# クラスタの削除(削除保護が無効の場合のみ)
aws dsql delete-cluster \
  --identifier <cluster-identifier>

クラスタの一覧取得

# 全クラスタの一覧
aws dsql list-clusters

# ページネーションを使用した一覧取得
aws dsql list-clusters \
  --max-results 10 \
  --next-token <token>

4. 認証とアクセス制御

4.1 IAM ベースの認証

Aurora DSQL は IAM(Identity and Access Management)認証 を使用してデータベースへのアクセスを制御する。従来のユーザー名/パスワードベースの認証ではなく、AWS IAM の認証メカニズムを活用する。

認証トークンの生成

# 管理者用認証トークンの生成
aws dsql generate-db-connect-admin-auth-token \
  --hostname <cluster-endpoint> \
  --region us-east-1 \
  --expires-in 900

# 一般ユーザー用認証トークンの生成
aws dsql generate-db-connect-auth-token \
  --hostname <cluster-endpoint> \
  --region us-east-1 \
  --expires-in 900

IAM ポリシーの設定

管理者アクセス用ポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "dsql:DbConnectAdmin",
      "Resource": "arn:aws:dsql:us-east-1:123456789012:cluster/abc123def456"
    }
  ]
}

一般ユーザーアクセス用ポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "dsql:DbConnect",
      "Resource": "arn:aws:dsql:us-east-1:123456789012:cluster/abc123def456"
    }
  ]
}

クラスタ管理用ポリシー:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dsql:CreateCluster",
        "dsql:GetCluster",
        "dsql:ListClusters",
        "dsql:UpdateCluster",
        "dsql:DeleteCluster",
        "dsql:CreateMultiRegionClusters",
        "dsql:DeleteMultiRegionClusters",
        "dsql:ListTagsForResource",
        "dsql:TagResource",
        "dsql:UntagResource"
      ],
      "Resource": "*"
    }
  ]
}

4.2 データベースロールの管理

Aurora DSQL ではデータベースレベルのロール管理が PostgreSQL 互換の SQL で行える。

-- 管理者として接続後、ロールの作成
-- 注意: Aurora DSQL ではパスワードベースの認証は使用しない
-- ロール名は IAM ユーザー/ロール名と対応づけられる

-- カスタムロールの作成
CREATE ROLE app_readonly;
CREATE ROLE app_readwrite;

-- ロールへの権限付与
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_readwrite;

-- IAM ユーザーにロールを付与
-- IAM ユーザー名でデータベースユーザーが自動的に作成される
GRANT app_readwrite TO "iam_user_name";

4.3 スキーマレベルのアクセス制御

-- スキーマの作成
CREATE SCHEMA app_data;
CREATE SCHEMA analytics;

-- スキーマへのアクセス権限
GRANT USAGE ON SCHEMA app_data TO app_readwrite;
GRANT USAGE ON SCHEMA analytics TO app_readonly;

-- スキーマ内のテーブルへの権限
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA app_data TO app_readwrite;
GRANT SELECT ON ALL TABLES IN SCHEMA analytics TO app_readonly;

-- 将来作成されるテーブルへのデフォルト権限
ALTER DEFAULT PRIVILEGES IN SCHEMA app_data
  GRANT ALL PRIVILEGES ON TABLES TO app_readwrite;

ALTER DEFAULT PRIVILEGES IN SCHEMA analytics
  GRANT SELECT ON TABLES TO app_readonly;

4.4 VPC エンドポイントによるネットワークアクセス制御

Aurora DSQL は VPC エンドポイント(PrivateLink)を通じたプライベートアクセスもサポートする。

# VPC エンドポイントの作成
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-0123456789abcdef0 \
  --service-name com.amazonaws.us-east-1.dsql \
  --vpc-endpoint-type Interface \
  --subnet-ids subnet-abc123 subnet-def456 \
  --security-group-ids sg-0123456789abcdef0

# セキュリティグループの設定(PostgreSQL ポート 5432 を許可)
aws ec2 authorize-security-group-ingress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 5432 \
  --source-group sg-app-servers

5. データベース操作と SQL サポート

5.1 接続方法

psql による接続

# 認証トークンの取得と psql での接続
export PGPASSWORD=$(aws dsql generate-db-connect-admin-auth-token \
  --hostname <cluster-endpoint> \
  --region us-east-1 \
  --expires-in 3600)

psql "host=<cluster-endpoint> \
      port=5432 \
      dbname=postgres \
      user=admin \
      sslmode=require"

Python(psycopg2)による接続

import boto3
import psycopg2

def get_dsql_connection(cluster_endpoint, region='us-east-1'):
    """Aurora DSQL への接続を確立する"""
    # DSQL クライアントの作成
    client = boto3.client('dsql', region_name=region)
    
    # 認証トークンの生成
    token = client.generate_db_connect_admin_auth_token(
        Hostname=cluster_endpoint,
        Region=region,
        ExpiresIn=3600
    )
    
    # PostgreSQL 接続
    conn = psycopg2.connect(
        host=cluster_endpoint,
        port=5432,
        dbname='postgres',
        user='admin',
        password=token,
        sslmode='require'
    )
    
    return conn

# 使用例
cluster_endpoint = "abc123def456.dsql.us-east-1.on.aws"
conn = get_dsql_connection(cluster_endpoint)

try:
    with conn.cursor() as cur:
        cur.execute("SELECT version()")
        version = cur.fetchone()
        print(f"Connected to: {version[0]}")
finally:
    conn.close()

Python(SQLAlchemy)による接続

from sqlalchemy import create_engine, text
import boto3

def create_dsql_engine(cluster_endpoint, region='us-east-1'):
    """SQLAlchemy エンジンを作成する"""
    client = boto3.client('dsql', region_name=region)
    
    token = client.generate_db_connect_admin_auth_token(
        Hostname=cluster_endpoint,
        Region=region,
        ExpiresIn=3600
    )
    
    # SQLAlchemy エンジンの作成
    engine = create_engine(
        f"postgresql+psycopg2://admin:{token}@{cluster_endpoint}:5432/postgres",
        connect_args={"sslmode": "require"},
        pool_size=10,
        max_overflow=20,
        pool_pre_ping=True,  # 接続の有効性チェック
        pool_recycle=1800     # 30 分で接続を再生成(トークン期限対策)
    )
    
    return engine

# 使用例
engine = create_dsql_engine("abc123def456.dsql.us-east-1.on.aws")

with engine.connect() as conn:
    result = conn.execute(text("SELECT 1"))
    print(result.fetchone())

Java(JDBC)による接続

import software.amazon.awssdk.services.dsql.DsqlUtilities;
import software.amazon.awssdk.regions.Region;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;

public class DSQLConnection {
    
    public static Connection getConnection(String clusterEndpoint, String region) throws Exception {
        // 認証トークンの生成
        DsqlUtilities utilities = DsqlUtilities.builder()
            .region(Region.of(region))
            .build();
        
        String token = utilities.generateDbConnectAdminAuthToken(builder -> builder
            .hostname(clusterEndpoint)
            .region(Region.of(region))
            .expiresIn(java.time.Duration.ofHours(1))
        );
        
        // JDBC 接続
        Properties props = new Properties();
        props.setProperty("user", "admin");
        props.setProperty("password", token);
        props.setProperty("ssl", "true");
        props.setProperty("sslmode", "require");
        
        String url = String.format("jdbc:postgresql://%s:5432/postgres", clusterEndpoint);
        return DriverManager.getConnection(url, props);
    }
    
    public static void main(String[] args) throws Exception {
        String endpoint = "abc123def456.dsql.us-east-1.on.aws";
        
        try (Connection conn = getConnection(endpoint, "us-east-1");
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT version()")) {
            
            if (rs.next()) {
                System.out.println("Connected to: " + rs.getString(1));
            }
        }
    }
}

Node.js(pg)による接続

const { DsqlSigner } = require('@aws-sdk/dsql-signer');
const { Client } = require('pg');

async function connectToDSQL(clusterEndpoint, region = 'us-east-1') {
    // 認証トークンの生成
    const signer = new DsqlSigner({
        hostname: clusterEndpoint,
        region: region,
    });
    
    const token = await signer.getDbConnectAdminAuthToken();
    
    // PostgreSQL クライアントの作成
    const client = new Client({
        host: clusterEndpoint,
        port: 5432,
        database: 'postgres',
        user: 'admin',
        password: token,
        ssl: { rejectUnauthorized: true },
    });
    
    await client.connect();
    return client;
}

// 使用例
(async () => {
    const client = await connectToDSQL('abc123def456.dsql.us-east-1.on.aws');
    
    try {
        const result = await client.query('SELECT version()');
        console.log('Connected to:', result.rows[0].version);
    } finally {
        await client.end();
    }
})();

5.2 サポートされる SQL 機能

Aurora DSQL は PostgreSQL 互換の SQL をサポートしているが、分散データベースとしての特性から一部の機能に制限がある。

サポートされるデータ型

カテゴリデータ型
数値型smallint, integer, bigint, decimal, numeric, real, double precision, serial, bigserial
文字列型char(n), varchar(n), text
バイナリ型bytea
日時型date, time, timestamp, timestamp with time zone, interval
ブーリアン型boolean
UUID 型uuid
JSON 型json, jsonb
配列型上記の型の配列(例: integer[], text[]
ネットワーク型inet, cidr, macaddr

DDL(データ定義言語)

-- テーブルの作成
CREATE TABLE users (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email       VARCHAR(255) NOT NULL UNIQUE,
    name        VARCHAR(100) NOT NULL,
    profile     JSONB,
    created_at  TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at  TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- インデックスの作成
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_users_name ON users (name);
CREATE INDEX idx_users_profile ON users USING GIN (profile);

-- テーブルの変更
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;

-- テーブルの削除
DROP TABLE IF EXISTS users CASCADE;

DML(データ操作言語)

-- データの挿入
INSERT INTO users (email, name, profile)
VALUES (
    'user@example.com',
    'Taro Yamada',
    '{"department": "Engineering", "level": "Senior"}'::jsonb
)
RETURNING id, email, created_at;

-- データの更新
UPDATE users
SET name = 'Taro Tanaka',
    updated_at = NOW()
WHERE email = 'user@example.com'
RETURNING id, name, updated_at;

-- データの削除
DELETE FROM users
WHERE created_at < NOW() - INTERVAL '1 year'
RETURNING id, email;

-- UPSERT(INSERT ON CONFLICT)
INSERT INTO users (id, email, name, profile)
VALUES (
    'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
    'user@example.com',
    'Taro Yamada',
    '{"department": "Engineering"}'::jsonb
)
ON CONFLICT (email)
DO UPDATE SET
    name = EXCLUDED.name,
    profile = EXCLUDED.profile,
    updated_at = NOW()
RETURNING *;

クエリ機能

-- 基本的な SELECT
SELECT id, email, name, profile->>'department' AS department
FROM users
WHERE profile @> '{"level": "Senior"}'::jsonb
ORDER BY created_at DESC
LIMIT 10 OFFSET 0;

-- JOIN
SELECT u.name, o.order_id, o.total_amount
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.created_at >= NOW() - INTERVAL '30 days'
ORDER BY o.total_amount DESC;

-- 集計関数
SELECT
    profile->>'department' AS department,
    COUNT(*) AS user_count,
    AVG(EXTRACT(EPOCH FROM (NOW() - created_at)) / 86400) AS avg_days_since_creation
FROM users
GROUP BY profile->>'department'
HAVING COUNT(*) > 5
ORDER BY user_count DESC;

-- ウィンドウ関数
SELECT
    name,
    profile->>'department' AS department,
    created_at,
    ROW_NUMBER() OVER (PARTITION BY profile->>'department' ORDER BY created_at) AS dept_rank
FROM users;

-- CTE(Common Table Expressions)
WITH recent_orders AS (
    SELECT user_id, COUNT(*) AS order_count, SUM(total_amount) AS total_spent
    FROM orders
    WHERE created_at >= NOW() - INTERVAL '90 days'
    GROUP BY user_id
)
SELECT u.name, u.email, ro.order_count, ro.total_spent
FROM users u
JOIN recent_orders ro ON u.id = ro.user_id
WHERE ro.total_spent > 10000
ORDER BY ro.total_spent DESC;

-- サブクエリ
SELECT name, email
FROM users
WHERE id IN (
    SELECT user_id
    FROM orders
    GROUP BY user_id
    HAVING SUM(total_amount) > 50000
);

5.3 サポートされない機能(制限事項)

以下の PostgreSQL 機能は Aurora DSQL ではサポートされていない。

機能状態代替手段
ストアドプロシージャ非サポートアプリケーション層で実装
トリガー非サポートアプリケーション層のイベント駆動処理
外部キー制約非サポートアプリケーション層で参照整合性を管理
シーケンス非サポートUUID + gen_random_uuid() を使用
ユーザー定義型非サポートJSONB で柔軟なスキーマを実現
ビュー制限付きサポート基本的な VIEW は使用可能
マテリアライズドビュー非サポートアプリケーション層でキャッシュ
一時テーブル非サポートCTE やアプリケーション層のキャッシュ
LISTEN/NOTIFY非サポートAmazon SNS/SQS を使用
拡張機能(Extensions)非サポートビルトイン機能のみ使用可能
テーブルパーティショニング非サポート自動シャーディングに依存
カスタム集計関数非サポートアプリケーション層で実装

5.4 トランザクション管理

基本的なトランザクション

-- 明示的なトランザクション
BEGIN;

INSERT INTO accounts (id, name, balance)
VALUES ('acc-001', 'Alice', 10000);

INSERT INTO accounts (id, name, balance)
VALUES ('acc-002', 'Bob', 5000);

COMMIT;

OCC を考慮したリトライパターン(Python)

import psycopg2
import time
import random

def execute_with_retry(conn_func, operation, max_retries=5, base_delay=0.1):
    """
    OCC の競合によるトランザクション失敗に対するリトライロジック
    
    Args:
        conn_func: 接続を返す関数
        operation: 実行するトランザクション操作(connection を引数に取る関数)
        max_retries: 最大リトライ回数
        base_delay: 基本待機時間(秒)
    """
    for attempt in range(max_retries):
        conn = conn_func()
        try:
            conn.autocommit = False
            result = operation(conn)
            conn.commit()
            return result
        except psycopg2.errors.SerializationFailure as e:
            # OCC 競合による失敗 -- リトライ
            conn.rollback()
            if attempt < max_retries - 1:
                # 指数バックオフ + ジッター
                delay = base_delay * (2 ** attempt) + random.uniform(0, 0.1)
                print(f"Serialization failure (attempt {attempt + 1}/{max_retries}). "
                      f"Retrying in {delay:.3f}s...")
                time.sleep(delay)
            else:
                raise Exception(f"Transaction failed after {max_retries} retries: {e}")
        except Exception as e:
            conn.rollback()
            raise e
        finally:
            conn.close()

# 使用例: 口座間の送金
def transfer_funds(conn):
    """口座間送金のトランザクション"""
    with conn.cursor() as cur:
        # 送金元の残高確認
        cur.execute(
            "SELECT balance FROM accounts WHERE id = %s",
            ('acc-001',)
        )
        sender_balance = cur.fetchone()[0]
        
        if sender_balance < 1000:
            raise ValueError("Insufficient balance")
        
        # 送金元から引き落とし
        cur.execute(
            "UPDATE accounts SET balance = balance - %s WHERE id = %s",
            (1000, 'acc-001')
        )
        
        # 送金先に入金
        cur.execute(
            "UPDATE accounts SET balance = balance + %s WHERE id = %s",
            (1000, 'acc-002')
        )
        
        return "Transfer completed"

# リトライ付きで実行
result = execute_with_retry(
    conn_func=lambda: get_dsql_connection("abc123.dsql.us-east-1.on.aws"),
    operation=transfer_funds,
    max_retries=5
)
print(result)

Java でのリトライパターン

import java.sql.*;
import java.util.concurrent.TimeUnit;

public class DSQLTransactionRetry {
    
    private static final int MAX_RETRIES = 5;
    private static final long BASE_DELAY_MS = 100;
    
    @FunctionalInterface
    public interface TransactionOperation<T> {
        T execute(Connection conn) throws SQLException;
    }
    
    public static <T> T executeWithRetry(
            String endpoint, 
            String region,
            TransactionOperation<T> operation) throws Exception {
        
        for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
            try (Connection conn = DSQLConnection.getConnection(endpoint, region)) {
                conn.setAutoCommit(false);
                
                try {
                    T result = operation.execute(conn);
                    conn.commit();
                    return result;
                } catch (SQLException e) {
                    conn.rollback();
                    
                    // OCC 競合エラーの判定(SQLState: 40001)
                    if ("40001".equals(e.getSQLState()) && attempt < MAX_RETRIES - 1) {
                        long delay = BASE_DELAY_MS * (long) Math.pow(2, attempt) 
                                     + (long) (Math.random() * 100);
                        System.out.printf("Serialization failure (attempt %d/%d). "
                                         + "Retrying in %dms...%n", 
                                         attempt + 1, MAX_RETRIES, delay);
                        TimeUnit.MILLISECONDS.sleep(delay);
                    } else {
                        throw e;
                    }
                }
            }
        }
        throw new Exception("Transaction failed after " + MAX_RETRIES + " retries");
    }
    
    // 使用例
    public static void main(String[] args) throws Exception {
        String result = executeWithRetry(
            "abc123.dsql.us-east-1.on.aws",
            "us-east-1",
            conn -> {
                try (Statement stmt = conn.createStatement()) {
                    stmt.executeUpdate(
                        "UPDATE accounts SET balance = balance - 1000 WHERE id = 'acc-001'");
                    stmt.executeUpdate(
                        "UPDATE accounts SET balance = balance + 1000 WHERE id = 'acc-002'");
                    return "Transfer completed";
                }
            }
        );
        System.out.println(result);
    }
}