Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Сводка или преобразование данных ›Сводка исторических данных

Начало работы со сводками

Устарело в 8.11.0.

Сводки будут удалены в будущей версии. Пожалуйста, перейдите к уменьшению выборки вместо этого.

С версии 8.15.0 вызов API put job в кластере без использования сводок завершится с сообщением об устаревании и запланированном удалении сводок. Для выполнения API put job кластер должен содержать либо задание сводки, либо индекс сводки.

Для использования функции сводок необходимо создать одно или несколько «заданий сводки». Эти задания выполняются непрерывно в фоновом режиме и сводят указанный индекс или индексы, помещая свёрнутые документы во вторичный индекс (также по вашему выбору).

Представьте, что у вас есть серия ежедневных индексов, содержащих данные датчиков (sensor-2017-01-01, sensor-2017-01-02 и т.д.). Пример документа может выглядеть так:

{
  "timestamp": 1516729294000,
  "temperature": 200,
  "voltage": 5.2,
  "node": "a"
}

Создание задания сводки

Мы хотели бы свести эти документы в сводки по часам, что позволит нам генерировать отчеты и панели мониторинга с любым интервалом времени в час или более. Задание сводки может выглядеть так:

resp = client.rollup.put_job(
    id="sensor",
    index_pattern="sensor-*",
    rollup_index="sensor_rollup",
    cron="*/30 * * * * ?",
    page_size=1000,
    groups={
        "date_histogram": {
            "field": "timestamp",
            "fixed_interval": "60m"
        },
        "terms": {
            "fields": [
                "node"
            ]
        }
    },
    metrics=[
        {
            "field": "temperature",
            "metrics": [
                "min",
                "max",
                "sum"
            ]
        },
        {
            "field": "voltage",
            "metrics": [
                "avg"
            ]
        }
    ],
)
print(resp)
const response = await client.rollup.putJob({
  id: "sensor",
  index_pattern: "sensor-*",
  rollup_index: "sensor_rollup",
  cron: "*/30 * * * * ?",
  page_size: 1000,
  groups: {
    date_histogram: {
      field: "timestamp",
      fixed_interval: "60m",
    },
    terms: {
      fields: ["node"],
    },
  },
  metrics: [
    {
      field: "temperature",
      metrics: ["min", "max", "sum"],
    },
    {
      field: "voltage",
      metrics: ["avg"],
    },
  ],
});
console.log(response);
PUT _rollup/job/sensor
{
  "index_pattern": "sensor-*",
  "rollup_index": "sensor_rollup",
  "cron": "*/30 * * * * ?",
  "page_size": 1000,
  "groups": {
    "date_histogram": {
      "field": "timestamp",
      "fixed_interval": "60m"
    },
    "terms": {
      "fields": [ "node" ]
    }
  },
  "metrics": [
    {
      "field": "temperature",
      "metrics": [ "min", "max", "sum" ]
    },
    {
      "field": "voltage",
      "metrics": [ "avg" ]
    }
  ]
}

Мы даём заданию идентификатор «sensor» (в URL: PUT _rollup/job/sensor) и говорим ему свести шаблон индекса "sensor-*". Это задание найдёт и свведёт любой индекс, соответствующий этому шаблону. Сводки затем хранятся в индексе "sensor_rollup".

Параметр cron управляет временем и частотой активации задания. Когда планировщик задания сводки срабатывает, он начинает сводить данные с места, где он остановился после последней активации. Таким образом, если вы настроите планировщик на выполнение каждые 30 секунд, задание будет обрабатывать последние 30 секунд данных, которые были индексированы в индексы sensor-*.

Если планировщик был настроен на выполнение один раз в день в полночь, задание обработает последние 24 часа данных. Выбор в значительной степени зависит от того, насколько «реального времени» вы хотите сводки и хотите ли вы обрабатывать данные непрерывно или перенести это на непиковые часы.

Далее, мы определяем набор groups. По сути, мы определяем измерения, на которых мы хотим фокусироваться в будущем при запросе данных. Группировка в этом задании позволяет использовать агрегации date_histogram по полю timestamp, свёрнутые с часовыми интервалами. Это также позволяет выполнять агрегации `terms` по полю node.

Интервал гистограммы дат против планировщика

Обратите внимание, что планировщик задания настроен на выполнение каждые 30 секунд, а гистограмма дат — на сводку с интервалом 60 минут. Как они связаны?

Гистограмма дат управляет гранулярностью сохранённых данных. Данные будут сводиться в часовые интервалы, и вы не сможете запросить данные с более высокой гранулярностью. Планировщик просто управляет тем, когда процесс ищет новые данные для сводки. Каждые 30 секунд он проверяет, есть ли новые данные за час, и сводит их. Если нет, задание переходит в режим ожидания.

Часто не имеет смысла определять такой небольшой планировщик (30 сек.) для большого интервала (1 час), потому что большая часть активаций просто перейдёт в режим ожидания. Но это не плохо, задание сделает всё правильно.

После определения групп, которые должны быть сгенерированы для данных, вы настраиваете метрики, которые должны быть собраны. По умолчанию собираются только doc_counts для каждой группы. Чтобы сводка была полезной, вы часто добавляете метрики, такие как средние значения, минимальные и максимальные значения и т. д. В этом примере метрики довольно простые: мы хотим сохранить мин/макс/сумму поля temperature и среднее значение поля voltage.

Средние значения не компонуемые?!

Если вы раньше работали со сводками, вы можете быть осторожны со средними значениями. Если среднее значение сохраняется для интервала 10 минут, оно обычно не подходит для больших интервалов. Нельзя усреднить шесть средних значений 10-минутных интервалов, чтобы найти среднее значение за час; среднее значение средних значений не равно общему среднему значению.

По этой причине другие системы либо исключают возможность вычисления среднего значения, либо хранят среднее значение с несколькими интервалами, чтобы поддерживать более гибкие запросы.

Вместо этого функция сводки данных сохраняет count и sum для заданного интервала времени. Это позволяет нам восстановить среднее значение для любого интервала, большего или равного заданному интервалу. Это обеспечивает максимальную гибкость при минимальных затратах на хранение… и вам не нужно беспокоиться о точности средних значений (здесь нет среднего от среднего!)

Более подробную информацию о синтаксисе задания см. в Создание задач сводки.

После выполнения вышеприведённой команды и создания задания вы получите следующий ответ:

{
  "acknowledged": true
}

Запуск задания

После создания задание будет находиться в неактивном состоянии. Задания нужно запускать, прежде чем они начнут обрабатывать данные (это позволяет остановить их позже, как способ временной паузы без удаления конфигурации).

Для запуска задания выполните эту команду:

resp = client.rollup.start_job(
    id="sensor",
)
print(resp)
response = client.rollup.start_job(
  id: 'sensor'
)
puts response
const response = await client.rollup.startJob({
  id: "sensor",
});
console.log(response);
POST _rollup/job/sensor/_start

Поиск свёрнутых результатов

После того как задание выполнилось и обработало некоторые данные, мы можем использовать конечную точку поиска сводки для поиска. Функция сводки разработана таким образом, что вы можете использовать знакомый синтаксис Query DSL… он просто работает с свёрнутыми данными вместо исходных данных.

Например, рассмотрим этот запрос:

resp = client.rollup.rollup_search(
    index="sensor_rollup",
    size=0,
    aggregations={
        "max_temperature": {
            "max": {
                "field": "temperature"
            }
        }
    },
)
print(resp)
response = client.rollup.rollup_search(
  index: 'sensor_rollup',
  body: {
    size: 0,
    aggregations: {
      max_temperature: {
        max: {
          field: 'temperature'
        }
      }
    }
  }
)
puts response
const response = await client.rollup.rollupSearch({
  index: "sensor_rollup",
  size: 0,
  aggregations: {
    max_temperature: {
      max: {
        field: "temperature",
      },
    },
  },
});
console.log(response);
GET /sensor_rollup/_rollup_search
{
  "size": 0,
  "aggregations": {
    "max_temperature": {
      "max": {
        "field": "temperature"
      }
    }
  }
}

Это простая агрегация, которая вычисляет максимальное значение поля temperature. Но обратите внимание, что она отправляется в индекс sensor_rollup вместо исходных индексов sensor-*. И вы также заметите, что используется конечная точка _rollup_search. В остальном синтаксис точно такой же, как и ожидалось.

Если вы выполните этот запрос, вы получите результат, похожий на обычный ответ агрегации:

{
  "took" : 102,
  "timed_out" : false,
  "terminated_early" : false,
  "_shards" : ... ,
  "hits" : {
    "total" : {
        "value": 0,
        "relation": "eq"
    },
    "max_score" : 0.0,
    "hits" : [ ]
  },
  "aggregations" : {
    "max_temperature" : {
      "value" : 202.0
    }
  }
}

Единственное заметное отличие заключается в том, что результаты поиска сводки имеют нулевые hits, так как мы больше не ищем исходные, текущие данные. В остальном синтаксис идентичен.

Отсюда можно сделать несколько интересных выводов. Во-первых, даже несмотря на то, что данные были свёрнуты с часовыми интервалами и разбиты по имени узла, запрос, который мы выполнили, просто вычисляет максимальную температуру по всем документам. groups, которые были настроены в задании, не являются обязательными элементами запроса, а являются дополнительными измерениями, по которым вы можете разбивать данные. Во-вторых, синтаксис запроса и ответа почти идентичен обычному синтаксису DSL, что упрощает интеграцию в панели мониторинга и приложения.

Наконец, мы можем использовать эти поля группировки, которые мы определили, чтобы сконструировать более сложный запрос:

resp = client.rollup.rollup_search(
    index="sensor_rollup",
    size=0,
    aggregations={
        "timeline": {
            "date_histogram": {
                "field": "timestamp",
                "fixed_interval": "7d"
            },
            "aggs": {
                "nodes": {
                    "terms": {
                        "field": "node"
                    },
                    "aggs": {
                        "max_temperature": {
                            "max": {
                                "field": "temperature"
                            }
                        },
                        "avg_voltage": {
                            "avg": {
                                "field": "voltage"
                            }
                        }
                    }
                }
            }
        }
    },
)
print(resp)
response = client.rollup.rollup_search(
  index: 'sensor_rollup',
  body: {
    size: 0,
    aggregations: {
      timeline: {
        date_histogram: {
          field: 'timestamp',
          fixed_interval: '7d'
        },
        aggregations: {
          nodes: {
            terms: {
              field: 'node'
            },
            aggregations: {
              max_temperature: {
                max: {
                  field: 'temperature'
                }
              },
              avg_voltage: {
                avg: {
                  field: 'voltage'
                }
              }
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.rollup.rollupSearch({
  index: "sensor_rollup",
  size: 0,
  aggregations: {
    timeline: {
      date_histogram: {
        field: "timestamp",
        fixed_interval: "7d",
      },
      aggs: {
        nodes: {
          terms: {
            field: "node",
          },
          aggs: {
            max_temperature: {
              max: {
                field: "temperature",
              },
            },
            avg_voltage: {
              avg: {
                field: "voltage",
              },
            },
          },
        },
      },
    },
  },
});
console.log(response);
GET /sensor_rollup/_rollup_search
{
  "size": 0,
  "aggregations": {
    "timeline": {
      "date_histogram": {
        "field": "timestamp",
        "fixed_interval": "7d"
      },
      "aggs": {
        "nodes": {
          "terms": {
            "field": "node"
          },
          "aggs": {
            "max_temperature": {
              "max": {
                "field": "temperature"
              }
            },
            "avg_voltage": {
              "avg": {
                "field": "voltage"
              }
            }
          }
        }
      }
    }
  }
}

Что возвращает соответствующий ответ:

{
   "took" : 93,
   "timed_out" : false,
   "terminated_early" : false,
   "_shards" : ... ,
   "hits" : {
     "total" : {
        "value": 0,
        "relation": "eq"
     },
     "max_score" : 0.0,
     "hits" : [ ]
   },
   "aggregations" : {
     "timeline" : {
       "buckets" : [
         {
           "key_as_string" : "2018-01-18T00:00:00.000Z",
           "key" : 1516233600000,
           "doc_count" : 6,
           "nodes" : {
             "doc_count_error_upper_bound" : 0,
             "sum_other_doc_count" : 0,
             "buckets" : [
               {
                 "key" : "a",
                 "doc_count" : 2,
                 "max_temperature" : {
                   "value" : 202.0
                 },
                 "avg_voltage" : {
                   "value" : 5.1499998569488525
                 }
               },
               {
                 "key" : "b",
                 "doc_count" : 2,
                 "max_temperature" : {
                   "value" : 201.0
                 },
                 "avg_voltage" : {
                   "value" : 5.700000047683716
                 }
               },
               {
                 "key" : "c",
                 "doc_count" : 2,
                 "max_temperature" : {
                   "value" : 202.0
                 },
                 "avg_voltage" : {
                   "value" : 4.099999904632568
                 }
               }
             ]
           }
         }
       ]
     }
   }
}

Помимо того, что он более сложный (гистограмма дат и агрегация `terms` плюс дополнительная метрика среднего значения), вы заметите, что гистограмма дат использует интервал 7d вместо 60m.

Заключение

Этот быстрый старт должен был дать краткое описание основных функций, которые предоставляет Rollup. Больше советов и моментов, которые следует учитывать при настройке сводок, вы найдёте в остальной части этого раздела. Вы также можете изучить API REST для обзора доступных возможностей.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/rollup-getting-started.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API