😎

Lookerの備忘録③ 増分PDTとはなんぞやについて。差分だけを更新したら効率的だよねの話

2024/12/09に公開

前回の続きです

LookerでPDT(Persistent Derived Table)を活用する際、例えば大規模データのテーブルを使って毎回PDT構築していると、新しいデータの追加によって構築時間は増えていくし、構築コストも増えていきます。
そうするとWH側への負担も気になるところです。全てを再構築するPDTの方法よりも効果的な方法を紹介します。

例の如くこの公式ドキュメントを参照しております

増分PDT(Incremental PDT)は、テーブル全体ではなく、新規・更新部分のみを差分的に再構築する仕組みを提供し、大規模データセットのPDT更新を効率化します。

本記事では、増分PDTにおけるincrement_keyパラメータを中心に、その基本的な概念と実装手順、具体例を紹介します。

増分PDT(Incremental PDT)とは

通常のPDTは指定されたタイミングでテーブル全体を再構築しますが、増分PDTでは「追加・更新分」だけをPDTに追記し、不要な再計算を削減できます。これにより、以下のメリットが期待できます。

  • 再構築時間の短縮:全件再計算ではなく、新規データ分のみ反映するため、処理負荷が軽減。
  • コスト削減:クラウドDWH(例:Snowflake)使用時、スキャンコストを抑えられる可能性大。
  • リアルタイム性向上:更新頻度が高いテーブルでも短時間で最新状態を反映できる。

increment_keyパラメータの役割

increment_keyは、増分PDTを有効化するために使用するLookMLパラメータです。
increment_keyで指定したカラム(キー)は、前回PDT更新以降の新規・更新データを特定するために利用され、Lookerは指定されたキーをもとに差分的なINSERTを行います。

基本的な要件

  • increment_keyは単調に増加する値を持つ列であること(例:タイムスタンプや連番IDなど)。
  • 新規行や更新行がこのキーにより一意に判別できる必要があります。

増分PDTのLookML例

以下は、ordersテーブルを増分PDTとして扱う例です。order_idが増分キーとして単調増加する場合、increment_keyorder_idを指定します。

view: incremental_orders {
  derived_table: {
    sql:
      SELECT
            order_id
            ,user_id
            ,order_total
            ,order_date
      FROM
             WH.schema.orders
    ;;

    datagroup_trigger: order_dg_24hours
    # 増分PDT設定
    increment_key: "order_id"   # 新規データを判断するためのキー, stringで提供しないとエラー出ます
    # increment_offset: 0  # (オプション) 0だとPDT作成時のorder_idまでは無視して、そのorder_idより新しいorder_idのみ考慮するよってこと
  }

  dimension: order_id {
    type: number
  }

  dimension: user_id {
    type: number
  }

  measure: total_revenue {
    type: sum
    sql: ${TABLE}.order_total ;;
  }

  dimension: order_date {
    type: time
    sql: ${TABLE}.order_date ;;
  }
}

この設定を行うと、incremental_ordersというPDTはorder_idを使って新しい行のみを差分的にPDTへ追加します。

increment_offsetの活用

increment_offsetは、増分更新時に少し前のデータも再取得し、欠損や遅延更新を補正するためのオプションです。
たとえばincrement_offset: 3を指定すると、increment_keyがadte型である場合は過去3日間分のデータを再度取得し、抜けや遅延挿入があった場合でもPDTに反映できます。
increment_offset: 0にしていれば最新だけ考慮できます。

increment_offsetをさらに深堀り

increment_offsetは、増分PDTを構築する際に“わざと少し過去分のデータ”も再取得するための仕組みです。
なぜそのような設定が必要なのでしょうか?
以下に、increment_offsetを利用する代表的なケースや、実際のLookML例、考慮すべきポイントを詳しく解説します。

なぜincrement_offsetが有用なのか?

  • 遅延更新や非同期処理に対応
    データパイプラインによっては、トランザクションやイベントが遅れてDWHに着地することがあり、直近の増分更新では取りこぼしてしまう可能性があります。
    increment_offsetを用いて、たとえば「直近3日分」も再度読み込むことで、そうした遅延分のデータもキャッチアップでき、PDTの整合性を保てます。

  • 追記が間に合わないケースへの対応
    分散システムやストリーミング処理でデータが到達するまでにタイムラグがある場合も似た効果を発揮します。

  • バッファによる安全策
    システムの異常やメンテナンスなどで一時的にデータ反映が止まる場合でも、バッファ期間を設けておくことで、後から到着したデータを取りこぼすことなくPDTに反映可能です。
    このバッファによる安全策はかなーーり有効だと思ってます。これはLookerに限らずではありますが。

DatagroupとIncrement_keyとの併用

Datagroup(datagroup_trigger)とIncremental PDT(increment_key)の設定は共存可能ですが使い方には注意なので説明します。
両者は役割が異なるため、組み合わせることでより柔軟な運用を行うことができますし、PDT構築のタイミングや構築のきっかけがなかなかわかりづらいのでまとめていくと

まずはそれぞれの概念を再確認

  • Datagroup
    データ更新サイクル全体を管理し、一定のトリガー条件(sql_triggermax_cache_age)が満たされた際にPDTを再計算する仕組みです。
    複数のPDTやExploreキャッシュを一括で制御可能で、全体的な「いつ再構築するか」を管理します。

  • Incremental PDT(increment_key
    PDT再構築時に「テーブル全件を作り直す」のではなく、「新規・変更部分のみ差分適用」する方法を提供します。
    increment_keyincrement_offsetを用いて、実際のPDT再計算時に再読込するデータ範囲を絞り、処理コストを削減します。

  • PDTメンテナンススケジュール(Datagroup and PDT Maintenance Schedule)
    PDT構築のきっかけを確認するスケジュール。connectionにて設定できる
    この場合、この設定が最優先で確認されます。

両者を組み合わせた場合のイメージ

  1. Datagroupトリガーによる再計算タイミングの制御
    Datagroupを使って、「1日1回」あるいは「特定のテーブルのupdated_atが変わったら」など、再計算の「きっかけ」を管理します。
    が、max_cache_age: 24 hoursとかにしていると24時間後にPDTメンテナンススケジュール(Datagroup and PDT Maintenance Schedule) によって結局フルビルドされてしまいます。なので初回フルビルドでincrement_keyを発動させないようにderived_tableで工夫が必要。

  2. Incremental PDTによる差分更新
    Datagroupがcacheの関係で作動しなくても、PDTメンテナンススケジュール(Datagroup and PDT Maintenance Schedule) によってPDTに関する最新のデータがあるかないかを確認しますので、その時にIncremental PDTの設定(increment_keyincrement_offset)が有効であれば、PDT再計算は差分更新のみで行われます。
    これにより、毎日フルビルドさせなくとも、必要な部分のみを更新するためコスト・時間を節約できます。フルビルドはデータの鮮度もあるので週1とかでいいでしょう。

例:
シナリオはフルビルドは初回のみ、それ以降は週1フルビルドさせるが、sql_triggerによって基本は毎日きっかけは用意する。(元データの最新日時が更新されている時、とかカウント数が変わった時など)
そのきっかけで作動するのはincrement_keyで、それはorder_date
新しい追加日付のみ考慮するので、increment_offset: 0にしておく

この時最重要なのが{% incrementcondition %} order_date {% endincrementcondition %}で、これは最初のビルドの時には作動しないので、全期間のデータをビルドしてくれて、次回以降はincrement_keyの設定に従ってこの条件式が発動する。
つまりこれによって最新の日付の結果だけをこのクエリーは返すので、それを増分(increment)させるということができる

datagroup: orders_dg_168hours {
  sql_trigger: SELECT MAX(updated_at) FROM WH.schema.orders ;;
  max_cache_age: "168 hours" ## 週1のフルビルドにしておく
}
view: incremental_orders_summary {
  derived_table: {
    sql:
      SELECT
            order_date
            ,user_id
            ,order_total
      FROM
            WH.schema.orders
      WHERE {% incrementcondition %} order_date {% endincrementcondition %}
    ;;
    datagroup_trigger: orders_dg_168hours ##PDTはこれで維持する。キャッシュ時間はあれど、sql_triggerによって常に設定された時間にPDT更新のきっかけは探されているので大丈夫
    increment_key: "order_date" ## 日付できっかけにする
    increment_offset: 0 ## 常に最新の追加データのみを考慮させる
  }

  dimension: order_date { type: date }
  dimension: user_id { type: number }
  measure: order_total {
    type: sum
    sql: ${TABLE}.order_total ;;
  }
}

まとめ

増分PDT(Incremental PDT)は、大量データを扱うLooker環境で、PDT再構築のコストを抑えながらスピーディにかつ安全にデータを保持する方法だと思います。

  • increment_keyを用いて新規・更新部分のみ差分取得
  • increment_offsetで微調整し、遅延更新や抜け漏れに対応
  • 大規模データセットや頻繁な更新が求められるシナリオで特に有用
  • Datagroupと組み合わせることで既存のPDTを活用しつつ増分だけ更新させてコストや構築時間を短縮可能

公式ドキュメントを参考に、増分PDT活用によってLooker環境のパフォーマンスとコストバランスを最適化してみてください!

次は集計認識(aggregate_table)について

Discussion