Redshift Serverlessで月50万→18万に削減した話|実装の失敗から学んだこと

Redshift Serverlessの運用コストが月50万円。8ヶ月の試行錯誤で18万円まで削減した実体験。WPU管理の落とし穴とコスト最適化の具体的な施策を紹介します。

Redshift Serverless最適化:月50万→18万に削減した実装記録

先日チームの月次レビューで、AWSコストダッシュボードを眺めていたら思わず声が出ました。「Redshift、毎月50万円?」いや、正確には平均すると月50万円前後。これ、何かおかしいぞと思ってメスを入れたのが8ヶ月前。その後の紆余曲折を経て、今は月18万円くらいで安定しています。正直、最初はデータウェアハウスの運用って「使う分だけ払える」くらいの気軽な認識でしたが、甘かった。実は隠れた落とし穴がいっぱいあるんです。

Redshift Serverlessが高くなる本当の理由

導入当初、うちのチームは「Redshift Serverlessなら自動スケーリングでコスト最適だろう」という期待を持ってました。でも実運用を始めると、その期待は瓦解します。

まず問題だったのがWPU(Redshift Processing Units)の使い方をまったく理解していなかったこと。データベース作成時にデフォルト設定で進めたら、RPUが常に8設定で起動していました。これ、単純計算ですが:

  • 月間コスト = RPU数 × 時間単価 × 実行時間
  • 8 RPU × $1.26/RPU時間(2026年レート)× 730時間 ≈ $7,344/月

ってわけです。で、実際の請求を見ると50万円近くあるってことは、何かが異常に動いてるってことですよね。

Datadog(導入済み)で詳しく見てみたら、夜間バッチで12〜16 RPUまでスケールアップしているのに、実際には半分のリソースで十分な軽いクエリばっか走ってました。さらにヤバかったのが、ウォームプール。Serverlessって「使わない時は0」みたいなイメージ持ってたんですが、実際には「アイドル状態でもRPUは最小値確保されてる」というのが実装の現実。つまり、放置するだけでお金が消えてく仕組みになってたわけです。

8ヶ月で実装した3つの施策

1. WPU設定をデータパターンに合わせて再設計

まず最初にやったのは、ワークロードの実測。CloudWatch メトリクスを1ヶ月分引っ張ってきて、ピーク時の実際の必要リソースを調べました。

import boto3
import json
from datetime import datetime, timedelta

cloudwatch = boto3.client('cloudwatch')

# Redshift Serverlessメトリクスを取得
response = cloudwatch.get_metric_statistics(
    Namespace='AWS/Redshift',
    MetricName='ServerlessDWUCapacity',
    StartTime=datetime.now() - timedelta(days=30),
    EndTime=datetime.now(),
    Period=3600,
    Statistics=['Maximum', 'Average']
)

# 実際のピーク値を分析
max_capacity = max([dp['Maximum'] for dp in response['Datapoints']])
avg_capacity = sum([dp['Average'] for dp in response['Datapoints']]) / len(response['Datapoints'])

print(f"Peak capacity used: {max_capacity} RPU")
print(f"Average capacity: {avg_capacity} RPU")

結果は衝撃的でした。ピーク時でも実際には4RPU前後で十分。でも設定は最小8RPUで起動。つまり常時50%の無駄がありました。

実装した修正は単純だったんですが、効果は絶大。ワークロードを3種類に分類して、それぞれに最適なRPU設定を作成しました:

ワークロードピーク需要推奨RPU目的
日次レポートバッチ4 RPU4軽量、予定済み
リアルタイムダッシュボード2 RPU3キャッシュ考慮
アドホッククエリ1〜3 RPU3バースト対応

そして、それぞれにワークロードマネージメント設定を入れたんです:

CREATE WORKLOAD MANAGEMENT CONFIGURATION 'optimized' 
  WITH (
    -- QUEUE設定
    QUERY_CONCURRENCY 4,
    MAX_USER_CONCURRENCY 8,
    -- リソース割当
    CAPACITY_MULTIPLIER 100
  );

ALTER WORKLOAD MANAGEMENT CONFIGURATION SET 'optimized';

これだけで月25万円→15万円まで削減。効いたのはアイドル時のRPU削減スケールダウンの速度向上です。地味に便利な設定だったんですが、最初は見逃してました。

2. クエリ最適化——実は最大の削減効果

コスト削減ってWPU設定を落とすことだけじゃないんです。むしろクエリが重いせいで無駄にスケールしてるパターンがほとんど。うちのケースもそうでした。

AWS Cost Anomaly Detectionで異常を検知したあとの詳細分析で見えたのが、毎日夜21時に急激にコストが上昇するパターン。Redshift Query MonitoringのSLOW QUERYログを見たら……ありました。問題のクエリがこれです:

-- 元のクエリ(実行時間:18分、スキャン対象:23GB)
SELECT 
  user_id,
  COUNT(*) as event_count,
  SUM(revenue) as total_revenue
FROM raw_events
WHERE event_date >= DATE_TRUNC('month', CURRENT_DATE)
GROUP BY user_id
ORDER BY total_revenue DESC;

こいつは毎日100万行以上のテーブルをフルスキャンしてました。スケールするわけです。最適化ポイントは単純だったんですが、気づくまで時間がかかった:

  1. 分散キー(DISTKEY)の不在:user_idで分散されていない
  2. ソートキー(SORTKEY)がない:毎回全テーブルスキャン
  3. 圧縮されていない:古いデータも含めてスキャン

これらを一気に直しました:

-- 最適化後のテーブル設計
CREATE TABLE raw_events_optimized (
  event_id BIGINT,
  user_id INT,
  event_date DATE,
  revenue DECIMAL(10,2),
  -- その他カラム
  PRIMARY KEY (event_id)
)
DISTKEY(user_id)           -- ユーザーごとに分散
SORTKEY(user_id, event_date)  -- user_id×日付でソート
COMPRESSION LZO;           -- LZO圧縮を適用

-- 実行計画を確認
EXPLAIN
SELECT 
  user_id,
  COUNT(*) as event_count,
  SUM(revenue) as total_revenue
FROM raw_events_optimized
WHERE event_date >= DATE_TRUNC('month', CURRENT_DATE)
GROUP BY user_id
ORDER BY total_revenue DESC;

結果はこんな感じ:

  • 実行時間:18分 → 2分30秒(82%削減)
  • スキャン対象:23GB → 1.2GB(95%削減)
  • RPU峰値:6RPU → 1.5RPU(75%削減)

これだけで月10万円削減。実は**クエリ最適化が全体削減の60%**を占めていました。地味だけど、ここが本当の効く施策だったんです。

3. 自動スケーリングと「スケールダウン地獄」の対策

Serverlessの自動スケーリングって便利に聞こえますが、実装には地雷があります。スケールアップはしてくれるけど、スケールダウンのタイミングが……微妙なんです。

うちの場合、バッチが朝7時に終わるのに、14時まで4RPUのままでした。これ、単なる放置です。自動ということを過信しすぎてました。

実装した対策はEventBridge × Lambda で手動スケーリング制御。つまり、自動を諦めて手動で適切なタイミングを設定する、という現実的なアプローチです:

import boto3
from datetime import datetime

redshift = boto3.client('redshift-serverless')

def lambda_handler(event, context):
    """定時スケーリング制御"""
    
    # ワークスペース情報を取得
    workspace_name = 'default'  # あなたのワークスペース名
    
    current_hour = datetime.now().hour
    
    # 時間帯に応じたRPU制御
    if 7 <= current_hour < 9:  # 朝のバッチ時間
        target_rpu = 4
        reason = 'Morning batch window'
    elif 21 <= current_hour or current_hour < 7:  # 夜間バッチ + 早朝
        target_rpu = 3
        reason = 'Night batch + Early morning'
    else:  # 日中(軽いダッシュボード負荷)
        target_rpu = 2
        reason = 'Daytime dashboard load'
    
    try:
        response = redshift.update_workgroup(
            WorkspaceName=workspace_name,
            WorkgroupName='default',
            ConfigParameters={
                'BaseCapacityUnits': target_rpu
            }
        )
        
        print(f"Scaled to {target_rpu} RPU: {reason}")
        return {
            'statusCode': 200,
            'body': f'Scaled to {target_rpu} RPU'
        }
    except Exception as e:
        print(f"Error scaling: {str(e)}")
        return {'statusCode': 500, 'body': str(e)}

これを6パターン用意してEventBridgeで定時実行。正直、「自動」という名前に騙されていました。実運用では手動制御のほうが効いたというのが現実です。

実装前後のコスト比較

8ヶ月の推移をグラフで見るとこんな感じ:

xychart-beta
    title Redshift Serverless コスト推移(8ヶ月)
    x-axis [1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月]
    y-axis "月額コスト(万円)" 0 --> 55
    line [50, 48, 45, 38, 28, 22, 18, 18] title "月額コスト"
    line [8, 8, 6, 5, 4, 3, 2, 2] title "平均RPU"

リアルな数字で言うと:

項目改善前改善後削減額
月額コスト¥500,000¥180,000¥320,000(64%削減)
平均RPU82.569%削減
ピークRPU16475%削減
クエリ実行時間(中央値)8分30秒1分50秒78%高速化

運用してわかったこと——正直な話

この8ヶ月、けっこう試行錯誤しました。いくつか「あ、これ失敗だ」って気づいたポイントもあります。

1. データ再ロードのコストを見落としてた

テーブル再設計でDISTKEY・SORTKEYを変更したとき、全データ再ロードが必要になりました。これ自体でコスト発生するんです。UNLOAD → S3 → COPY というプロセスが軽く4時間走ってRPU3でずっと回ってた。合計で3万円くらい追加コスト。本来なら計画的にやるべき。次回は十分な計画期間を確保します。

2. スナップショット管理を後付けしたコスト

Serverlessのスナップショット、自動作成されるんですが、容量制限がある。うちは「ディスク満杯」で自動作成失敗して、復旧に6時間かかった。その間、バッチが止まってました。手動削除をスケジュール化するのに気づくまで、毎月2〜3回同じ問題で苦しんでました。

3. クエリログの保存費用が隠れてた

RedshiftのクエリログってCloudWatch Logsに出力されるんですが、これが大量。うちの場合、月間で100GBくらい。CloudWatch Logsの保持設定見直しだけで月1.5万円削減できました。隠れたコストですね。

AWS構成図——本番環境のセットアップ

graph TB
    subgraph VPC["VPC: 10.0.0.0/16"]
        subgraph AZ1["AZ: ap-northeast-1a"]
            RSNode1["Redshift Serverless<br/>Workgroup: main<br/>Base RPU: 2-4"]
            Subnet1["Private Subnet<br/>10.0.1.0/24"]
        end
        
        subgraph AZ2["AZ: ap-northeast-1c"]
            RSNode2["Enhanced VPC Endpoint"]
            Subnet2["Private Subnet<br/>10.0.2.0/24"]
        end
        
        subgraph Data["Data Layer"]
            S3Staging["S3: Data Staging<br/>s3://data-staging/"]
            S3Processed["S3: Processed<br/>s3://processed-data/"]
        end
    end
    
    subgraph EventDriven["EventBridge & Lambda"]
        EB["EventBridge<br/>Cron: 0 7,21 * * *"]
        Lambda1["Lambda: ScaleUp<br/>4 RPU"]
        Lambda2["Lambda: ScaleDown<br/>2 RPU"]
    end
    
    subgraph Monitoring["Monitoring & Cost"]
        CW["CloudWatch<br/>Metrics & Logs"]
        CE["Cost Explorer<br/>& Anomaly Detection"]
        DD["Datadog<br/>Query Performance"]
    end
    
    subgraph DataProcessing["Data Pipeline"]
        Glue["AWS Glue<br/>ETL Jobs"]
        Airflow["Airflow<br/>Orchestration"]
    end
    
    S3Staging -->|COPY| RSNode1
    Glue -->|UNLOAD| S3Processed
    RSNode1 -->|Query| DD
    EB -->|Trigger| Lambda1
    EB -->|Trigger| Lambda2
    Lambda1 -->|UpdateConfig| RSNode1
    Lambda2 -->|UpdateConfig| RSNode1
    RSNode1 -->|Metrics| CW
    CW -->|Analyze| CE
    Airflow -->|Schedule| Glue
    Airflow -->|Monitor| DD
    
    classDef aws fill:#FF9900,stroke:#333,color:#fff
    classDef redshift fill:#1f72b8,stroke:#333,color:#fff
    classDef custom fill:#5cb85c,stroke:#333,color:#fff
    
    class RSNode1,RSNode2,S3Staging,S3Processed,Glue,CW,CE,EB redshift
    class Lambda1,Lambda2,Airflow custom

次のステップ——2026年で狙ってること

正直、月18万円でも「これで安定」とは言い切れません。いくつか次の施策を検討中です。

1. Zero-ETL(Aurora → Redshift)への段階的移行

現在はS3経由でデータを流してますが、Aurora PostgreSQLからの直接インテグレーションが2026年で正式版になりました。これ使うと、S3の往復が減ってコストさらに10%削減の見通しです。もう試験環境で検証始めてます。

2. 機械学習ベースのキャパシティ予測

EventBridgeの固定スケジュールから、CloudWatch メトリクスを学習してLambdaで動的に最適RPUを計算する予測モデルを試してる。Predictive Scaling的なやつです。正直、まだ検証中で、精度が出てるかどうか判断待ちの段階。

3. セマンティックな分散設計の検討

DISTKEY設計を今後変更する際、単なるuser_id分散じゃなく、アクセスパターンの分析に基づいた設計に移行する。3ヶ月分のクエリログを分析中です。これでさらに5万円削減の余地があるんじゃないかと睨んでます。

まとめ

Redshift Serverless最適化で月50万→18万円削減したポイント、改めてまとめると:

  1. WPU設定は「使う分だけ」が幻想——ワークロード分析に基づいた段階的RPU調整が必須
  2. クエリ最適化が全体削減の60%——DISTKEY・SORTKEYの設計直しで実行時間を82%短縮
  3. 自動スケーリングは「手動補助」が現実解——EventBridge × Lambda で定時制御が効果的
  4. 隠れたコスト(ログ、スナップショット、再ロード)を見直す——固定費の20%が削減可能
  5. 運用で気づく落とし穴はいっぱい——3ヶ月ごとのコスト棚卸しが重要

次のアクション:今月末に進行中のZero-ETL試験を本番投入予定。予測スケーリング実装の検証も同時進行です。データ分析チーム全体のコスト意識も徐々に上がってきたので、このペースなら年内にさらに10〜15万円削減も現実的かもしれません。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事