コンテンツにスキップ

DQL


DQL(Debug Query Language)は、Guanceプラットフォームの中核となるクエリ言語であり、時系列データ、ログデータ、イベントデータなどを効率的にクエリ・分析するために設計されています。DQL は SQL の意味表現と PromQL の構文構造を組み合わせ、柔軟かつ強力なクエリツールを提供します。

このドキュメントでは、DQL の基本構文と設計思想をすばやく理解し、サンプルを通じて DQL クエリの作成方法を説明します。

基本クエリ構造

DQL の基本クエリ構造は以下のとおりです。

namespace[index]::datasource[:select-clause] [{where-clause}] [time-expr] [group-by-clause] [having-clause] [order-by-clause] [limit-clause] [sorder-by-clause] [slimit-clause] [soffset-clause]

index は省略可能です。省略時はデフォルトで default インデックスが使用されます。

実行順序

DQL クエリの実行順序は重要で、クエリの意味とパフォーマンスを決定します。

  1. データフィルタリング:namespace::datasource、where-clause、time-expr に基づいてデータをフィルタリングします。

    • データソースを特定
    • WHERE 条件を適用して生データ行をフィルタリング
    • 時間範囲フィルタリングを適用
    • この段階で可能な限り早くデータをフィルタリングし、後続処理の効率を向上
  2. 時間集計:time-expr に rollup が含まれている場合、最初に rollup ロジックを実行します。

    • Rollup 関数は時間軸でデータを前処理します。
    • Counter タイプのメトリクスでは、通常、生の値ではなくレートや増分を計算します。
    • Gauge タイプのメトリクスでは、last、avg などの集計関数が使用される場合があります。
  3. グループ集計:group-by-clause を実行してグループ化し、グループ内で select-clause の集計関数を実行します。

    • BY 句の式に従ってデータをグループ化
    • 各グループ内で集計関数(sum、count、avg、max、min など)を計算
    • 時間ウィンドウも同時に指定されている場合、2次元データ構造が形成されます。
  4. グループフィルタリング:having-clause を実行して集計グループをフィルタリングします。

    • HAVING 句は集計後の結果に対して作用します。
    • 集計関数の結果を使用してフィルタリングできます。
    • これが WHERE 句との重要な違いです。
  5. 非集計関数:select-clause 内の非集計関数を実行します。

    • 集計を必要としない式や関数を処理
    • 集計結果に対してさらに計算を実行
  6. グループ内ソート:order-by-clause、limit-clause を実行して、グループ内のデータをソートおよびページングします。

    • ORDER BY は各グループ内で独立して実行されます。
    • LIMIT は各グループが返すデータ行数を制限します。
  7. グループ間ソート:sorder-by-clause、slimit-clause、soffset-clause を実行して、グループをソートおよびページングします。

    • SORDER BY はグループ自体をソートします。
    • グループ結果を次元圧縮する必要があります(max、avg、last などの関数を使用)。
    • SLIMIT は返されるグループ数を制限します。

完全な例

完全な例を通じて DQL の構造を理解しましょう。

M::cpu:(avg(usage) as avg_usage, max(usage) as max_usage) {host =~ 'web-.*', usage > 50} [1h::5m] BY host, env HAVING avg_usage > 60 ORDER BY time DESC LIMIT 100 SORDER BY avg_usage DESC SLIMIT 10

このクエリの意味は次のとおりです。

  • 名前空間:M(メトリクスデータ)
  • インデックスproduction
  • データソースcpu
  • 選択フィールドusage フィールドの平均値と最大値
  • 時間範囲:過去1時間、5分単位で集計
  • フィルタ条件:ホスト名が web で始まり、CPU 使用率が 50% より大きい
  • グループ化:ホスト名と環境でグループ化
  • グループフィルタ:平均 CPU 使用率が 60% より大きい
  • ソート:時間降順、各グループ最大100件
  • グループ間ソート:平均使用率降順、最大10グループ

名前空間(namespace)

名前空間はデータの種類を区別するために使用され、各データタイプには特定のクエリ方法と保存戦略があります。DQL は複数のビジネスデータタイプのクエリをサポートします。

名前空間 説明 典型的な用途
M Metric、時系列メトリクスデータ CPU 使用率、メモリ使用量、リクエスト数など
L Logging、ログデータ アプリケーションログ、システムログ、エラーログなど
O Object、インフラストラクチャオブジェクトデータ サーバー情報、コンテナ情報、ネットワークデバイスなど
OH History object、オブジェクト履歴データ サーバー構成変更履歴、パフォーマンス指標履歴など
CO Custom object、カスタムオブジェクトデータ ビジネスカスタムオブジェクト情報
COH History custom object、カスタムオブジェクト履歴データ カスタムオブジェクトの変更履歴
N Network、ネットワークデータ ネットワークトラフィック、DNS クエリ、HTTP リクエストなど
T Trace、トレース呼び出しデータ 分散型トレーシング、トレース分析など
P Profile、プロファイリングデータ パフォーマンスプロファイリング、CPU フレームグラフなど
R RUM、ユーザーアクセスデータ フロントエンドパフォーマンス、ユーザー行動分析など
E Event、イベントデータ アラートイベント、デプロイイベント、システムイベントなど
UE Unrecovered Event、未復旧イベントデータ 未解決のアラートとイベント
B Cloud billing、クラウド請求

インデックス(index)

インデックスは DQL クエリ最適化の重要なメカニズムであり、従来のデータベースにおけるテーブルまたはパーティションと考えることができます。単一の名前空間内でも、データソース、データ量、アクセスパターンなどの要因により、データを分割してクエリパフォーマンスと管理効率を向上させることがあります。

インデックスの役割

  • パフォーマンス最適化:インデックスによりデータを分散保存し、単一クエリのデータスキャン量を削減
  • データ分離:異なるビジネス、環境、時間範囲のデータを異なるインデックスに保存可能
  • 権限管理:インデックスごとに異なるアクセス権限を設定可能
  • ライフサイクル管理:インデックスごとに異なるデータ保持ポリシーを設定可能

インデックスの命名規則

  • インデックス名は明示的に宣言できます。明示的に宣言しない場合、デフォルトで default が使用されます。
  • インデックス名を明示的に宣言する場合、ワイルドカードや正規表現でのマッチングはサポートされません。
  • インデックス名は通常、データのビジネス属性を反映します(例:productionstagingweb-logsapi-logs)。

基本構文

// デフォルトインデックスを使用(指定しない場合、自動的に default が使用される)
M::cpu           // M("default")::cpu と同等
L::nginx         // L("default")::nginx と同等

// 単一インデックスを指定
M("production")::cpu     // production インデックスの CPU メトリクスをクエリ
L("web-logs")::nginx     // web-logs インデックスの Nginx ログをクエリ

// 複数インデックスクエリ(複数のインデックスのデータを同時にクエリ)
M("production", "staging")::cpu     // 本番環境とステージング環境の CPU メトリクスをクエリ
L("web-logs", "api-logs")::nginx     // Web ログと API ログをクエリ

インデックスとパフォーマンス

インデックスを適切に使用することで、クエリパフォーマンスを大幅に向上できます。

  • 正確なインデックス:データがどのインデックスにあるか明確な場合、そのインデックスを直接指定
  • 複数インデックスクエリ:複数のインデックスにまたがるクエリが必要な場合、ワイルドカードではなく複数インデックス構文を使用
  • 全インデックススキャンの回避:インデックスと WHERE 条件の組み合わせにより、データスキャン範囲を削減

互換性構文(非推奨)

歴史的な理由により、DQL は where 句でのインデックス指定もサポートしていますが、推奨されません。

// 旧構文、非推奨
L::nginx { index = "web-logs" }
L::nginx { index IN ["web-logs", "api-logs"] }

適用例

// 本番環境の CPU 使用率をクエリ
M::cpu:(avg(usage)) [1h] BY host

// 本番環境とステージング環境の比較をクエリ
M("production", "staging")::cpu:(avg(usage)) [1h] BY index, host

// Web サーバーログを分析
L("web-logs")::nginx:(count(*)) {status >= 400} [1h] BY status

// Web サーバーと API サーバーのエラー率を比較
L("web-logs", "api-logs")::*:(count(*)) {status >= 500} [1h] BY index

データソース(datasource)

データソースは、クエリの具体的なデータソースを指定します。データセット名、ワイルドカードパターン、正規表現、またはサブクエリを指定できます。

基本データソース

名前空間ごとにデータソースの定義が異なります。

名前空間 データソースタイプ
M メジャーメント(measurement) cpu, memory, network
L データソース(source) nginx, tomcat, java-app
O インフラストラクチャオブジェクト分類 host, container, process
T サービス名(service) user-service, order-service
R RUM データタイプ session, view, resource, error

データソース構文

データソース名の指定

M::cpu:(usage)                    // CPU メトリクスをクエリ
L::nginx:(count(*))                // Nginx ログをクエリ
T::user-service:(traces)           // ユーザーサービストレースをクエリ

ワイルドカードマッチ

M::*:(usage)                      // すべてのメトリクスをクエリ

正規表現マッチ

M::re('cpu.*'):(usage)            // cpu で始まるメトリクスをクエリ
L::re('web.*'):(count(*))          // web で始まるログをクエリ
T::re('.*-service'):(traces)      // -service で終わるサービスをクエリ

サブクエリデータソース

サブクエリは、DQL で複雑な分析を実現するための重要な機能であり、あるクエリの結果を別のクエリのデータソースとして使用することを可能にします。このネストされたクエリメカニズムは、多層的な分析ニーズをサポートします。

実行メカニズム

サブクエリの実行は以下の原則に従います。

  1. 逐次実行:内側のサブクエリが先に実行され、その結果が外側のクエリのデータソースとして使用されます。
  2. 結果のカプセル化:サブクエリの結果は一時テーブル構造にカプセル化され、外側のクエリで使用されます。
  3. 名前空間の混合:サブクエリは異なる名前空間の混合クエリをサポートし、クロスデータタイプ分析を実現します。
  4. パフォーマンスの考慮:サブクエリは計算の複雑さを増加させるため、クエリロジックを適切に設計する必要があります。

基本構文

namespace::(subquery):(projections)

実行プロセス

典型的なサブクエリの例:

L::(L::*:(count(*)) {level = 'error'} BY app_id):(count_distinct(app_id))

実行プロセス:

  1. 内側サブクエリL::*:(count(*)) {level = 'error'} BY app_id

    • すべてのログデータをスキャン
    • エラーレベルのログをフィルタリング
    • app_id ごとにグループ化してエラー数をカウント
    • 一時テーブルを生成:app_id | count(*)
  2. 外側クエリL::(...):(count_distinct(app_id))

    • サブクエリ結果をデータソースとして使用
    • 異なる app_id の数をカウント
    • 最終結果:エラーが発生したアプリケーションの数

適用例

// エラーが発生したアプリケーションの数をカウント
L::(L::*:(count(*)) {level = 'error'} BY app_id):(count_distinct(app_id))

// CPU 使用率が高いサーバーを分析
M::(M::cpu:(avg(usage)) [1h] BY host {avg(usage) > 80}):(count(host))

// エラー率が 1% を超えるサービスエンドポイントを特定し、影響を受けるサービス数をカウント
M::(M::http_requests:(sum(request_count), sum(error_count)) [1h] BY service, endpoint
   {sum(error_count) / sum(request_count) > 0.01}
):(count(service))

Select 句(select-clause)

Select 句は、クエリが返すフィールドや式を指定するために使用され、DQL クエリの最も基本的かつ重要な部分の1つです。

フィールド選択

基本構文

// 単一フィールドを選択
M::cpu:(usage)

// 複数フィールドを選択
M::cpu:(usage, system, user)

// すべてのフィールドを選択
M::cpu:(*)

フィールド名のルール

フィールド名は以下の形式で記述できます。

  1. 名前を直接記述:通常の識別子に適用

    • message
    • host_name
    • response_time
  2. バッククォートで囲む:特殊文字やキーワードを含むフィールド名に適用

    • message
    • limit
    • host-name
    • column with spaces
  3. 避けるべき記述:シングルクォートやダブルクォートで囲まれたものは文字列であり、フィールド名ではありません。

    • 'message'
    • "message"

JSON フィールド抽出

データフィールドに JSON 形式のコンテンツが含まれている場合、JSON Path 構文のサブセットを使用して内部フィールドのデータを抽出できます。

基本構文

field-name@json-path
@json-path                    // デフォルトで message フィールドを使用

JSON Path 構文

  • ドットでオブジェクトプロパティにアクセス:.field_name
  • 角括弧でオブジェクトプロパティにアクセス:["key"](スペースや特殊文字を含むキーに適用)
  • 配列インデックスで値にアクセス:[index]

適用例

以下の JSON ログデータがあると仮定します。

{
  "message": "User login attempt",
  "request": {
    "method": "POST",
    "path": "/api/login",
    "headers": {
      "user-agent": "Mozilla/5.0",
      "content-type": "application/json"
    },
    "body": {
      "username": "john.doe",
      "password": "***",
      "permissions": ["read", "write", "admin"]
    }
  },
  "response": {
    "status": 200,
    "time": 156,
    "data": [
      {"id": 1, "name": "user1"},
      {"id": 2, "name": "user2"}
    ]
  }
}
// リクエストメソッドを抽出
L::auth_logs:(message@request.method)

// リクエストパスを抽出
L::auth_logs:(message@request.path)

// ユーザー名を抽出
L::auth_logs:(message@request.body.username)

// レスポンスステータスを抽出
L::auth_logs:(message@response.status)

// User-Agent を抽出(ハイフンを含むため、角括弧が必要)
L::auth_logs:(message@request.headers["user-agent"])

// 権限配列の最初の要素を抽出
L::auth_logs:(message@request.body.permissions[0])

// レスポンスデータの最初のオブジェクトの name を抽出
L::auth_logs:(message@response.data[0].name)

// 異なるリクエストメソッドの数をカウント
L::auth_logs:(count(*)) [1h] BY message@request.method

// レスポンス時間の分布を分析
L::auth_logs:(avg(message@response.time), max(message@response.time)) [1h] BY message@request.method

// 複数フィールドを抽出
L::auth_logs:(
    message,
    message@request.method as method,
    message@request.path as path,
    message@response.status as status,
    message@response.time as response_time
) {message@response.status >= 400} [1h]

計算フィールド

式の計算

基本的な算術演算をサポートします。

// 単位変換(ミリ秒から秒へ)
L::nginx:(response_time / 1000) as response_time_seconds

// パーセンテージ計算
M::memory:(used / total * 100) as usage_percentage

// 複合計算
M::network:((bytes_in + bytes_out) / 1024 / 1024) as total_traffic_mb

関数による計算

さまざまな集計関数や変換関数をサポートします。

// 集計関数
M::cpu:(max(usage), min(usage), avg(usage)) [1h] BY host

// 変換関数
L::logs:(int(response_time) as response_time_seconds)
L::logs:(floor(response_time) as response_time_seconds)

条件式

CASE WHEN は、クエリ内で条件に応じて異なる値を選択するために使用されます。通常、集計関数と組み合わせて、条件付きカウント、条件付き合計、または条件に応じたフィールドの正規化に使用されます。

基本構文

CASE
  WHEN condition THEN value
  [WHEN condition THEN value ...]
  [ELSE default_value]
END

単純な CASE 構文を使用して、同じフィールドを複数の値とマッチングすることもできます。

CASE field
  WHEN value1 THEN result1
  WHEN value2 THEN result2
  ELSE default_value
END

WHEN は記述順にマッチングされ、最初に条件にヒットした THEN の値を返します。いずれにもヒットしない場合は、ELSE の値を返します。ELSE が明示的に記述されていない場合、デフォルトで nil が返されます。

適用例

条件付き合計:5xx リクエストのトラフィックのみをカウント

L::nginx_access:(
    sum(CASE WHEN status >= 500 THEN bytes ELSE 0 END) as error_bytes
) [1h] BY host

条件付きカウント:エラーリクエスト数をカウント

L::nginx_access:(
    count(CASE WHEN status >= 500 THEN 1 ELSE nil END) as error_count
) [1h] BY service

注意:count(expr)nil 以外の値をカウントします。0nil ではないため、count(CASE WHEN condition THEN 1 ELSE 0 END) は条件にヒットした行のみではなく、すべての行をカウントします。条件付きカウントを行う場合は、ELSE nil を使用するか、sum(CASE WHEN condition THEN 1 ELSE 0 END) を使用することを推奨します。

マルチブランチ分類:ステータスコードに基づくレベル分け

L::nginx_access:(
    max(CASE
        WHEN status >= 500 THEN 3
        WHEN status >= 400 THEN 2
        WHEN status >= 300 THEN 1
        ELSE 0
    END) as status_level
) [1h] BY service

フィールドクリーニング後の判定:大文字小文字を区別せずにエラーログをカウント

L::app_logs:(
    sum(CASE WHEN lower(level) = "error" THEN 1 ELSE 0 END) as error_count
) [1h] BY service

型変換後の集計:フィールドが文字列の場合、数値に変換して合計に参加

L::nginx_access:(
    sum(CASE WHEN status >= 500 THEN int(bytes) ELSE 0 END) as error_bytes
) [1h] BY host

サポート範囲

CASE WHEN は現在、制限付きの行レベル条件式であり、ハイパフォーマンスなプッシュダウン実行をサポートするように設計されています。集計関数の内部(例:sum(CASE ...)count(CASE ...)max(CASE ...))で使用できます。

CASE 内部でサポートされるもの:

  • フィールド
  • リテラルと nil
  • ブール条件
  • 以下のスカラー関数:intfloatstringmd5loweruppertrimltrimrtrimlengthregexp_replace

CASE 内での集計関数の使用はサポートされていません。集計関数は CASE の外側で使用する必要があります。

// 推奨:各行で CASE を計算してから集計
L::nginx_access:(
    sum(CASE WHEN status >= 500 THEN bytes ELSE 0 END) as error_bytes
) [1h] BY host

// 非サポート:CASE 内での集計関数の使用
L::nginx_access:(
    CASE WHEN sum(bytes) > 0 THEN "has_bytes" ELSE "empty" END
) [1h] BY host

また、サポート範囲に含まれていない複雑なスカラー関数(例:regexp_extract)の CASE 内での使用もサポートされていません。その他の複雑な処理が必要な場合は、クエリ条件、フィールドクリーニング、またはサブクエリを使用してロジックを分割し、CASE 内での広範囲な詳細スキャンを避けることを推奨します。

エイリアス

フィールドや式にエイリアスを指定することで、結果を読みやすくし、後続の参照を容易にします。

基本構文

expression as alias_name

適用例

// 単純なエイリアス
M::cpu:(avg(usage) as avg_usage, max(usage) as max_usage) [1h] BY host

// 式のエイリアス
M::memory:((used / total) * 100 as usage_percent) [1h] BY host

// 関数のエイリアス
L::logs:(count(*) as error_count) {level = 'error'} [1h] BY service

// JSON 抽出のエイリアス
L::api_logs:(
    message@request.method as http_method,
    message@response.status as http_status,
    message@response.time as response_time_ms
) [1h]

使用のヒント

DQL では、集計関数の結果を元のフィールド名で直接参照できます。これにより、エイリアスの使用が減ります。

M::cpu:(max(usage)) [1h] BY host

// 結果には max(usage) 列が含まれ、後続で直接 usage 列名を使用してサブクエリ結果の `max(usage)` 列を取得できます。
M::(M::cpu:(max(usage)) [1h] BY host):(max(usage)) { usage > 80 }

ただし、同じフィールドに対して複数の集計関数が使用されている場合は、後続で正しく区別するためにエイリアスを使用する必要があります。

// エイリアスが必要な場合
M::cpu:(max(usage) as max_usage, min(usage) as min_usage) [1h] BY host

時間句(time-clause)

時間句は DQL の中核的な機能の1つであり、クエリの時間範囲、集計時間ウィンドウ、および Rollup 集計関数を指定するために使用されます。

基本構文

[start_time:end_time:interval:rollup]

時間範囲

絶対タイムスタンプ

[1672502400000:1672588800000]     // ミリ秒単位のタイムスタンプ
[1672502400:1672588800]           // 秒単位のタイムスタンプ

相対時間

複数の時間単位をサポートしており、混合して使用できます。

[1h]                             // 過去1時間から現在まで
[1h:5m]                          // 過去1時間から過去5分まで
[1h30m]                          // 過去1時間30分
[2h15m30s]                       // 過去2時間15分30秒

時間式の説明

単位 説明
s 30s
m 5m
h 時間 2h
d 7d
w 4w
y 1y

時間式は時間句で使用される場合、現在時刻からのオフセットを示します。Select 句や Where 句などで使用される場合、ミリ秒の整数として扱われ計算に参加します。

集計クエリで使用される場合、さらに2つの時間単位がサポートされます。

単位 説明
i、is 集計時間ウィンドウの倍数、戻り値は浮動小数点秒 1i、1is
ims 集計時間ウィンドウの倍数、戻り値は整数ミリ秒 1ims
O::HOST:(count(*)){ `last_update_time` > (now()-10m) } // 10m は 600,000 の整数として計算に参加
L::*:( count(*) / 1i ) [::1m] // 時間ウィンドウサイズ (1m) の秒数で割り、ログ書き込みの QPS を計算

プリセット時間範囲

よく使用される時間範囲のキーワードを提供します。

キーワード 説明 時間範囲
TODAY 今日 本日0時から現在まで
YESTERDAY 昨日 昨日0時から本日0時まで
THIS WEEK 今週 今週月曜0時から現在まで
LAST WEEK 先週 先週月曜0時から今週月曜0時まで
THIS MONTH 今月 今月1日0時から現在まで
LAST MONTH 先月 先月1日0時から今月1日0時まで
[TODAY]                          // 今日のデータ
[YESTERDAY]                      // 昨日のデータ
[THIS WEEK]                      // 今週のデータ
[LAST WEEK]                      // 先週のデータ
[THIS MONTH]                     // 今月のデータ
[LAST MONTH]                     // 先月のデータ

時間範囲キーワードを使用する場合は、ワークスペースのタイムゾーン設定が正しいことを確認し、ユーザーのリクエストタイムゾーンに厳密に従って換算する必要があります。

時間ウィンドウ集計

時間ウィンドウは、指定された時間間隔でデータをグループ化して集計し、返される結果の time 列は各時間ウィンドウの開始時刻を示します。

単一時間ウィンドウ

時間範囲全体が1つの値に集計されます。

M::cpu:(max(usage_total)) [1h]

クエリ結果:

{
  "columns": ["time", "max(usage_total)"],
  "values": [
    [1721059200000, 37.46]
  ]
}

時間ウィンドウ集計

時間間隔でグループ化して集計します。

M::cpu:(max(usage_total)) [1h::10m]

クエリ結果:

{
  "columns": ["time", "max(usage_total)"],
  "values": [
    [1721059200000, 37.46],
    [1721058600000, 34.12],
    [1721058000000, 33.81],
    [1721057400000, 30.92],
    [1721058000000, 34.53],
    [1721057400000, 36.11]
  ]
}

Rollup 関数

Rollup 関数は DQL における重要な前処理ステップであり、グループ集計よりも先に実行され、生の時系列データを前処理します。

実行タイミング

クエリ実行フローにおける Rollup の位置:

生データ → WHERE フィルタ → **Rollup 前処理** → グループ集計 → HAVING フィルタ → 最終結果

実行メカニズム

Rollup の実行プロセスは2つのフェーズに分かれます。

  1. 時系列ごとの処理:各独立した時系列に対して個別に Rollup 関数を適用
  2. 集計計算:Rollup 処理後の結果に対してグループ集計を実行

Rollup 短縮形、Rollup 関数呼び出し、明示的な集計呼び出し

DQL には、混同しやすい3つの時系列関数の記述方法があります。

  1. Rollup 短縮形:時間句に関数名のみを記述します(例:[rate][1h::5m:slope])。
  2. Rollup 関数呼び出し:時間句で Rollup 関数にアルゴリズムパラメータを渡します(例:[1h::1m:ewma(0.3)][1h::1m:moving_average(5)][1h::1m:percentile(95)])。
  3. 明示的な集計呼び出し:Select 句で完全な関数呼び出しを記述します(例:rate(request_count)ewma(usage, 0.3)corr(cpu_usage, request_count))。

3つの記述方法の主な違いは、実行フェーズとパラメータ機能です。

記述方法 実行フェーズ 適用シナリオ
Rollup 短縮形 [1h::5m:rate] グループ集計の前、元の時系列ごとに実行 各時系列を前処理してからグループ集計
Rollup 関数呼び出し [1h::1m:ewma(0.3)] グループ集計の前、元の時系列ごとに実行 Rollup 関数に追加のアルゴリズムパラメータが必要
明示的な集計呼び出し ewma(usage, 0.3) Select 句の集計フェーズ フィールド、追加パラメータ、または複数の入力フィールドを指定する必要がある場合

時間句の Rollup 入力フィールドは、Select フィールド、時間ウィンドウ、および元の時系列によって決定されるため、Rollup 関数呼び出しでは追加のアルゴリズムパラメータのみを渡し、時間句ではフィールド名を渡しません。単一入力で追加パラメータがない時系列関数は、通常、Rollup 短縮形をサポートできます。追加のアルゴリズムパラメータが必要な単一入力関数は、Rollup 関数呼び出しをサポートできます。複数の入力フィールドが必要な関数は、明示的な集計呼び出しを使用する必要があります。

例:Counter メトリクスを Rollup してから集計

Counter メトリクスは、最初に各元の時系列で増加率を計算し、次にビジネスディメンションで合計する必要があります。

// 推奨:各時系列で rate を計算し、次に service で合計
M::http_requests:(sum(request_count)) [1h::5m:rate] BY service

// 非推奨:Counter の生の累積値を直接集計すると、QPS にならない
M::http_requests:(sum(request_count)) [1h::5m] BY service

例:追加のアルゴリズムパラメータが必要な場合の Rollup 関数呼び出し

ewma は平滑化係数 alpha を明示的に渡す必要があります。各元の時系列で EWMA を計算してからグループ集計を行いたい場合は、時間句に記述します。

// Rollup 関数呼び出し:各時系列で EWMA を計算し、次に host で平均を計算
M::cpu:(avg(usage)) [1h::1m:ewma(0.3)] BY host

// エラー:ewma には alpha が必要なため、関数名のみを記述できない
M::cpu:(avg(usage)) [1h::1m:ewma] BY host

// エラー:時間句の Rollup パラメータはアルゴリズムパラメータのみを記述し、フィールド名は記述しない
M::cpu:(avg(usage)) [1h::1m:ewma(usage, 0.3)] BY host

Select 集計フェーズで EWMA を計算したい場合は、明示的な集計呼び出しも使用できます。

// 明示的な集計呼び出し:Select 集計フェーズで usage に対して EWMA を計算
M::cpu:(ewma(usage, 0.3)) [1h::1m] BY host

同様に、時間句の Rollup に適した単一数値パラメータ化関数には以下も含まれます。

// 各時系列で5点移動平均を計算し、次に host で平均を計算
M::cpu:(avg(usage)) [1h::1m:moving_average(5)] BY host

// 各時系列で P95 を取得し、次に service で平均を計算
M::response_time:(avg(duration)) [1h::5m:percentile(95)] BY service

例:複数の入力フィールドがある場合の明示的な集計呼び出し

corr は2つの入力フィールドを必要とするため、時間句の Rollup に記述できません。

// 正しい:Select 句で2つのフィールドを明示的に指定
M::service_metric:(corr(cpu_usage, request_count)) [1h::5m] BY service

// エラー:時間句の Rollup では2つの入力フィールドを表現できない
M::service_metric:(avg(cpu_usage)) [1h::5m:corr] BY service

例:同名関数の実行フェーズの違い

関数が Rollup 短縮形と明示的な集計呼び出しの両方をサポートしている場合、2つの記述方法は異なる実行フェーズを示すため、完全に同等であるとデフォルトで解釈すべきではありません。

// Rollup 短縮形:各元の時系列で zscore を計算してからグループ集計
M::cpu:(max(usage)) [1h::5m:zscore] BY host

// 明示的な集計呼び出し:Select 集計フェーズで usage に対して zscore を計算
M::cpu:(zscore(usage)) [1h::5m] BY host

適用シナリオ

Rollup 関数の典型的な適用シナリオは、Counter メトリクスの処理です。

Prometheus の Counter タイプのメトリクスでは、生の値を直接集計しても意味がありません。Counter は単調増加するためです。まず、時系列ごとに増加率を計算し、その後で集計する必要があります。

*問題例:2台のサーバーのリクエストカウンターがあると仮定します。

{
  "host": "web-server-01",
  "data": [
    {"time": "2024-07-15 08:25:00", "request_count": 150},
    {"time": "2024-07-15 08:20:00", "request_count": 140},
    {"time": "2024-07-15 08:15:00", "request_count": 130},
    {"time": "2024-07-15 08:10:00", "request_count": 120},
    {"time": "2024-07-15 08:05:00", "request_count": 110},
    {"time": "2024-07-15 08:00:00", "request_count": 100}
  ]
}
{
  "host": "web-server-02",
  "data": [
    {"time": "2024-07-15 08:25:00", "request_count": 250},
    {"time": "2024-07-15 08:20:00", "request_count": 240},
    {"time": "2024-07-15 08:15:00", "request_count": 230},
    {"time": "2024-07-15 08:10:00", "request_count": 220},
    {"time": "2024-07-15 08:05:00", "request_count": 210},
    {"time": "2024-07-15 08:00:00", "request_count": 200}
  ]
}

直接集計の問題点:

  • web-server-01 の request_count の開始値は 100
  • web-server-02 の request_count の開始値は 200
  • 両方のサーバーのリクエストレートは同じ(5分ごとに10リクエスト)ですが、絶対値が異なります。

Rollup を使用した解決策:

M::http:(sum(request_count)) [rate]

実行プロセス:

  1. Rollup フェーズ(各時系列で個別に実行):

    • web-server-01: rate([100, 110, 120, 130, 140, 150]) = 2 リクエスト/分
    • web-server-02: rate([200, 210, 220, 230, 240, 250]) = 2 リクエスト/分
  2. 集計フェーズ

    • sum([2, 2]) = 4 リクエスト/分

関数タイプ

一般的な Rollup 関数は次のとおりです。

関数タイプ 説明 適用シナリオ
rate() 増加率を計算 Counter タイプ指標
increase() 増加量を計算 Counter タイプ指標
last() 最後の値を取得 Gauge タイプ指標
avg() 平均値を計算 データ平滑化
max() 最大値を取得 ピーク分析
min() 最小値を取得 ボトム分析

ただし、単一の値を返すほとんどすべての集計関数が使用可能なため、ここではすべての関数リストを記載しません。

デフォルト Rollup

Rollup 関数が明示的に指定されていない場合、DQL はデフォルトでRollup 計算を行いません。PromQL のデフォルト Rollup は last であるため、Prometheus のメトリクスを計算している場合は、この違いを理解し、手動で Rollup 関数を指定する必要があります。

適用例

// すべてのサーバーの合計リクエストレートを計算
M::http_requests:(sum(request_count)) [rate]

// エラー率を計算
M::http_requests:(
    sum(error_count) as errors,
    sum(request_count) as requests
) [rate] BY service

時間ウィンドウの柔軟な構文

DQL は、複数の時間ウィンドウの短縮形をサポートしており、クエリの記述をより便利にします。

短縮形

[1h]                             // 時間範囲のみ指定
[1h::5m]                         // 時間範囲 + 集計ステップ
[1h:5m]                          // 開始時間 + 終了時間
[1h:5m:1m]                       // 開始 + 終了 + ステップ
[1h:5m:1m:avg]                   // 完全な形式
[::5m]                           // 集計ステップのみ指定
[:::sum]                         // rollup 関数のみ指定
[sum]                            // rollup 関数のみ指定(最も簡潔な形式)

時間シフト(SHIFT){#shift}

SHIFT は、クエリ全体の時間ウィンドウを前方にシフトし、指定された期間前のデータを読み取ります。結果の時間列は現在のウィンドウを表示します。

<select-query> SHIFT <duration>

SHIFT は時間ウィンドウの直後に記述し、BYHAVINGORDER BY などの句よりも前に配置します。

L::logs:(service, count(*) AS requests)[1d:1h] SHIFT 7d BY service

フィルタリング、グループ化、集計、ソート、ページングは、シフト前のデータに基づいて実行されます。結果の構造は SHIFT がないクエリと同じですが、時間列のみが現在のウィンドウに残ります。duration は正の固定長(例:1h7d)である必要があります。

ネストされた SELECT は、それぞれ独自のクエリレベル SHIFT を宣言でき、オフセットは階層的に積み重ねられます。クエリレベル SHIFT と式レベル SHIFTDQL 関数リファレンスの SHIFT を参照)は組み合わせて使用できます。クエリレベルのオフセットがクエリ全体の基準ウィンドウを決定し、式レベルのオフセットはその基準ウィンドウを基準にして、より古い集計値を読み取ります。

フィルタ条件(where-clause)

フィルタ条件は、データ行をフィルタリングし、条件を満たすデータのみを後続処理用に保持するために使用されます。

基本構文

{condition1, condition2, condition3}

複数の条件は、カンマ、AND、OR、&&、|| で接続できます。

比較演算子

演算子 説明
= 等しい host = 'web-01'
!= 等しくない status != 200
> より大きい cpu_usage > 80
>= 以上 memory_usage >= 90
< より小さい response_time < 1000
<= 以下 disk_usage <= 80

パターンマッチング演算子

演算子 説明
=~ 正規表現マッチ message =~ 'error.*\\d+'
!~ 正規表現非マッチ message !~ 'debug.*'

集合演算子

演算子 説明
IN 集合に含まれる status IN [200, 201, 202]
NOT IN 集合に含まれない level NOT IN ['debug', 'info']

論理演算子

演算子 説明
AND または && 論理積 cpu > 80 AND memory > 90
OR または || 論理和 status = 500 OR status = 502
NOT 論理否定 NOT status = 200

ヒントOR 演算子は、論理和の実行に加えて、左辺の式が NULL を返す場合に右辺の式の結果を直接返す NULL 値フォールバックセマンティクスを提供します。これにより、優先順位のフォールバックロジック(例:status OR backup_status)を実装できます。

適用例

基本フィルタリング

// 単一条件
M::cpu:(usage) {host = 'web-01'} [1h]

// 複数の AND 条件
M::cpu:(usage) {host = 'web-01', usage > 80} [1h]

// AND キーワードを使用
M::cpu:(usage) {host = 'web-01' AND usage > 80} [1h]

// 論理演算子の混合使用
M::cpu:(usage) {(host = 'web-01' OR host = 'web-02') AND usage > 80} [1h]

正規表現マッチング

// エラーログのマッチング
L::logs:(message) {message =~ 'ERROR.*\\d{4}'} [1h]

// 特定の形式のログをマッチング
L::logs:(message) {message =~ '\\[(ERROR|WARN)\\].*'} [1h]

// デバッグ情報を除外
L::logs:(message) {message !~ 'DEBUG.*'} [1h]

// ホスト名のパターンマッチング
M::cpu:(usage) {host =~ 'web-.*\\.prod\\.com'} [1h]

集合操作

// ステータスコードフィルタリング
L::nginx:(count(*)) {status IN [200, 201, 202, 204]} [1h]

// 特定のステータスコードを除外
L::nginx:(count(*)) {status NOT IN [404, 500, 502]} [1h]

// ログレベルフィルタリング
L::app_logs:(count(*)) {level IN ['ERROR', 'WARN', 'CRITICAL']} [1h]

配列フィールドの処理

フィールドタイプが配列の場合、DQL は複数の配列マッチング操作をサポートします。

フィールド tags = ['web', 'prod', 'api'] があると仮定します。

推奨構文:IN と NOT IN の使用

// 配列が特定の値を含むかどうかをチェック
{tags IN ['web']}           // true、tags が 'web' を含むため
{tags IN ['mobile']}        // false、tags が 'mobile' を含まないため

// 配列が特定の値を含まないかどうかをチェック
{tags NOT IN ['mobile']}    // true、tags が 'mobile' を含まないため
{tags NOT IN ['web']}       // false、tags が 'web' を含むため

// 配列が指定されたすべての値を含むかどうかをチェック
{tags IN ['web', 'api']}           // true、'web' と 'api' を含むため
{tags IN ['web', 'api', 'mobile']} // false、'mobile' を含まないため

互換性構文(非推奨)

以下の構文は歴史的な互換性のために保持されており、新しいクエリでの使用は推奨されません。これらの演算子の配列フィールドでのセマンティクスは通常のフィールドとは異なり、混乱を引き起こす可能性があります。

// 歴史的構文:単一値の包含チェック(等価演算子のオーバーロードセマンティクス)
{tags = 'web'}           // true、tags が 'web' を含むため
{tags = 'mobile'}        // false、tags が 'mobile' を含まないため

// 歴史的構文:単一値の非包含チェック(不等価演算子のオーバーロードセマンティクス)
{tags != 'mobile'}       // true、tags が 'mobile' を含まないため
{tags != 'web'}          // false、tags が 'web' を含むため

関数によるフィルタリング

ブール値を返す関数は、フィルタ条件として使用できます。

// 文字列マッチング
L::logs:(message) { match(message, 'error') }
L::logs:(message) { wildcard(message, 'error*') }

// レスポンス時間が異常なリクエストをクエリ
L::access_logs:(*) {
    response_time > 1000 AND
    match(message, 'timeout')
}

// メモリ使用率が異常なホストをクエリ
M::memory:(usage) {
    (usage > 90 OR usage < 10) AND
    host =~ 'prod-.*' AND
    tags IN ['critical', 'important']
}

WHERE サブクエリ

WHERE サブクエリは、DQL で動的フィルタリングを実現するための強力な機能であり、あるクエリの結果を別のクエリのフィルタ条件として使用することを可能にします。このメカニズムは、データ分析結果に基づく動的なフィルタリングをサポートします。

クエリの特徴

  • 動的フィルタリング:フィルタ条件は固定値ではなく、クエリによって動的に計算されます。
  • 名前空間の混合:名前空間をまたがるクエリをサポートし、異なるデータタイプの関連分析を実現します。
  • 逐次実行:サブクエリが先に実行され、その結果がメインクエリのフィルタリングに使用されます。
  • 配列結果:サブクエリの結果は配列としてカプセル化されるため、IN および NOT IN 演算子のみがサポートされます。

実行フロー

典型的な WHERE サブクエリの例:

M::cpu:(avg(usage)) { host IN (O::HOST:(hostname) {provider = 'cloud-a'}) } [1h] BY host

実行プロセス:

  1. サブクエリの実行O::HOST:(hostname) {provider = 'cloud-a'}

    • すべてのインフラストラクチャオブジェクトをクエリ
    • provider が 'cloud-a' のホストをフィルタリング
    • ホスト名のリストを返す:['host-01', 'host-02', 'host-03']
  2. メインクエリの実行M::cpu:(avg(usage)) [1h] BY host {host IN [...]}

    • CPU 使用率データをクエリ
    • サブクエリが返したホストのみを対象
    • ホストごとにグループ化して平均使用率を計算

適用例

// 特定のクラウドプロバイダーのサーバーを監視
M::cpu:(avg(usage)) { host IN (O::HOST:(hostname) {provider = 'cloud-a'}) } [1h] BY host


// 異なるクラウドプロバイダーのパフォーマンスを比較
M::memory:(avg(used / total * 100)) { host IN (O::HOST:(hostname) {provider IN ['cloud-a', 'cloud-b']}) } [1h] BY host


// 特定のビジネスのログを分析
L::app_logs:(count(*)) { service IN (T::services:(service_name) {business_unit = 'ecommerce'}) } [1h] BY level


// 重要なビジネスのアプリケーションパフォーマンスを監視
M::response_time:(avg(response_time)) { service IN (T::services:(service_name) {criticality = 'high'}) } [1h] BY service

グループ化(group-by-clause)

グループ化はデータ分析の中核機能であり、指定されたディメンションに従ってデータをグループ化し集計するために使用されます。

基本構文

BY expression1, expression2, ...

グループ化のタイプ

フィールドによるグループ化

// 単一フィールドによるグループ化
M::cpu:(avg(usage)) [1h] BY host

// 複数フィールドによるグループ化
M::cpu:(avg(usage)) [1h] BY host, env

// ネストされたグループ化
M::cpu:(avg(usage)) [1h] BY datacenter, rack, host

式によるグループ化

// 複合数式
M::memory:(avg(used)) [1h] BY ((used / total) * 100) as
usage_percent

// 複数フィールドの算術演算
M::performance:(avg(response_time)) [1h] BY
(response_time / 1000) as response_seconds

関数によるグループ化

// Drain クラスタリングアルゴリズム
L::logs:(count(*)) BY drain(message, 0.7) as sample

// 正規表現抽出によるグループ化
L::logs:(count(*)) [1h] BY regexp_extract(message, 'error_code: (\\d+)', 1)

グループ結果の処理

クエリにグループ化と時間ウィンドウの両方が含まれている場合、2次元データ構造が生成されます。Group By は時間ウィンドウと組み合わせて使用できます。このようなクエリ結果は2次元配列になります。この2次元配列の最初の次元はグループ化キーによって区別される複数のグループ自体であり、2番目の次元は単一グループ内の複数の時間範囲データです。

2次元データ構造の例:

クエリ:M::cpu:(max(usage_total)) [1h::10m] by host

クエリ結果の構造:

{
  "series": [
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-01"},
      "values": [
        [1721059200000, 78.5],
        [1721058600000, 82.3],
        [1721058000000, 75.8],
        [1721057400000, 88.2]
      ]
    },
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-02"},
      "values": [
        [1721059200000, 45.2],
        [1721058600000, 52.8],
        [1721058000000, 48.5],
        [1721057400000, 61.3]
      ]
    },
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-03"},
      "values": [
        [1721059200000, 92.1],
        [1721058600000, 95.7],
        [1721058000000, 89.4],
        [1721057400000, 97.6]
      ]
    }
  ]
}

この2次元配列をさらに加工する場合:

  1. この2次元配列の結果をフィルタリングするには、Having 句を使用します。
  2. 2次元配列内の単一グループ内のデータをソートまたはページングするには、order by、limit、offset 系のステートメントを使用します。
  3. 2次元配列のグループ自体をソートまたはページングするには、sorder by、slimit、soffset 系のステートメントを使用します。

2次元データ構造の詳細な説明とソート・ページング機能については、ソートとページングを参照してください。

Having 句(having-clause){#having}

Having 句は、グループ集計後の結果をフィルタリングするために使用され、WHERE 句と似ていますが、集計後のデータに対して作用します。

基本構文

HAVING condition

WHERE との違い

WHERE と HAVING はどちらもデータをフィルタリングするための句ですが、クエリ実行の異なるフェーズで作用し、処理するフィルタ条件も異なります。

実行タイミングの違い

生データ → WHERE フィルタ → グループ集計 → HAVING フィルタ → 最終結果
  1. WHERE 句

    • グループ集計の前に実行
    • 生のデータ行に対して作用
    • 条件を満たさないデータ行をフィルタリングし、後続処理のデータ量を削減
  2. HAVING 句

    • グループ集計の後に実行
    • 集計後の結果に対して作用
    • 集計関数の結果に基づいてフィルタリング

適用シナリオ

HAVING 句は、集計結果のフィルタリングに適しています。

// 集計関数の値に基づくフィルタリング
M::cpu:(avg(usage) as avg_usage) [1h] BY host HAVING avg_usage > 80

// 複数の集計条件に基づくフィルタリング
M::http_requests:(
    sum(request_count) as total,
    sum(error_count) as errors
) [1h] BY service, endpoint
HAVING errors / total > 0.01 AND total > 1000

// グループ統計に基づくフィルタリング
L::logs:(count(*) as count) [1h] BY service HAVING count > 100

// 複合集計条件に基づくフィルタリング
M::response_time:(
    avg(response_time) as avg_time,
    max(response_time) as max_time,
    min(response_time) as min_time
) [1h] BY endpoint
HAVING avg_time > 1000 AND max_time > 5000 AND (max_time - min_time) > 2000

ソートとページング

DQL におけるソートとページングは非常に重要かつユニークな機能であり、時系列データの特性に合わせて二重のソートメカニズム(グループ内ソートとグループ間ソート)を設計しています。この設計により、DQL は複雑な多次元時系列データ分析の要件を効率的に処理できます。

DQL のデータ構造を理解する

ソートとページングの詳細に入る前に、DQL クエリ結果の2次元データ構造を理解しておきましょう。クエリにグループ化(BY)と時間ウィンドウの両方が含まれている場合、2次元配列が生成されます。

  • 第1次元(グループ化次元):グループ化キーによって区別される複数のグループ
  • 第2次元(時間次元):各グループ内で時間ウィンドウごとに集計されたデータ

2次元データ構造の詳細な説明と JSON 形式の例については、グループ結果の処理を参照してください。この2次元構造は DQL のソートとページング機能の基礎であり、この構造を理解することは DQL のソートメカニズムを習得するために不可欠です。

グループ内ソートとページング(ORDER BY、LIMIT、OFFSET)

グループ内ソートとページングは、2次元構造の第2次元、つまり各グループ内部のデータに対して作用します。このソートは各グループ内で独立して実行され、他のグループのデータには影響しません。

基本構文

ORDER BY expression [ASC|DESC]
LIMIT row_count
OFFSET row_offset

実行メカニズム

グループ内ソートの実行プロセス:

  1. グループ処理:各グループに対して独立してソート操作を実行
  2. ソート基準:時間フィールド、集計関数の結果、または計算式を使用可能
  3. ページング制限:LIMIT は各グループが返すデータ行数を制限
  4. オフセット処理:OFFSET は各グループの先頭 N 行のデータをスキップ

適用例

基本的な時間ソート
// 時間降順で並べ替え、各ホストの最新データを表示
M::cpu:(max(usage_total)) [1h::10m] BY host ORDER BY time DESC

// 時間昇順で並べ替え、履歴トレンドを表示
M::cpu:(max(usage_total)) [1h::10m] BY host ORDER BY time ASC

実行結果(ORDER BY time DESC):

{
  "series": [
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-01"},
      "values": [
        [1721059200000, 78.5],  // 12:00:00
        [1721058600000, 82.3],  // 11:50:00
        [1721058000000, 75.8],  // 11:40:00
        [1721057400000, 88.2],  // 11:30:00
        [1721056800000, 72.1],  // 11:20:00
        [1721056200000, 69.4]   // 11:10:00
      ]
    },
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-02"},
      "values": [
        [1721059200000, 45.2],  // 12:00:00
        [1721058600000, 52.8],  // 11:50:00
        [1721058000000, 48.5],  // 11:40:00
        [1721057400000, 61.3],  // 11:30:00
        [1721056800000, 55.7],  // 11:20:00
        [1721056200000, 58.9]   // 11:10:00
      ]
    }
  ]
}
数値に基づくソート
// CPU 使用率降順で並べ替え、各ホストのピーク時間帯を特定
M::cpu:(max(usage_total) as max_usage_total) [1h::10m] BY host ORDER BY max_usage_total DESC

// レスポンス時間昇順で並べ替え、パフォーマンスが最も良い時間帯を特定
M::response_time:(avg(response_time) as avg_response_time) [1h::5m] BY endpoint ORDER BY avg_response_time ASC
グループ内ページング
// 各ホストの最新3データポイントのみを表示
M::cpu:(max(usage_total)) [1h::10m] BY host ORDER BY time DESC LIMIT 3

// 最新の2データポイントをスキップし、次の3データポイントを表示
M::cpu:(max(usage_total)) [1h::10m] BY host ORDER BY time DESC LIMIT 3 OFFSET 2

実行結果(LIMIT 3):

{
  "series": [
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-01"},
      "values": [
        [1721059200000, 78.5],  // 12:00:00 - 最新
        [1721058600000, 82.3],  // 11:50:00
        [1721058000000, 75.8]   // 11:40:00
        // 最初の3件のみを返す
      ]
    },
    {
      "columns": ["time", "max(usage_total)"],
      "name": "cpu",
      "tags": {"host": "web-server-02"},
      "values": [
        [1721059200000, 45.2],  // 12:00:00 - 最新
        [1721058600000, 52.8],  // 11:50:00
        [1721058000000, 48.5]   // 11:40:00
        // 最初の3件のみを返す
      ]
    }
  ]
}

グループ間ソートとページング(SORDER BY、SLIMIT、SOFFSET)

グループ間ソートとページングは DQL の特徴的な機能であり、2次元構造の第1次元、つまりグループ自体に対してソートを行います。このソートでは、各グループのデータを1つの値に次元圧縮し、異なるグループ間でこの値を比較する必要があります。

クエリで BY が使用されていない場合、結果は通常1つのグループのみになります。この場合、SORDER BY はソートとして目に見える効果は通常ありませんが、SLIMIT / SOFFSET は「グループ数」のセマンティクスで有効です(つまり、その単一グループに対して作用します)。

基本構文

SORDER BY aggregate_function(expression) [ASC|DESC]
SLIMIT group_count
SOFFSET group_offset

実行メカニズム

グループ間ソートの実行プロセス:

  1. 次元圧縮計算:各グループに集計関数を適用し、代表的な数値を計算
  2. グループソート:次元圧縮された値に基づいてすべてのグループをソート
  3. グループページング:SLIMIT は返されるグループ数を制限し、SOFFSET は先頭 N 個のグループをスキップ

次元圧縮関数の選択

グループ間ソートでは、次元圧縮に集計関数を使用する必要があります。集計関数が指定されていない場合、デフォルトで使用される集計関数は last です。

一般的な次元圧縮関数は次のとおりです。

関数 説明 適用シナリオ
last() 最後の値を取得 時系列の現在の状態
max() 最大値を取得 ピーク分析
min() 最小値を取得 ボトム分析
avg() 平均値を計算 全体的なトレンド分析
sum() 合計を計算 総量統計
count() カウント 頻度分析

ただし、単一の値を返すほとんどすべての集計関数が使用可能なため、ここではすべての関数リストを記載しません。

適用例

平均値に基づくソート
// 平均 CPU 使用率降順で並べ替え、負荷が最も高いホストを特定
M::cpu:(avg(usage)) [1h::10m] BY host SORDER BY avg(usage) DESC SLIMIT 5

// 平均レスポンス時間昇順で並べ替え、パフォーマンスが最も良いサービスを特定
M::response_time:(avg(response_time)) [1h] BY service SORDER BY avg(response_time) ASC SLIMIT 10

実行プロセス分析:

  1. 次元圧縮計算

    • web-server-01: avg(usage) = 77.7
    • web-server-02: avg(usage) = 53.7
    • web-server-03: avg(usage) = 90.9
  2. グループソート(avg(usage) DESC):

    • web-server-03: 90.9
    • web-server-01: 77.7
    • web-server-02: 53.7
  3. 最終結果(SLIMIT 2):

{
  "series": [
    {
      "columns": ["time", "avg(usage)"],
      "name": "cpu",
      "tags": {"host": "web-server-03"},  // 平均使用率: 90.9 - 1位
      "values": [
        [1721059200000, 92.1],
        [1721058600000, 95.7],
        [1721058000000, 89.4]
      ]
    },
    {
      "columns": ["time", "avg(usage)"],
      "name": "cpu",
      "tags": {"host": "web-server-01"},  // 平均使用率: 77.7 - 2位
      "values": [
        [1721059200000, 78.5],
        [1721058600000, 82.3],
        [1721058000000, 75.8]
      ]
    }
    // web-server-02 (avg: 53.7) は SLIMIT 2 により除外
  ]
}
適用例
// 最大 CPU 使用率でソートし、異常なピークがあるホストを特定
M::cpu:(max(usage_total)) [1h::10m] BY host SORDER BY max(usage_total) DESC SLIMIT 10

// 最小メモリ使用率でソートし、リソース利用率が最も低いホストを特定
M::memory:(min(usage_percent)) [24h::1h] BY host SORDER BY min(usage_percent) ASC SLIMIT 5

// 最新の CPU 使用率でソートし、現在の負荷が最も高いホストを特定
M::cpu:(usage) [1h::10m] BY host SORDER BY last(usage) DESC SLIMIT 10

// 最新のエラー率でソートし、現在問題が最も多いサービスを特定
L::logs:(count(*) as error_count) {level = 'error'} [1h] BY service SORDER BY error_count DESC SLIMIT 5

二重ソートとページングの組み合わせ使用

実際のアプリケーションでは、グループ内ソートとグループ間ソートが組み合わせて使用され、複雑なデータ表示要件を実現することがよくあります。この組み合わせにより、グループの順序とグループ内のデータの順序を同時に制御できます。

実行順序

二重ソートの実行順序:

  1. グループ間ソート:最初にすべてのグループをソートおよびページング
  2. グループ内ソート:選択されたグループに対して内部ソートおよびページングを実行

適用例

モニタリングダッシュボードのシナリオ
// CPU 使用率が最も高い10台のサーバーを特定し、各サーバーの最新5データポイントを表示
M::cpu:(avg(usage)) [1h::10m] BY host
SORDER BY avg(usage) DESC SLIMIT 10    // グループ間ソート:使用率が最も高い10台を特定
ORDER BY time DESC LIMIT 5             // グループ内ソート:各サーバーの最新5ポイントを表示

実行結果:

{
  "series": [
    {
      "columns": ["time", "avg(usage)"],
      "name": "cpu",
      "tags": {"host": "web-server-03"},  // 平均使用率: 90.9 - 1位
      "values": [
        [1721059200000, 92.1],  // 12:00:00 - 最新
        [1721058600000, 95.7],  // 11:50:00
        [1721058000000, 89.4],  // 11:40:00
        [1721057400000, 97.6],  // 11:30:00
        [1721056800000, 87.3]   // 11:20:00
        // 最新5データポイントのみを返す(LIMIT 5)
      ]
    },
    {
      "columns": ["time", "avg(usage)"],
      "name": "cpu",
      "tags": {"host": "web-server-01"},  // 平均使用率: 77.7 - 2位
      "values": [
        [1721059200000, 78.5],  // 12:00:00 - 最新
        [1721058600000, 82.3],  // 11:50:00
        [1721058000000, 75.8],  // 11:40:00
        [1721057400000, 88.2],  // 11:30:00
        [1721056800000, 72.1]   // 11:20:00
        // 最新5データポイントのみを返す(LIMIT 5)
      ]
    }
    // 他のホストは SLIMIT 10 により除外され、使用率が最も高い10台のみが返される
  ]
}

DQL のソートとページング機能を習得することで、強力なモニタリングダッシュボード、パフォーマンス分析ツール、ビジネスインサイトシステムを構築できます。

注意

グループ内ソートとグループ間ソートの組み合わせを適切に使用することで、データ分析の効率と効果を大幅に向上できます。

フィードバック

このページは役に立ちましたか?