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 RPU | 4 | 軽量、予定済み |
| リアルタイムダッシュボード | 2 RPU | 3 | キャッシュ考慮 |
| アドホッククエリ | 1〜3 RPU | 3 | バースト対応 |
そして、それぞれにワークロードマネージメント設定を入れたんです:
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万行以上のテーブルをフルスキャンしてました。スケールするわけです。最適化ポイントは単純だったんですが、気づくまで時間がかかった:
- 分散キー(DISTKEY)の不在:user_idで分散されていない
- ソートキー(SORTKEY)がない:毎回全テーブルスキャン
- 圧縮されていない:古いデータも含めてスキャン
これらを一気に直しました:
-- 最適化後のテーブル設計
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%削減) |
| 平均RPU | 8 | 2.5 | 69%削減 |
| ピークRPU | 16 | 4 | 75%削減 |
| クエリ実行時間(中央値) | 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万円削減したポイント、改めてまとめると:
- WPU設定は「使う分だけ」が幻想——ワークロード分析に基づいた段階的RPU調整が必須
- クエリ最適化が全体削減の60%——DISTKEY・SORTKEYの設計直しで実行時間を82%短縮
- 自動スケーリングは「手動補助」が現実解——EventBridge × Lambda で定時制御が効果的
- 隠れたコスト(ログ、スナップショット、再ロード)を見直す——固定費の20%が削減可能
- 運用で気づく落とし穴はいっぱい——3ヶ月ごとのコスト棚卸しが重要
次のアクション:今月末に進行中のZero-ETL試験を本番投入予定。予測スケーリング実装の検証も同時進行です。データ分析チーム全体のコスト意識も徐々に上がってきたので、このペースなら年内にさらに10〜15万円削減も現実的かもしれません。