Ранее в этом году мы приступили к крупнейшему в нашей компании
Естественно, мы выбрали Timescale, нашу зрелую облачную платформу, в основе которой лежит TimescaleDB. Мы привыкли работать с PostgreSQL и создали TimescaleDB, чтобы сделать PostgreSQL более быстрым и масштабируемым — что может быть лучше, чем жить по собственному примеру?
Самый простой способ описать этот экспериментальный эксперимент — использовать цифры, которые помогут количественно оценить его масштаб. Чтобы создать Insights, нам нужно было собрать информацию о запросах по всему нашему парку постоянно работающих баз данных. Мы быстро собрали более 1 триллиона записей об отдельных (обработанных) запросах на платформе.
Теперь, когда Insights запущен в эксплуатацию, мы принимаем более 10 миллиардов новых записей в день . Набор данных, обслуживаемый одной службой Timescale, увеличивается примерно на 3 ТБ в день и в настоящее время составляет более 350 ТБ , и одна и та же служба базы данных обеспечивает работу информационных панелей в реальном времени для всех наших клиентов.
В этом сообщении блога можно заглянуть за кулисы процесса создания Insights. Работать в таком масштабе означало расширить границы одного сервиса Timescale и масштабировать не только PostgreSQL, но и нашу эмпатию к разработчикам. Мы считаем, что Timescale более чем соответствует поставленной задаче, но есть и области, которые мы хотим улучшить!
Чтобы реализовать Insights, нам пришлось надеть на себя роль администратора базы данных 🤠 и решить несколько технических задач по масштабированию PostgreSQL до многих терабайт данных. Мы хотели использовать службу Timescale в качестве нашей центральной базы данных, размещенной на нашей платформе без «специальной» инфраструктуры. Это означало следующее:
Нам пришлось построить конвейер, способный принимать миллиарды записей в день в единый сервис Timescale. Timescale может обрабатывать высокие темпы приема данных и регулярно делает это для наших клиентов, но такой уровень масштабирования при нагрузке производственных запросов всегда вызывает удивление.
Наши клиенты должны были иметь возможность запрашивать эту базу данных с достаточной гибкостью, чтобы обеспечить всю аналитику, которую предлагает Insights, и мы не хотели заставлять их ждать ответа несколько минут!
Нам нужно было хранить сотни ТБ в одном сервисе Timescale, поскольку каждый день мы добавляем несколько ТБ. Более старые данные (то есть старше нескольких недель) должны были быть доступными, но не обязательно быстрыми для запроса.
Со стороны сбора данных мы использовали архитектуру платформы Timescale. Timescale работает в Kubernetes (k8s), и у нас есть несколько кластеров k8s , работающих в разных географических регионах. В этих кластерах есть узлы, на которых содержится одна или несколько служб баз данных клиентов. Чтобы собрать данные о выполнении запросов для всех этих баз данных, мы перемещаемся из этой базы данных на региональный уровень, а затем имеем региональный модуль записи, который хранит пакеты записей в службе базы данных Timescale, которая поддерживает Insights.
Простите за волнение, которое позволяет избежать некоторых кровавых подробностей низкого уровня, но в общих чертах, вот как все работает: каждая база данных, работающая в парке, настроена на создание записи (очищенной в целях конфиденциальности и безопасности) после каждого запроса, включая сам запрос и статистика, которая нас интересует.
Эти записи собираются на уровне узла, помечаются метками, чтобы сохранить их связь с тем, из какой службы базы данных они получены, и группируются для отправки региональному записывающему устройству. Региональная служба записи реплицируется по мере необходимости для обработки нагрузки в каждом регионе. Каждый модуль записи собирает пакеты с узлов каждого кластера и создает еще более крупные пакеты.
Эти большие пакеты затем сначала записываются с помощью COPY во временную таблицу (без ведения журнала упреждающей записи = быстро). Записи в этой временной таблице затем используются для обновления необходимых таблиц (см. ниже). Временная таблица позволяет нам использовать COPY, не беспокоясь о дубликатах, которые обрабатываются последующими операциями, уничтожающими записи из временной таблицы.
Подводя итог:
Давайте увеличим масштаб базы данных , на которой работает Insights. Мы используем Insights в готовом сервисе Timescale с
База данных, лежащая в основе Insights, состоит из нескольких частей, но мы постараемся выделить наиболее важные из них.
Во-первых, у нас есть две обычные таблицы PostgreSQL, которые служат «справочными таблицами». Эти таблицы содержат метаданные информационной базы данных и метаданные строки запроса. Вот их (псевдо)схемы:
Метаданные базы данных
Table "insights.cloud_db" Column | Type | Collation | Nullable | Default ---------------+--------------------------+-----------+----------+-------------------------------------- id | bigint | | not null | nextval('cloud_db_id_seq'::regclass) service_id | text | | not null | project_id | text | | not null | created | timestamp with time zone | | not null | now() Indexes: "cloud_db_pkey" PRIMARY KEY, btree (id) "cloud_db_project_id_service_id_key" UNIQUE CONSTRAINT, btree (project_id, service_id)
Метаданные запроса
Table "insights.queries" Column | Type | Collation | Nullable | Default ---------------+--------------------------+-----------+----------+-------------------------------------- hash | text | | not null | normalized_query | text | | not null | created | timestamp with time zone | | not null | now() Indexes: "queries_pkey" PRIMARY KEY, btree (hash)
Каждый раз, когда к новой базе данных начинают выполняться запросы, она добавляется в `insights.cloud_db`. Каждый раз, когда запускается новый нормализованный запрос, он добавляется в `insights.queries`.
(Что такое нормализованный запрос? Это запрос, в котором все константы заменены заполнителями: $1 для первого, $2 для второго и т. д., поэтому мы видим только «форму» запроса, а не его значения. .)
До сих пор мы просто использовали обычный Postgres без секретного соуса Timescale. Но другие важные объекты в базе данных уникальны для TimescaleDB, что помогает масштабировать PostgreSQL на новый уровень. Вот где происходит волшебство: гипертаблицы и непрерывные агрегаты.
Гипертаблицы — это автоматически секционированные таблицы Timescale. Они автоматически секционируют данные по измерениям во время их приема, что значительно упрощает масштабирование таблиц PostgreSQL до больших масштабов. Гипертаблицы являются строительными блоками шкалы времени. Как мы увидим позже, мы храним показатели статистики запросов в огромной гипертаблице.
Непрерывные агрегаты — это улучшенная версия материализованных представлений PostgreSQL от Timescale, позволяющая выполнять инкрементную и автоматическую материализацию, что оказалось очень полезным при построении Insights.
Давайте рассмотрим, как мы использовали эти функции для обеспечения быстрых аналитических запросов на стороне пользователей.
Как мы уже говорили, мы используем большую гипертаблицу для хранения информации о каждом выполнении запроса. Эта гипертаблица — наша основная таблица, в которой хранятся очищенные необработанные метрики. Он выглядит примерно так, как показано ниже, и настроен на использование столбца метки времени ( created
) для автоматического секционирования данных по мере их приема.
Table "insights.records" Column | Type | Collation | Nullable | Default -----------------------------+--------------------------+-----------+----------+--------- cloud_db_id | bigint | | not null | query_hash | text | | | created | timestamp with time zone | | not null | total_time | bigint | | | rows | bigint | | | ...
В этом примере мы опустили ряд статистических данных, но суть вы поняли.
Теперь нам нужно разрешить быстрые запросы со стороны пользователя, но эта таблица огромна. Чтобы ускорить процесс, мы в значительной степени полагались на непрерывные агрегаты (используя
Непрерывные агрегаты имеют большой смысл в таком продукте, как Insights, предлагающем аналитику в режиме реального времени, ориентированную на пользователя. Чтобы предоставить пользователям полезную информацию, нам необходимо агрегировать показатели: мы не показываем пользователям журнал каждого выполненного ими запроса со статистикой рядом с ним — некоторые базы данных выполняют тысячи запросов в секунду, поэтому было бы кошмаром найти что-нибудь полезное. Вместо этого мы обслуживаем совокупность пользователей.
Таким образом, мы могли бы также воспользоваться тем фактом, что мы не показываем пользователям необработанные отдельные записи, и сохранить результат.
Мы могли бы использовать материализованные представления PostgreSQL, но непрерывные агрегаты Timescale имеют несколько преимуществ, которые были особенно полезны для нас. Мы часто обновляем представления, а непрерывные агрегаты имеют встроенные политики автоматического обновления и обновляются постепенно.
Мы обновляем представления каждые пять минут, поэтому вместо повторного создания всей материализованной информации каждые пять минут непрерывные агрегаты постепенно обновляют представление, отслеживая изменения в исходной таблице. В том масштабе, в котором мы работаем, мы просто не можем себе позволить сканировать нашу основную гипертаблицу сверху вниз каждые пять минут, поэтому функциональность непрерывных агрегатов стала для нас фундаментальной «разблокировкой».
С помощью этих непрерывных агрегированных агрегированных данных мы также объединяем большую часть интересной статистики в
Тем не менее, в определенный момент база данных начала выполнять большую работу по вставке всех этих необработанных записей и их последующей материализации для обслуживания. Мы столкнулись с некоторыми ограничениями на то, сколько мы могли проглотить и поддерживать.
Чтобы еще больше увеличить скорость приема данных до необходимого нам уровня, мы передали генерацию UDDSketch из базы данных авторам регионов. Теперь мы по-прежнему храним некоторое количество записей как «необработанные» записи, но остальные также помещаем в предварительно сгенерированные эскизы, которые сохраняем в базе данных:
Table "insights.sketches" Column | Type | Collation | Nullable | Default -----------------------------+--------------------------+-----------+----------+--------- cloud_db_id | bigint | | not null | query_hash | text | | | created | timestamp with time zone | | not null | total_time_dist | uddsketch | | | rows_dist | uddsketch | | | ...
Самое приятное в UDDSketchs то, что скетчи можно постоянно «сворачивать» для поддержки больших временных диапазонов. Используя такое объединение, эскизы, охватывающие более узкие временные диапазоны, можно агрегировать в эскиз, охватывающий более широкий временной диапазон, как при построении иерархического непрерывного агрегата, так и во время запроса.
Еще один инструмент, который мы использовали для обеспечения быстрого приема и выполнения запросов, — это реплики чтения. В нашем случае использование репликации имеет первостепенное значение как для обеспечения высокой доступности, так и для производительности, поскольку Insights обеспечивает основную функцию, ориентированную на клиента, для платформы Timescale.
Наш основной экземпляр базы данных очень занят объемной работой, записью данных, материализацией непрерывных агрегатов, сжатием и многим другим. (Подробнее о сжатии чуть позже.) Чтобы частично облегчить нагрузку, мы разрешили клиенту службы реплик читать запросы из консоли Insights.
Наконец, нам нужно было удобно разместить сотни ТБ в едином сервисе Timescale. База данных Insights быстро масштабируется: когда мы начинали, ее размер составлял порядка 100 ТБ, а сейчас он превышает 350 ТБ (и продолжает расти).
Чтобы эффективно хранить такой большой объем данных, мы включили
Мы наблюдаем более чем 20-кратное сжатие нашей основной гипертаблицы.
Еще одним большим преимуществом при управлении очень большой гипертаблицей стала возможность изменения схемы сжатых данных. Мы описали нашу приблизительную схему в предыдущем разделе, но, как вы можете себе представить, мы часто меняем ее, чтобы добавить больше статистики и т. д. Очень полезно иметь возможность делать это непосредственно в сжатой гипертаблице.
Мы также активно используем многоуровневое распределение данных Timescale. Эта функция вошла в ранний доступ ранее в этом году (скоро ждите новостей GA 🔥) и позволяет нам сохранять доступ к сотням ТБ через нашу базу данных Timescale. Многоуровневое хранение данных также оказалось очень эффективным: здесь мы также видим потрясающую степень сжатия: 130 ТБ сократились до высокоэффективных 5 ТБ.
Процесс создания Insights показал нам, как далеко на самом деле может зайти наш продукт, но лучше всего было пройти несколько миль на месте наших клиентов. Мы узнали много нового о пользовательском опыте масштабирования PostgreSQL с помощью Timescale и добавили кое-что в наш список дел как инженеров, стоящих за продуктом.
Давайте пройдемся по всему: и хорошему, и так себе.
Простите за нескромность, но временами мы чувствовали себя очень гордыми своим продуктом. Ежедневно вносить десятки миллиардов записей в единую базу данных PostgreSQL, содержащую уже сотни ТБ, — это не шутка . Мы потратили пару недель на настройку базы данных, когда она начала набирать обороты, но теперь она просто работает , без присмотра за детьми и постоянного мониторинга. (Обратите внимание, что это отличается от отсутствия контроля: оно определенно контролируется!)
Наш
Сжатие сработало для нас очень хорошо. Как мы рассказывали в предыдущем разделе, мы получили впечатляющую степень сжатия (20x!), используя простую опцию «сегментировать по». Для нас опыт настройки и корректировки политики не был трудным — хотя мы, конечно, эту фичу построили… можно сказать, у нас есть небольшое преимущество. 🙂 Кроме того, возможность плавно добавлять новые столбцы в сжатые данные еще больше повысила гибкость и адаптируемость нашей базы данных. Мы использовали эту возможность без осложнений.
Непрерывные агрегаты упрощают логику построения различных периодов времени, оптимизируя анализ и обработку данных. Мы использовали тонны иерархических непрерывных агрегатов.
Алгоритмы аппроксимации, включенные в гиперфункции Timecale, упростили нашу реализацию и значительно масштабировали наш анализ. Возможность легкого объединения эскизов также была ключевой для эффективной поддержки различных временных диапазонов и детализации временных интервалов на наших информационных панелях Insights, ориентированных на клиентов.
«Бесконечное» теплое хранилище, которое база данных Timescale получает в свое распоряжение посредством многоуровневого хранения данных, имеет решающее значение для масштабирования до сотен ТБ с достаточным запасом для роста. Наша текущая __ политика многоуровневого хранения данных __ сохраняет записи в горячем хранилище в течение трех недель.
Наконец, мы использовали возможность создавать собственные задания для повышения наблюдаемости (например, мониторинг истории заданий) и реализовали экспериментальные стратегии обновления.
После рассказа вам всего хорошего, пришло время признать и не очень хорошие. Ничто не идеально, включая Timescale. При реализации нашего конвейера мы столкнулись с несколькими проблемами, и мы не считаем их недовольством:
На платформе Timescale можно улучшить наблюдаемость базы данных, особенно в отношении заданий и производительности непрерывной материализации агрегатов.
TimescaleDB в основном предоставляет представления на основе моментальных снимков, что затрудняет понимание производительности и тенденций с течением времени. Например, не существует готовой таблицы «истории заданий». Вначале мы заметили, что постепенная материализация наших непрерывных агрегатов, казалось, занимала все больше и больше времени — что в конечном итоге привело к обнаружению ошибки — но у нас не было возможности подтвердить или количественно оценить объем.
Как мы отмечали ранее, возможность определять собственные задания и запускать их в среде заданий Timescale позволила нам создать «достаточно хорошую» версию. Мы постоянно запрашивали представления, которые хотели отслеживать, и вносили любые изменения в гипертаблицу. На данный момент это работает для Insights, но мы также работаем над тем, чтобы превратить некоторые из этих вещей во встроенные функции, потому что мы считаем, что они имеют решающее значение, когда вы масштабируете Timescale за пределы точки «все быстро и всегда». .
Непрерывные агрегаты могут быть сложными для правильного выполнения, если базовые данные большие .
Использование опции `__ БЕЗ ДАННЫХ` при создании непрерывных агрегатов __ спасает жизнь. Также важно разумно подходить к смещениям политики обновления, чтобы объем данных, которые вы постепенно обновляете, случайно не стал слишком большим.
Даже если вы последуете этому совету, вы все равно можете получить непрерывный агрегат, обновление которого займет больше времени, чем объем данных, которые вы пытаетесь материализовать, например, для материализации 15 минут данных потребуется 30 минут. Это происходит потому, что иногда базовая задача непрерывного агрегирования слишком велика, чтобы поместиться в памяти, и выгружается на диск.
Мы столкнулись с этой проблемой, которая усугублялась из-за найденной нами ошибки с отклонением на единицу (теперь исправленной), из-за которой в план запроса включались дополнительные фрагменты, даже если они в конечном итоге не вносили никаких данных в материализацию. Обнаружение этой ошибки на самом деле было случаем «собачьей реакции»: мы обнаружили эту проблему с производительностью, когда использовали Insights во время его разработки 🤯. Информация о времени, которую мы увидели в Insights, показала, что здесь что-то не так, и мы обнаружили проблему, используя EXPLAIN и просмотрев планы. Поэтому мы можем сказать вам, что это работает!
Чтобы ускорить материализацию, мы создали собственную политику добавочного обновления, которая ограничивала размер приращений для обновления. Мы работаем над тем, сможем ли мы правильно обобщить это в TimescaleDB.
Изменения сложны в масштабе .
Как только ваши данные достигнут определенного размера, некоторые операции DDL (модификация схемы) в TimescaleDB могут занять больше времени, чем идеально. Мы уже испытали это несколькими способами.
Например, добавление новых индексов в большие гипертаблицы становится трудоемким занятием. Поскольку TimescaleDB в настоящее время не поддерживает использование CONCURRENTLY с CREATE INDEX, следующим лучшим вариантом является использование встроенного метода для создания индекса по частям. В нашем случае мы должны запустить его сразу после создания нового чанка, поэтому блокировка «активного» чанка минимальна. То есть создание индекса, когда чанк новый, означает, что он (почти) пуст и, следовательно, может завершиться быстро и не блокировать новые вставки.
Другой способ внесения изменений — это обновление непрерывных агрегатов для добавления новых показателей (столбцов). Непрерывные агрегаты в настоящее время не поддерживают ALTER. Итак, когда мы хотим предоставить пользователям новую метрику, мы создаем совершенно новую «версию» непрерывного агрегата, т. е. для непрерывного агрегата «foo» у нас будут «foo_v2», «foo_v3» и т. д. Это далеко не идеал, но в настоящее время работает.
Наконец, изменить настройки сжатия довольно сложно в масштабе. Фактически, сейчас это для нас практически невозможно, так как для этого потребуется распаковать все сжатые фрагменты, изменить настройки, а затем повторно сжать их, что неосуществимо в наших нынешних масштабах.
Мы продолжаем проводить мозговые штурмы с нашими коллегами, чтобы найти действенные решения всех этих проблем. Не только для нас, но и для всех пользователей Timescale.
Информации было довольно много, чтобы собрать ее в одном посте. Но если вам нужен
Building Insights стал для нашей команды глубоким опытом. Мы своими глазами увидели, как далеко мы можем продвинуть Timescale, доведя его до впечатляющих масштабов. Болевые точки, с которыми мы столкнулись в процессе, дали нам столько сочувствия к клиентам — в этом вся прелесть собачьего кормления.
В следующем году я надеюсь написать еще одну публикацию в блоге о том, как мы отслеживаем еще на порядок больше баз данных и как мы продолжаем улучшать опыт работы с Timescale в больших масштабах.
Тогда увидимся! 👋
Также опубликовано здесь.