📋 このステップで学ぶこと
ディストリビューションキー(DISTKEY)の選択と設定
ソートキー(SORTKEY)の選択と設定
圧縮(Compression)の仕組みと効果
VACUUM、ANALYZEコマンドの使い方
実行計画(EXPLAIN)の読み方
実践演習:パフォーマンスチューニング
🎯 このステップのゴール
このステップを終えると、Redshiftのクエリを10倍以上高速化できるようになります 。DISTKEY、SORTKEY、圧縮の3つの最適化テクニックをマスターしましょう。
🔑 1. ディストリビューションキー(DISTKEY)
DISTKEYとは
ディストリビューションキー(DISTKEY) は、データをどのようにノード間で分散させるか を決定するカラムです。
💡 例え話:物流倉庫の配置戦略
【シナリオ】
全国に4つの倉庫(= 4ノード)があります。
どこに何を置くかで、配送効率が大きく変わります。
【戦略1:ランダムに配置(DISTSTYLE EVEN)】
商品をランダムに4倉庫に分散
→ 注文が来たら、4倉庫を探し回る必要がある
→ 遅い!
【戦略2:地域別に配置(DISTSTYLE KEY)】
東北の商品は東北倉庫、関西の商品は関西倉庫…
→ 「東北向け注文」は東北倉庫だけで完結
→ 他の倉庫に問い合わせ不要 = 高速!
【戦略3:全倉庫に在庫(DISTSTYLE ALL)】
人気商品は全倉庫に在庫を持つ
→ どの倉庫でもすぐ出荷可能
→ ただし在庫コスト増
【Redshiftでの対応】
DISTKEY = 地域(どの倉庫に置くか決めるキー)
→ JOINで使うカラムを指定すると、
関連データが同じノードに集まり、JOIN高速化!
DISTSTYLEの種類
DISTSTYLE
説明
使用シーン
例
KEY
指定したDISTKEYの値でデータを分散
JOINでよく使うカラムがある
sales, orders
EVEN
全ノードに均等に分散(ラウンドロビン)
JOINしない、単独で集計
logs, events
ALL
全ノードに全データをコピー
小さいディメンションテーブル
products, customers
AUTO
Redshiftが自動選択
どれを選ぶか迷う場合
–
DISTKEYの効果(具体例)
📊 JOINの処理比較
【クエリ】
SELECT s.*, c.customer_name
FROM sales s
JOIN customers c ON s.customer_id = c.customer_id;
【DISTKEYなし(EVEN分散)の場合】
┌─────────────────────────────────────────────────────────┐
│ ノード1: sales(customer_id=1,3,5,7…) │
│ ノード2: sales(customer_id=2,4,6,8…) │
│ ノード3: customers(customer_id=1,3,5,7…) │
│ ノード4: customers(customer_id=2,4,6,8…) │
└─────────────────────────────────────────────────────────┘
JOINするには…
→ ノード間でデータを転送(Redistribute)
→ ネットワーク通信が発生
→ 遅い!(10秒)
【DISTKEY(customer_id)設定の場合】
┌─────────────────────────────────────────────────────────┐
│ ノード1: sales(customer_id=1,2) + customers(customer_id=1,2) │
│ ノード2: sales(customer_id=3,4) + customers(customer_id=3,4) │
│ ノード3: sales(customer_id=5,6) + customers(customer_id=5,6) │
│ ノード4: sales(customer_id=7,8) + customers(customer_id=7,8) │
└─────────────────────────────────────────────────────────┘
JOINするには…
→ 各ノード内で完結(データ転送なし)
→ 高速!(1秒)
→ 10倍高速化!
DISTSTYLE別のテーブル作成例
📝 DISTSTYLE KEY(大きいファクトテーブル)
— 売上ファクトテーブル(100万件以上)
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
customer_id INT,
product_id INT,
amount DECIMAL(10,2)
)
DISTSTYLE KEY
DISTKEY(customer_id); — JOINで使うカラム
— customer_idでデータを分散
— 同じcustomer_idのデータは同じノードに配置
📝 DISTSTYLE ALL(小さいディメンションテーブル)
— 顧客マスタ(1万件程度)
CREATE TABLE customers (
customer_id INT,
customer_name VARCHAR(200),
email VARCHAR(200),
city VARCHAR(100)
)
DISTSTYLE ALL; — 全ノードにコピー
— 全ノードに全データがあるので
— JOINでデータ転送が不要 → 超高速
📝 DISTSTYLE EVEN(JOINしないテーブル)
— ログテーブル(JOINしない)
CREATE TABLE access_logs (
log_id BIGINT,
timestamp TIMESTAMP,
user_agent VARCHAR(500),
ip_address VARCHAR(50),
path VARCHAR(200)
)
DISTSTYLE EVEN; — 均等に分散
— JOINしないので、DISTKEYは不要
— 均等に分散して並列処理を最大化
DISTKEYの選び方
✅ 良いDISTKEYの条件
JOINでよく使う カラム
カーディナリティが高い (値の種類が多い)
均等に分散 する(偏りが少ない)
例: customer_id, order_id, user_id
❌ 悪いDISTKEYの例
カーディナリティが低い :性別(2値)
偏りがある :都道府県(東京に集中)
JOINで使わない :タイムスタンプ
問題: データが偏り、一部ノードに負荷集中
データ分散状況の確認
📊 スライス別のデータ分布を確認
— データ分散状況を確認
SELECT
TRIM(name) as table_name,
slice,
COUNT(*) as row_count
FROM stv_blocklist
WHERE name = ‘sales’
GROUP BY name, slice
ORDER BY slice;
— 理想的な結果(均等に分散)
table_name | slice | row_count
————|——-|———-
sales | 0 | 250000
sales | 1 | 250000
sales | 2 | 250000
sales | 3 | 250000
— 各スライスに均等に分散されている ✅
— 悪い結果(偏りがある)
table_name | slice | row_count
————|——-|———-
sales | 0 | 800000 ← 偏っている!
sales | 1 | 50000
sales | 2 | 100000
sales | 3 | 50000
— slice 0に集中 → DISTKEYの選択が悪い ❌
🔢 2. ソートキー(SORTKEY)
SORTKEYとは
ソートキー(SORTKEY) は、データをどの順番で保存するか を決定するカラムです。
💡 例え話:辞書の索引
【辞書で「Redshift」を探す】
【ソートなし(バラバラ)】
「A」「Z」「M」「R」「B」…とランダムに並んでいる
→ 全ページをめくって探す
→ 10分かかる(遅い!)
【50音順にソート】
「A」「B」「C」…「R」…「Z」と並んでいる
→ 「R」のページに直接ジャンプ
→ 10秒で見つかる(速い!)
【Redshiftでの対応】
SORTKEY(sale_date) を設定すると…
データが日付順に保存される
WHERE sale_date = ‘2025-01-19’
→ 該当日付のブロックに直接アクセス
→ 全データをスキャンする必要なし
→ 100倍以上高速化も可能!
SORTKEYの効果(具体例)
📊 範囲検索の処理比較
【クエリ】
SELECT * FROM sales
WHERE sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’;
【SORTKEYなしの場合】
データ: 1000万件(1年分)
ランダムな順番で保存されている
処理: 全データをスキャン(1000万件)
その中から1月分を抽出
時間: 30秒
【SORTKEY(sale_date)設定の場合】
データ: 1000万件(1年分)
日付順に保存されている
処理: 1月のブロックだけスキャン(約80万件)
全データの8%だけ読み込み
時間: 2.4秒
→ 12倍高速化!
【Zone Mapの仕組み】
Redshiftは各ブロックの最小値・最大値を記録
ブロック1: 2024-01-01 〜 2024-03-31 → スキップ
ブロック2: 2024-04-01 〜 2024-06-30 → スキップ
…
ブロック12: 2025-01-01 〜 2025-03-31 → 読み込み!
SORTKEYの種類
種類
説明
メリット
デメリット
COMPOUND
複数カラムの組み合わせでソート (最初のカラムが最優先)
VACUUMが速い 一般的に推奨
2番目以降のカラム単独では効果薄
INTERLEAVED
複数カラムを均等に重視
どのカラムでも効果あり
VACUUMが非常に遅い
SORTKEYの設定例
📝 単一カラム(最も一般的)
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
customer_id INT,
amount DECIMAL(10,2)
)
SORTKEY(sale_date); — 日付順でソート
— 効果が高いクエリ
SELECT * FROM sales
WHERE sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’;
— → 日付範囲に該当するブロックだけスキャン
📝 複数カラム(COMPOUND)
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
customer_id INT,
amount DECIMAL(10,2)
)
COMPOUND SORTKEY(sale_date, customer_id);
— sale_date → customer_id の順でソート
— 効果が高いクエリ
WHERE sale_date = ‘2025-01-19’ — ✅ 高速
WHERE sale_date = ‘2025-01-19’ AND customer_id = 100 — ✅ 高速
— 効果がないクエリ
WHERE customer_id = 100 — ❌ 効果なし
— (2番目のカラム単独では効かない)
⚠️ INTERLEAVED SORTKEY(特殊なケースのみ)
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
customer_id INT,
product_id INT
)
INTERLEAVED SORTKEY(sale_date, customer_id, product_id);
— 3つのカラムを均等に重視
WHERE sale_date = … — ✅ 高速
WHERE customer_id = … — ✅ 高速
WHERE product_id = … — ✅ 高速
— ⚠️ 注意:VACUUMが非常に遅い
— 通常はCOMPOUNDを推奨
SORTKEYの選び方
✅ 良いSORTKEYの条件
WHERE句 でよく使うカラム
範囲検索 するカラム(日付、数値)
JOIN条件 に使うカラム
例: sale_date, created_at, order_date, timestamp
🗜️ 3. 圧縮(Compression)
圧縮とは
Redshiftはカラムナーストレージ なので、各カラムを個別に圧縮できます。適切な圧縮でストレージ70〜90%削減 も可能です。
💡 例え話:衣類の圧縮袋
【旅行の荷物】
セーター10枚をスーツケースに入れたい
【圧縮なし】
10枚 → スーツケースに入りきらない
追加のバッグが必要 → 荷物が増える → 移動が遅い
【圧縮袋使用】
10枚 → 圧縮して3枚分のサイズに
スーツケース1つで足りる → 移動が速い
【Redshiftでの効果】
元データ: 100GB
圧縮後: 20GB(80%削減)
・ストレージ料金: 80%削減
・読み込み時間: 80%削減(I/O削減)
・クエリ速度: 大幅向上
圧縮の効果(具体例)
📊 実際の圧縮効果
【1億件のsalesテーブル】
カラム 元サイズ 圧縮後 圧縮率 エンコーディング
————————————————————–
sale_id 400MB 80MB 80%削減 DELTA
sale_date 400MB 40MB 90%削減 DELTA32K
customer_id 400MB 60MB 85%削減 DELTA
product_name 2000MB 400MB 80%削減 LZO
amount 800MB 200MB 75%削減 ZSTD
————————————————————–
合計 4000MB 780MB 80%削減
→ ストレージ料金 80%削減!
→ クエリ速度も向上(読み込むデータ量が減る)
圧縮エンコーディングの種類
エンコーディング
説明
適したデータ
圧縮率
RAW
圧縮なし
すでに圧縮されたデータ
0%
LZO
汎用圧縮(高速)
長い文字列
60-80%
ZSTD
汎用圧縮(高圧縮率)
様々なデータ
70-90%
BYTEDICT
辞書圧縮
256種類以下の値
80-95%
DELTA
差分圧縮
連続する数値(ID)
70-90%
DELTA32K
差分圧縮(日付向け)
日付、近い値の数値
80-95%
圧縮の設定方法
📝 方法1:COPY時に自動設定(推奨)
— テーブル作成(圧縮指定なし)
CREATE TABLE sales (
sale_id INT,
sale_date DATE,
amount DECIMAL(10,2)
);
— COPYコマンドで自動圧縮
COPY sales
FROM ‘s3://bucket/data.csv’
IAM_ROLE ‘arn:…’
CSV
COMPUPDATE ON; — Redshiftが最適な圧縮を自動選択
— Redshiftがデータを分析して、
— 各カラムに最適な圧縮を選んでくれる
📝 方法2:手動で指定
CREATE TABLE sales (
sale_id INT ENCODE DELTA, — 連続ID → 差分圧縮
sale_date DATE ENCODE DELTA32K, — 日付 → 日付用差分圧縮
product_name VARCHAR(200) ENCODE LZO, — 文字列 → LZO圧縮
category VARCHAR(50) ENCODE BYTEDICT, — カテゴリ → 辞書圧縮
amount DECIMAL(10,2) ENCODE ZSTD — 数値 → 汎用圧縮
);
— 手動設定は上級者向け
— 通常はCOMPUPDATE ONで自動設定を推奨
圧縮状況の確認
📊 テーブルの圧縮状況を確認
— テーブルの圧縮状況を確認
SELECT
TRIM(tablename) as table_name,
TRIM(“column”) as column_name,
TRIM(type) as data_type,
TRIM(encoding) as encoding
FROM pg_table_def
WHERE tablename = ‘sales’
ORDER BY column_name;
— 実行結果例
table_name | column_name | data_type | encoding
————|————–|———–|———-
sales | sale_id | integer | delta
sales | sale_date | date | delta32k
sales | amount | numeric | zstd
sales | product_name | varchar | lzo
🔧 4. VACUUM、ANALYZEコマンド
VACUUMコマンド
VACUUM は、削除されたデータの領域を回収し、ソート順を整理 するコマンドです。
💡 例え話:本棚の整理
【本棚の状態】
50音順に本を並べていたが…
・途中の本を何冊か捨てた(DELETE)
・新しい本を最後に追加した(INSERT)
【現在の状態】
「あ」「い」「_」「え」「お」…「わ」「新刊1」「新刊2」
↑空きスペース ↑ソートされていない
【VACUUM = 本棚の整理】
・空きスペースを詰める
・新しい本を正しい位置に移動
・50音順に再整理
【整理後】
「あ」「い」「え」「お」「新刊1」「新刊2」…「わ」
→ きれいにソートされた!
📝 VACUUMの実行
— 全テーブルをVACUUM
VACUUM;
— 特定テーブルをVACUUM
VACUUM sales;
— ソートのみ(削除領域の回収はしない)
VACUUM SORT ONLY sales;
— 削除領域の回収のみ(ソートはしない)
VACUUM DELETE ONLY sales;
— フル再ソート(100%再ソート)
VACUUM FULL sales;
⚠️ VACUUMの注意点
時間がかかる :大きなテーブルは数時間
リソース消費 :CPU、ディスクI/Oを大量に使用
実行タイミング :夜間など、負荷の低い時間帯に実行
自動VACUUM :Redshiftが自動実行するので、通常は手動不要
ANALYZEコマンド
ANALYZE は、テーブルの統計情報を更新 するコマンドです。クエリオプティマイザが正しい実行計画を作成するために必要です。
📝 ANALYZEの実行
— 全テーブルをANALYZE
ANALYZE;
— 特定テーブルをANALYZE
ANALYZE sales;
— 特定カラムをANALYZE
ANALYZE sales(sale_date, customer_id);
— ANALYZEが必要なテーブルを確認
SELECT
TRIM(tablename) as table_name,
stats_off
FROM svv_table_info
WHERE stats_off > 10 — 統計情報が10%以上古い
ORDER BY stats_off DESC;
💡 VACUUMとANALYZEの使い分け
コマンド
目的
実行タイミング
VACUUM
削除領域の回収、ソート順の整理
DELETE/UPDATE後、大量ロード後
ANALYZE
統計情報の更新
大量ロード後、実行計画が不適切な時
📊 5. 実行計画(EXPLAIN)
EXPLAINとは
EXPLAIN は、クエリの実行計画 を表示するコマンドです。クエリを実行せずに、どのような処理が行われるか確認できます。
📝 EXPLAINの使い方
— 実行計画のみ表示(クエリは実行されない)
EXPLAIN
SELECT * FROM sales
WHERE sale_date = ‘2025-01-19’;
— 実行結果例
QUERY PLAN
———————————————————
XN Seq Scan on sales (cost=0.00..10000.00 rows=1000 width=20)
Filter: (sale_date = ‘2025-01-19’::date)
実行計画の読み方
用語
意味
備考
XN Seq Scan
順次スキャン(全行スキャン)
遅い、SORTKEYがない
XN Hash Join
ハッシュJOIN
一般的なJOIN方法
XN Merge Join
マージJOIN
ソート済みデータ向け、高速
DS_DIST_ALL_INNER
ALL分散テーブルとのJOIN
データ移動なし、高速
DS_BCAST_INNER
ブロードキャストJOIN
小テーブルを全ノードに配信
DS_DIST_BOTH
両テーブルを再分散
遅い、DISTKEYを見直す
cost
推定コスト
小さいほど速い
rows
推定行数
–
最適化の例
❌ 最適化前(遅いクエリ)
EXPLAIN
SELECT * FROM sales
WHERE sale_date = ‘2025-01-19’;
— 実行計画
XN Seq Scan on sales (cost=0.00..10000.00 rows=1000 width=20)
Filter: (sale_date = ‘2025-01-19’::date)
— cost=10000 → 高コスト!
— Seq Scan → 全行スキャン(遅い)
✅ 最適化後(速いクエリ)
— SORTKEYを設定した新テーブルを作成
CREATE TABLE sales_optimized AS
SELECT * FROM sales
SORTKEY(sale_date);
VACUUM sales_optimized;
ANALYZE sales_optimized;
EXPLAIN
SELECT * FROM sales_optimized
WHERE sale_date = ‘2025-01-19’;
— 実行計画
XN Seq Scan on sales_optimized (cost=0.00..100.00 rows=1000 width=20)
Filter: (sale_date = ‘2025-01-19’::date)
— cost=100 → 100倍改善!
— Zone Mapにより、該当日付のブロックのみスキャン
💪 6. 実践演習:パフォーマンスチューニング
演習 1
実践
遅いクエリを最適化して、実行時間を10分の1に短縮してください
状況:
salesテーブル(100万件)
customersテーブル(1万件)
以下のクエリが10秒かかる
SELECT
c.customer_name,
SUM(s.amount) as total_sales
FROM sales s
JOIN customers c ON s.customer_id = c.customer_id
WHERE s.sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’
GROUP BY c.customer_name
ORDER BY total_sales DESC
LIMIT 10;
解答を見る
【解答例】
1. 最適化されたテーブルを作成
— salesテーブル(最適化版)
CREATE TABLE sales_optimized (
sale_id INT ENCODE DELTA,
sale_date DATE ENCODE DELTA32K,
customer_id INT ENCODE DELTA,
product_id INT ENCODE DELTA,
amount DECIMAL(10,2) ENCODE ZSTD
)
DISTSTYLE KEY
DISTKEY(customer_id) — JOINで使用
SORTKEY(sale_date); — WHERE句で使用
— customersテーブル(最適化版)
CREATE TABLE customers_optimized (
customer_id INT ENCODE DELTA,
customer_name VARCHAR(200) ENCODE LZO,
email VARCHAR(200) ENCODE LZO
)
DISTSTYLE ALL; — 小さいテーブル → 全ノードにコピー
2. データをコピー&最適化
— データをコピー
INSERT INTO sales_optimized SELECT * FROM sales;
INSERT INTO customers_optimized SELECT * FROM customers;
— 統計情報を更新
ANALYZE sales_optimized;
ANALYZE customers_optimized;
3. 最適化後のクエリを実行
— 実行時間を計測
SELECT
c.customer_name,
SUM(s.amount) as total_sales
FROM sales_optimized s
JOIN customers_optimized c ON s.customer_id = c.customer_id
WHERE s.sale_date BETWEEN ‘2025-01-01’ AND ‘2025-01-31’
GROUP BY c.customer_name
ORDER BY total_sales DESC
LIMIT 10;
— 実行時間: 約1秒(10分の1に短縮!)
最適化のポイント:
DISTKEY(customer_id) :JOINキーで分散 → データ移動なし
SORTKEY(sale_date) :日付範囲検索が高速
DISTSTYLE ALL :小さいcustomersを全ノードにコピー
圧縮設定 :I/O削減でさらに高速化
⏰ 演習終了後は必ずクラスターを一時停止!
Redshiftコンソール → クラスター選択 → アクション → クラスターを一時停止
📝 STEP 14 のまとめ
✅ このステップで学んだこと
DISTKEY :JOINで使うカラムを指定 → データ移動なしでJOIN高速化
SORTKEY :WHERE句で使うカラムを指定 → 範囲検索が高速化
圧縮 :ストレージ70-90%削減 + クエリ高速化
VACUUM :削除領域の回収、ソート順の整理
ANALYZE :統計情報の更新
EXPLAIN :実行計画の確認、ボトルネック発見
💡 最適化のチェックリスト
□ DISTKEY = JOINで使うカラム
□ SORTKEY = WHERE句で使うカラム
□ 小さいディメンションテーブルは DISTSTYLE ALL
□ 圧縮は COMPUPDATE ON で自動設定
□ 定期的に VACUUM と ANALYZE を実行
□ EXPLAIN で実行計画を確認
🎯 次のステップの準備
次のSTEP 15では、「AWS Glue入門」 を学びます。
ETLサービスを使って、データパイプラインを自動化しましょう!
❓ よくある質問
Q1: DISTKEYを後から変更できますか?
直接変更はできません。 新しいテーブルを作成して、データをコピーする必要があります。
1. 新しいテーブルを正しいDISTKEYで作成
2. INSERT INTO new_table SELECT * FROM old_table
3. 古いテーブルを削除
4. 新しいテーブルをリネーム
大きなテーブルの場合、時間がかかるので、最初から適切なDISTKEYを設定することが重要です。
Q2: VACUUMはどのくらいの頻度で実行すべきですか?
通常は自動VACUUMで十分です。 Redshiftはバックグラウンドで自動的にVACUUMを実行します。
手動VACUUMが必要なケース:
・大量のDELETE/UPDATEを実行した直後
・クエリが急に遅くなった場合
・svv_table_infoでunsortedが高い場合
Q3: COMPOUND SORTKEYとINTERLEAVED SORTKEYの使い分けは?
ほとんどの場合、COMPOUND SORTKEYを使用してください。
COMPOUND(推奨): 最初のカラムが最優先、VACUUMが速い
INTERLEAVED(特殊): 全カラムを均等に重視、VACUUMが非常に遅い
INTERLEAVEDはVACUUMのコストが高いため、よほどの理由がない限りCOMPOUNDを使いましょう。
Q4: 圧縮エンコーディングは自動設定と手動設定、どちらが良いですか?
初回ロードでは自動設定(COMPUPDATE ON)を推奨します。 Redshiftがデータを分析して、最適な圧縮を選択してくれます。
手動設定は上級者向けで、データの特性を熟知している場合のみ有効です。
Q5: EXPLAINで「cost」が大きいのですが、どうすれば良いですか?
以下の順番で最適化を検討してください:
1. SORTKEYを設定(WHERE句のカラム)
2. DISTKEYを設定(JOINのカラム)
3. ANALYZEを実行(統計情報更新)
4. VACUUMを実行(ソート順整理)
特に、DS_DIST_BOTHが表示されている場合は、DISTKEYの見直しが効果的です。