Привет, Хабр!

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

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

В этой статье рассмотрим, как реализован HLL в PostgreSQL.

Установим

Установим HyperLogLog в БД PostgreSQL с помощью команды:

CREATE EXTENSION hll;

Команда добавит необходимые функции и типы данных в систему.

После установки расширения вы можно начать использовать функции HyperLogLog.

Основные функции HLL

hll_add() принимает элемент и HLL скетч, добавляет элемент в скетч и возвращает обновленный HLL скетч:

SELECT hll_add(hll_hash_integer(user_id)) FROM users;

hll_cardinality() возвращает аппроксимированное количество уникальных элементов в HLL скетче:

SELECT hll_cardinality(hll) FROM some_table;

hll_hash_text(), hll_hash_integer() генерируют хеш-значение из текста или целого числа соответственно для дальнейшего использования в HLL скетчах.

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

SELECT hll_union_agg(hll) FROM multiple_sketches;

hll_empty() создает пустой HLL скетч.

hll_size() возвращает размер в байтах HLL скетча.

hll_add_agg() агрегатная функция, аналогичная hll_union_agg, но она также добавляет новые элементы к HLL скетчу по мере их обработки.

hll_union() объединяет два или более HLL скетча в один.

hll_compact() оптимизирует хранение HLL скетча, минимизируя его размер без потери точности.

hll_decompress() преобразует сжатый HLL скетч обратно в его обычное состояние.

hyperloglog_init() инициализирует новый HLL скетч с определенной степенью ошибки и предполагаемым количеством уникальных элементов.

hyperloglog_reset() сбрасывает HLL скетч к исходному состоянию, удаляя все добавленные элементы.

hyperloglog_merge() объединяет два HLL скетча в один, сохраняя аппроксимированное количество уникальных элементов из обоих скетчей.

Примеры применений

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

SELECT
    created_at,
    event_type,
    hll_cardinality(distinct_user_id_count) AS distinct_count
FROM
    github_events_rollup_minute
WHERE
    created_at BETWEEN '2024-12-01 05:00:00' AND '2024-12-01 06:00:00';

Для оценки количества уникальных адресов электронной почты в БД можно использовать следующий запрос:

SELECT hll_cardinality(hll_sketch) AS estimated_distinct_count
FROM (
    SELECT hll_add(hll_hash_text(email)) AS hll_sketch
    FROM users
) AS subquery;

Создание таблицы для ежедневного подсчета уникальных посетителей, используя HLL для учета уникальности:

CREATE TABLE daily_uniques (
    date DATE UNIQUE,
    users hll
);
UPDATE daily_uniques
SET users = hll_add(users, hll_hash_text('visitor_ip'))
WHERE date = current_date;

HLL также поддерживает оценку уникальности для комбинаций нескольких столбцов, например, для идентификации уникальных комбинаций юзеров и продуктов:

SELECT hll_cardinality(hll_sketch) AS estimated_distinct_count
FROM (
    SELECT hll_add(hll_hash_any(ARRAY[user_id, product_id])) AS hll_sketch
    FROM orders
) AS subquery;

Можно юзать HLL в системах, где необходима высокая производительность запросов на больших объемах данных, например, для агрегации уникальных посетителей с различными характеристиками:

SELECT hll_cardinality(hll_union_agg(unique_visitors))
FROM daily_unique_visitors
WHERE ts >= '2019-07-01' AND ts < '2019-08-01' AND customer_id = 'A' AND location = 'US';

HLL предлагает аппроксимацию количества уникальных элементов с типичной относительной точностью (стандартной ошибкой) около 2%, при этом используя всего около 1.5 КБ памяти. Т.е HLL хорош для приложений, где необходимо обрабатывать большие объемы данных с приемлемой точностью, но не требуется абсолютная точность.

Однако, несмотря на в целом хорошую эффективность, HLL имеет свои ограничения, в частности, в плане точности. HLL предоставляет аппроксимацию количества уникальных элементов, что может не подходить для приложений, требующих абсолютной точности, например, в фин. расчетах или при выполнении юридически значимых операций. Кроме того, при использовании HLL могут возникать ошибки округления, особенно при работе с очень большими или очень малыми наборами данных.

Материал подготовлен в преддверии старта продвинутой обучающей программы по PostgreSQL.

Комментарии (2)


  1. vmalyutin
    21.04.2024 16:49

    К сожалению, не понял ровным счётом ничего. Даже в какой области применяется это расширение не понятно. Это не значит, что оно бесполезно. Просто я ничего не понял.

    В общем, вместо того, чтобы писать, что делает функция, лучше объяснить зачем она это делает.

    Я вот не понимаю зачем считать пользователей примерно, когда можно точно.

    И что значит использует мало памяти? Оно разве не сканирует таблицы? Использует индексы? Какие, если использует?

    Вы надеюсь понимаете, что у PG есть несколько кешей, чтобы хоть что-то прочитать. Расширение, как-то их минует? Например, если в таблице 100 блоков, а каждый по 8КБ, то 100 * 8 = 800. 800 /1024 = чуть меньше мегабайта. А это 100 страниц всего лишь. Это меньше меньшего. Обычно в больших таблицам гигабайты. Куда денется все это если это прочитать?


    1. vmalyutin
      21.04.2024 16:49

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

      Так-то любой group by это сначала sort. А sort еще как память кушает. Hash тоже операция не из легких. Проц хороший нужен. А у вас что?