DEA / ドメイン2 データストア管理
重要な内容
パーティショニング・インデックス・圧縮の最適化
クエリを速く安くする工夫は、突き詰めると「読まなくてよいデータを読まない」ことです。パーティショニングは読むファイルを、列指向形式は読む列を、インデックスは読む行を、圧縮は読むバイト数を減らします。DEA-C01 では、インデックス作成、パーティショニング戦略、圧縮、その他のデータ最適化手法のベストプラクティス (スキル 2.4.5) が問われます。
Point
- パーティションプルーニング (partition pruning): WHERE 句の条件に合わないパーティションを読み飛ばすことです。prune は「刈り込む」という意味です。
- 述語のプッシュダウン (predicate pushdown): 絞り込みの条件 (述語) を、データを読む層へ先に渡して、不要なデータを早い段階で捨てることです。
- バケット化 (bucketing): 列の値のハッシュで、データを決まった数のファイルに分けることです。
- 和訳
- Prune: 刈り込む
- Weblio英和辞書「prune」
概要
| 手法 | 減らすもの | 主な対象 |
|---|---|---|
| パーティショニング | 読むファイル (フォルダ) | + / / / 、 |
| 化 | 読むファイル (高カーディナリティの列での絞り込み・結合) | Athena、EMR (Hive / ) |
| 列指向形式 ( / ) | 読む列 | S3 の |
| インデックス・ | 読む行・ブロック | 、、、 (ソートキー) |
| 圧縮 | 読む・保管するバイト数 | すべて |
| ファイルサイズの調整 () | ファイルを開く回数とメタデータの処理 | S3 のデータレイク |
主要技術
①パーティショニング戦略 ⚠️ 頻出
| 観点 | よい戦略 | 悪い戦略 |
|---|---|---|
| どの列で切るか | WHERE 句でよく使い、値の種類がほどほどの列 (日付、地域) | ほぼ一意な列 (ユーザー ID、注文 ID) |
| 粒度 | 1パーティションに十分なデータ (数百 MB 以上) がたまる粒度 | 1時間に数 KB しか来ないのに、時間単位で切る |
| 階層 | 絞り込む順に year=/month=/day= | 使わない列を先頭に置く |
| 管理 | ()、パーティションインデックス | パーティションが数十万あるのに毎回すべてを一覧する |
過剰なパーティション分割 (過剰分割) は、パーティションの一覧取得に時間がかかり、が大量にでき、かえって遅くなります。迷ったら、粗めの粒度 (日単位など) から始めます。
では、隠れたパーティション (日付の列から日・時を自動で計算) とパーティションの進化 (粒度を途中で変える) が使えるため、後から粒度を見直しやすくなります (「と 」の章)。パーティションをカタログへ登録する方法は「 Data Catalog と」の章で扱います。
②バケット化
化は、指定した列の値のハッシュで、データを決まった数のファイル (バケット) に分けます。
| 観点 | パーティショニング | バケット化 |
|---|---|---|
| 分け方 | 列の値ごとのフォルダ | 列の値のハッシュで決まった数のファイル |
| 向く列 | 値の種類が少なめ (日付、地域) | 値の種類が多い (顧客 ID、デバイス ID) |
| 効果 | 該当しないフォルダを読み飛ばす | 特定の値を含むバケットだけを読む、同じバケット同士で結合する |
では (CREATE TABLE AS SELECT) で bucketed_by と bucket_count を指定して作れます。日付でパーティションを切り、その中を顧客 ID でバケット化する、という組み合わせが典型です。
③インデックスの作成
インデックスは、条件に合う行をすべてのデータを読まずに見つけるための索引です。保管先ごとに仕組みが違います。
| 保管先 | 仕組み | ベストプラクティス |
|---|---|---|
| / | B-tree などのインデックス | WHERE・JOIN・ORDER BY でよく使う列に作る。複合インデックスは絞り込みの強い列を先に。インデックスが多すぎると書き込みが遅くなる |
| プライマリキー、、 | アクセスパターンごとにキーとインデックスを用意する。GSI には必要な属性だけを射影する | |
| 転置インデックス、マッピング | 検索に使わない項目はインデックスしない設定にする。の大きさを数十 GiB 程度にそろえる | |
| インデックスはない。とゾーンマップ (ブロックごとの最小値・最大値) | 範囲で絞り込む列をソートキーにする | |
| / | ファイル内の行グループごとの統計 (最小値・最大値) | よく絞り込む列で並べてから書くと、統計で読み飛ばせる範囲が増える |
| 、 | 「とベクトルインデックス」の章 |
④圧縮とファイル形式
| コーデック | 圧縮率 | 速さ | 分割可能性 (並列に読めるか) |
|---|---|---|---|
| gzip | 高い | 遅め | 分割できない (CSV・ の1ファイルを1つの処理でしか読めない) |
| bzip2 | 高い | 遅い | 分割できる |
| Snappy | 中くらい | 速い | / の中では分割できる |
| ZSTD | 高い | 速い | Parquet / ORC の中では分割できる |
Parquet や ORC の中で使う圧縮は、行グループごとに圧縮されるため、ファイルを並列に読めます。CSV を gzip で固めた大きな1ファイルは並列に読めず遅くなるため、ファイルを分けるか、Parquet へ変換します。形式の比較と変換の方法は「データ形式と形式の変換」の章で扱います。
では列ごとに圧縮エンコーディング (AZ64、ZSTD など) を設定し、ENCODE AUTO で自動に任せられます。
⑤ファイルサイズとコンパクション ⚠️ 頻出
のでは、が大量にあると、ファイルを開く処理とメタデータの一覧が支配的になり、・・ が遅くなります (小さいファイル問題)。
| 原因 | 対処 |
|---|---|
| ストリーミングから数秒ごとに書いている | のバッファのサイズと時間を大きくする |
| Spark のパーティション数が多すぎる | 書き込む前に coalesce / repartition でファイル数を減らす。 の groupFiles でまとめて読む |
| の頻繁な更新 | (Athena の OPTIMIZE、Glue のテーブル最適化、 の自動保守) |
| すでにたまった小さいファイル | 定期的なジョブ (Glue、Athena の / INSERT INTO) で大きなファイルにまとめ直す |
目安として、1ファイル 128 MB〜1 GB 程度の にそろえると、並列度と読み取りの効率のバランスがよくなります。
⑥その他の最適化手法
| 手法 | 内容 |
|---|---|
| 必要な列だけを SELECT する | SELECT * を避ける。列指向形式では読む列が減る |
| 述語のプッシュダウン | の push_down_predicate、 のフィルターを読み込みの直後に書く |
| の解消 | 偏ったキーにランダムな接尾辞を付けて分散する () |
| 事前の集計 | 、curated ゾーンの集計テーブル |
| 結果の再利用 | のクエリ結果の再利用、 の結果 |
| 統計の更新 | Redshift の ANALYZE、Glue Data Catalog の列の統計 |
試験対策
①判断マトリクス
| 問題文のキーワード | 選ぶもの |
|---|---|
| 日付で絞るクエリのスキャン量を減らす | 日付でパーティション分割 |
| パーティションが多すぎて遅い、が大量 | 粒度を粗くする、 |
| 顧客 ID のような値の多い列で絞り込み・結合 | 化 |
| 一部の列しか使わない分析 | / (列指向) |
| 大きな gzip の CSV の読み込みが並列にならない | ファイルを分割、または Parquet + Snappy / ZSTD へ変換 |
| の検索が遅い | WHERE・JOIN の列にインデックス |
| インデックスを増やしたら書き込みが遅くなった | 使っていないインデックスを削除 |
| の範囲の絞り込みを速く | |
| から小さいファイルが大量にできる | バッファのサイズと時間を大きくする |
| 一部のタスクだけ長い (偏り) | などでを解消 |
②よくある誤答
- ユーザー ID でパーティションを切ること。パーティションが膨大になります。値の多い列は化を検討します。
- にインデックスを作ろうとすること。Redshift はとゾーンマップで絞り込みます。
- 圧縮率だけで gzip を選び、大きな1ファイルにすること。並列に読めず遅くなります。
- のすべての列にインデックスを作ること。書き込みと保管の負担が増えます。