Hive パーティションとバケットの例

⚡ スマートサマリー

Apache Hiveでは、パーティションとバケットという2つの方法でテーブルデータをディスク上で分割します。パーティションはキー値ごとに1つのディレクトリを作成し、バケットは行をハッシュ化して固定数のファイルに格納します。

  • 🗂️ パーティションはディレクトリです。 パーティションキーのそれぞれの固有値は、HDFS内のテーブルフォルダの下に独自のサブディレクトリとして作成されます。
  • ✂️ パーティション剪定: パーティションキーでフィルタリングするクエリは、テーブル全体をスキャンするのではなく、一致するディレクトリのみを読み取ります。
  • ⚙️ ダイナミックモードが必要です。 SELECT から多数のパーティションをロードするには、hive.exec.dynamic.partition.mode を nonstrict に設定する必要があります。
  • 🧮 バケットはファイルです。 CLUSTERED BYは、選択した列をハッシュ化し、各行を固定数のファイルのうちの1つに書き込みます。
  • 🔍 サンプリングと結合: バケットテーブルは、通常のテーブルでは不可能な効率的なTABLESAMPLE読み取りとマップサイドバケット結合をサポートします。
  • 📐 基数で選択する: 状態や日付などのカーディナリティの低い列でパーティション分割し、ユーザー識別子などのカーディナリティの高い列をバケット化する。

Hiveのパーティションとバケットを、具体的な例を用いて解説します。

テーブル、パーティション、バケットは、Hiveのデータモデリングを構成する要素です。テーブルはスキーマを定義し、パーティションはそのテーブルをディスク上のディレクトリに分割し、バケットはディレクトリ内のデータを固定数のファイルに分割します。

パーティションとは何ですか?

Hive Partitionsは、パーティションキーに基づいてテーブルを異なる部分に分割することで、テーブルをパーティションに整理する方法です。物理的には、各パーティションはHDFSのテーブルフォルダの下にある個別のサブディレクトリであり、これによりHiveは不要なデータをスキップすることができます。

テーブルに1つ以上のパーティションキーがある場合、パーティション機能は役立ちます。パーティションキーは、テーブル内でデータがどのように格納されるかを決定するための基本要素です。パーティションキーはデータファイル自体には格納されず、その値はディレクトリ名にエンコードされるため、そのキーでフィルタリングするクエリでは、行が読み込まれる前にディレクトリ全体を破棄することができます。これをパーティションプルーニングと呼びます。

例えば: -

「クライアントは、インド国内の事業に関するEコマースデータを保有しており、そのデータには各州(38州)の事業がまとめて記載されています。州列をパーティションキーとして、インド全体のデータに対してパーティション分割を実行すると、インドの州数(38)と同じ数のパーティション(38個)が得られます。これにより、各州のデータをパーティションテーブル内で個別に表示できます。」

サンプル Code パーティションのスニペット

以下の6つのステートメントは、Hiveシェルで順番に実行されます。それぞれが独立したステップであり、続くスクリーンショットは、実際のクラスタで同じシーケンスが実行されている様子を示しています。

  1. テーブル allstates の作成
    create table allstates(state string, District string,Enrolments string)
    
    row format delimited
    
    fields terminated by ',';
    
  2. 作成したテーブル allstates にデータをロードします
    Load data local inpath '/home/hduser/Desktop/AllStates.csv' into table allstates;
  3. パーティションテーブルの作成
    create table state_part(District string,Enrolments string) PARTITIONED BY(state string);
  4. パーティションの場合、このプロパティを設定する必要があります
    set hive.exec.dynamic.partition.mode=nonstrict
  5. パーティションテーブルへのデータのロード
    INSERT OVERWRITE TABLE state_part PARTITION(state)
    SELECT district,enrolments,state from  allstates;
  6. パーティションキーとしての状態に基づいた実際の処理とパーティションテーブルの形成

HDFSストレージには、ファイル名が状態名となるパーティション出力が38個作成されます。このステップでは、これを確認します。

以下のスクリーンショットは、上記コードの実行結果を示しています。

最初の画面では、ステップ 1 が実行されます。 ハイブ> プロンプトを作成し、 オールステーツ 3つの列とフィールド区切り文字としてカンマを持つ表。

Hiveシェルで、区切り文字付きの3つの列を持つallstatesテーブルを作成する

次の画面では手順2と3について説明します。AllStates.csvファイルが読み込まれ、 オールステーツパーティションテーブル 状態部分 パーティションキーとして宣言された状態を使用して作成されます。

AllStates.csvをallstatesにロードし、パーティションテーブルstate_partを作成します。

ステップ4と5が次に表示されます。動的パーティションモードは非厳格に設定され、INSERT OVERWRITEステートメントによってMapReduceジョブが起動されます。そのログ行には、各状態値に対して1つのパーティションがロードされていることが示されます。

MapReduceジョブの出力は、状態値ごとに1つのHiveパーティションをロードします。

HDFS 内のウェアハウス ディレクトリを一覧表示すると、ステップ 6 の結果が確認できます。シェルは 38 個のアイテムを報告し、1 つのアイテムを報告します。 state_part/state= 州ごとのディレクトリ。

HDFSリストには、Hiveウェアハウス内の38個のstate_partパーティションディレクトリが表示されています。

上記のコードから、次のことを行います

  1. 「州」「地区」「登録者数」などの3つの列名を持つテーブル「allstates」を作成します。
  2. テーブル allstates にデータをロードしています
  3. 状態をパーティションキーとして使用するパーティションテーブルの作成
  4. この手順では、パーティションモードを非厳格に設定します(このモードでは動的パーティションモードが有効になります)。
  5. パーティションテーブル state_part にデータをロードしています
  6. パーティションキーとしての状態に基づいた実際の処理とパーティションテーブルの形成
  7. HDFSストレージには、ファイル名が状態名となるパーティション出力が38個作成されます。このステップでは、HDFS内の38個のパーティション出力を確認します。

Hiveにおける静的パーティショニングと動的パーティショニング

上記の例では動的パーティショニングを使用していますが、Hive は 2 つのロード スタイルをサポートしており、その違いによって、どれだけの ty を使用するかが決まります。ping そして、それぞれの荷物がどれだけのリスクを伴うか。

静的パーティショニング ステートメント自体にパーティション値を指定するため、ロードを実行する前にその値を把握しておく必要があり、1つのステートメントは正確に1つのパーティションを埋めます。 動的パーティショニング これにより、HiveはSELECTリストの最後の列からパーティション値を読み取り、実行時にディレクトリを作成します。そのため、この例ではINSERTを1回実行するだけで38個のディレクトリを作成できます。

側面 静的パーティショニング 動的パーティショニング
パーティション値 PARTITION条項に手渡しで記載 実行時にSELECT列から読み込む
ステートメントごとのパーティション数 1 その他にもたくさんのグーグルの
  デフォルトの厳格モードで動作します hive.exec.dynamic.partition.mode を nonstrict に設定する必要があります
負荷速度 値のスキャンが不要なため、より高速です。 ジョブが行をキーごとにグループ化するため、処理速度が遅くなります。
に最適 日々の負荷など、小さく既知のセット 38州などの大規模または未知のキーセット

動的負荷にも上限があります。Hive では、1 つのジョブが作成できるパーティションの数に制限があり、デフォルトではマッパーまたはリデューサーごとに 100、ステートメント全体で 1000 となっています。いずれかの上限を超えるとジョブは失敗するため、カーディナリティが非常に高いキーを使用する場合は、これらの制限を引き上げるか、別の設計を検討する必要があります。

バケットとは何ですか?

Hiveのバケットは、Hiveテーブルのデータを複数のファイルまたはディレクトリに分割するために使用されます。バケットは効率的なクエリ処理に役立ち、パーティションとは異なり、テーブル作成時にバケット数が固定されるため、データ量の増加に伴ってバケット数が増大することはありません。

  • これらのパーティションに存在するデータは、さらにバケットに分割することができます。
  • 分割は、テーブル内で選択した特定の列のハッシュに基づいて実行されます。
  • バケットはバックエンドで何らかのハッシュアルゴリズムを使用して各レコードを読み取り、バケットに配置します。
  • 列がどのバケットに入るかは、 hash_function(bucketing_column) mod num_buckets同じ値は常に同じファイルに保存される
  • Hive 0.x および 1.x では、バケット化を有効にするには、 hive.enforce.bucketing=true を設定します。 挿入前に

最後の設定は、現在のクラスターでは履歴として扱われます。 Apache Hiveのマニュアル Hive 2.x以降では、エンジンがテーブル定義からリデューサー数とクラスタリング列を自動的に選択するため、この設定は不要です。

ステップ1) 以下のようにバケットを作成します。

下の画面は、CREATE TABLE ステートメントを示しています。 サンプルバケット下部の CLUSTERED BY 句でバケット数を固定します。

HiveのCREATE TABLE文で、samplebucketを4つのバケットにクラスタリングする。

上のスクリーンショットより

  • first_name、job_id、department、salary、countryなどの列名を持つサンプルバケットを作成しています。
  • ここでは4つのバケットを作成しています
  • データが読み込まれると、自動的に4つのバケットにデータが振り分けられます。
  • 国列はクラスタリング列であるため、1つの国に関するすべての行は同じバケットファイルに書き込まれます。

ステップ2) データをテーブル samplebucket にロードしています

Hiveシステムに「employees」テーブルが既に作成されていることを前提として、このステップではemployeesテーブルからsamplebucketテーブルへのデータのロードを見ていきます。

従業員データをバケットに移動する前に、データにfirst_name、job_id、department、salary、countryなどの列名が含まれていることを確認してください。

ここでは、employeesテーブルからsamplebucketにデータをロードしています。下の画面は、コピーを実行するINSERT OVERWRITEステートメントを示しています。

INSERT OVERWRITE ステートメントで、従業員行を samplebucket テーブルにコピーします。

ステップ3) ステップ1で作成した4つのバケットを表示します。

HDFS内のテーブルディレクトリを一覧表示すると、物理的な結果として、1つのデータファイルではなく、4つの番号付きデータファイルが表示されます。

samplebucketディレクトリの下に作成された4つのバケットファイルのHDFSリスト

上記のスクリーンショットから、従業員テーブルのデータがステップ1で作成された4つのバケットに転送されていることがわかります。

Hiveのパーティショニングとバケット化:主な違い

どちらの機能もテーブルをより小さな断片に分割しますが、ファイルシステムの異なるレベルで分割を行い、異なる問題に対応します。以下の表は、それらを並べて比較したものです。

比較のポイント パーティショニング バケット
ユニットが作成されました キー値ごとのディレクトリ ハッシュバケットごとに1つのファイル
宣言済み 分割基準 …によってn個のバケットにグループ化されます
個数 異なる値の数に応じて増加する テーブル作成時に修正
データファイルに保存されている列 いいえ、値はディレクトリ名の中にあります はい、列は通常の列のままです
最適な列タイプ 州、年、国などのカーディナリティの低い要素 ユーザーIDやトランザクションIDなど、カーディナリティの高いもの
主なメリット パーティションの整理により、不要なディレクトリがスキップされます。 ファイルサイズが均一で、サンプリングコストが低く、マップ側で結合できる

この2つは競合関係にありません。一般的な運用レイアウトでは、ファクトテーブルを日付で分割し、結合キーに基づいて各日をバケットに分割します。そのため、クエリはまず1つのディレクトリに絞り込み、そのディレクトリ内でバケット間を結合します。

パーティショニング、バケット化、またはその両方を使用するタイミング

どちらを選択するかは、まず列のカーディナリティと、テーブルを読み取るクエリの構造によって決まります。

  • パーティション クエリがほぼ常に同じ低カーディナリティ列でフィルタリングされ、一意の値の数が数百万ではなく数百にとどまる場合
  • バケット 有用な列にディレクトリとして扱うには多すぎる一意の値がある場合、またはその列でテーブルが繰り返し結合またはサンプリングされる場合
  • 両方を使う 大規模なファクトテーブルの場合:日付でパーティションを作成し、各パーティション内で結合キーに基づいてバケットを作成します。

注意すべき障害モードは、小ファイル問題です。タイムスタンプや顧客識別子など、カーディナリティが非常に高い列でパーティショニングを行うと、HDFSブロックサイズをはるかに下回るファイルを格納する小さなディレクトリが数千個生成されます。これによりNameNodeのメモリが膨張し、スキャン速度が低下します。これはまさにパーティショニングの目的である問題です。バケット化では、ファイル数がテーブル定義によって制限されるため、この問題を回避できます。

バケット化には注意点があります。レイアウトが正しく機能するのは、すべての書き込み処理がそのレイアウトを遵守する場合に限られます。作成時に宣言されるバケット数はメタデータであるため、正しくクラスタリングせずにテーブルに書き込むジョブは、宣言されたレイアウトと一致しないファイルを残す可能性があり、後続のサンプリングやバケット結合で誤った行が読み取られることになります。

よくあるご質問

Hiveシェルで「SHOW PARTITIONS table_name」コマンドを実行します。このコマンドはメタストアを読み込み、パーティションディレクトリごとに1行ずつ出力します。これはHDFS内のウェアハウスパスを一覧表示するよりも高速で、メタストアが同期されていることも確認できます。

ALTER TABLE table_name ADD PARTITION (state='Goa') は新しいディレクトリを登録し、ALTER TABLE table_name DROP PARTITION (state='Goa') はそれを削除します。ping 管理パーティションはデータを削除しますが、ドロップping 外部のものはメタストアのエントリのみをクリアします。

Hive では、デフォルトでは動的パーティションの数がノードあたり 100、ステートメントあたり 1000 に制限されています。これを超える異なる値を持つキーを使用すると制限を超え、ジョブが強制終了します。hive.exec.max.dynamic.partitions.pernode と hive.exec.max.dynamic.partitions の値を上げるか、より粗いキーを選択してください。

いいえ。Hive 0.x および 1.x では、リデューサー数をバケット数と一致させるために必要でした。HIVE-12331 により Hive 2.0 で削除され、現在はエンジンがリデューサー数とクラスタリング列の両方をテーブルから取得するようになっています。

TABLESAMPLE(BUCKET x OUT OF y ON column) は、すべてのバケットファイルをスキャンするのではなく、一致するバケットファイルのみを読み取ります。書き込み時に行が同じ列でハッシュ化されているため、このサンプルは再現性が高く、ランダムな行フィルタよりもはるかに低コストです。

テーブルを数千個の小さなファイルに分割すると、NameNodeのメモリが無駄になり、ファイルごとに1つのタスクが開始されるため、スキャン速度が低下します。これは通常、カーディナリティが非常に高いパーティションキーに起因します。キーの粗さを粗くしたり、バケット化したり、ファイルを圧縮したりすることで解決できます。

はい。Cloudera Workload XMなどのツールに搭載されているクエリログ分析と機械学習モデルは、フィルタ頻度、偏り、カーディナリティに基づいて列をランク付けし、レイアウトを提案します。ただし、計画されたワークロードは考慮されていないため、提案内容にはレビューが必要です。

CopilotはコメントからPARTITIONED BYおよびCLUSTERED BYステートメントを迅速に作成し、エージェントアシスタントはロードスクリプト全体を生成できます。モデルは名前のみに基づいて推測するため、生成されたバケット数とキーを実際のカーディナリティと必ず照合してください。