Всем привет! Меня зовут Дмитрий Листвин, я занимаюсь аналитическим хранилищем данных в Авито.
Эта статья — третья часть серии про миграцию аналитической платформы Авито из классического DWH в Lakehouse. В первой статье мы разобрали предпосылки миграции и первые шаги, а во второй — почему замена движка оказалась лишь началом и какую экосистему пришлось построить вокруг Trino.
В этой статье я собрал наш опыт построения Lakehouse поверх объектного хранилища, как реальная аналитическая нагрузка быстро превращает «обычный S3» в самый капризный элемент всей архитектуры. Будет много про извлечение максимума производительности из Ceph: как добиться высокой пропускной способности HDD, когда поверх данных хочется запускать тяжёлые аналитические запросы.

Статья будет полезна архитекторам хранилищ, инженерам платформ данных и дата-инженерам — всем, кто строит аналитику поверх S3-совместимых хранилищ или только выбирает, какое решение использовать. Сразу дисклеймер: большинство описанных проблем и решений одинаково актуальны для любого объектного хранилища, не только Ceph.
TL;DR для тех, кто пришёл за практикой:
строить Lakehouse на объектном сторадже сложно;
S3-подобные хранилища не рассчитаны на многотысячные параллельные GET-запросы;
можно сделать быстрый и устойчивый Lakehouse поверх S3, если заранее знать, какие грабли ждут.
В статье — реальные проблемы, метрики и решения от Авито.
Как устроен наш Lakehouse
Аналитическая платформа Авито — система общего назначения, которая объединяет ad-hoc запросы, регулярную загрузку данных, регламентные расчёты витрин, метрик, фичей, отчётов и всё остальное, что обычно происходит в классическом DWH. Данные загружаются, раскладываются по слоям и выгружаются во внешние системы и сервисы.
Ежедневная аудитория ad-hoc и интерактивных сценариев — порядка 800 человек: аналитики, разработчики, ML-инженеры и специалисты внутренних платформ. Всё вместе это создаёт высокий и разнообразный профиль нагрузки на Lakehouse.

Ранее весь стек работал на Vertica. Сейчас мы находимся в процессе миграции на Trino + Ceph. С Vertica всё было хорошо, но с ростом данных, увеличением нагрузки, числа запросов от разных команд и необходимостью предоставлять гарантии мы начали упираться в ограничения масштабируемости и стоимость лицензии. Поэтому мы стали двигаться в сторону разделения вычислений и хранилища: вычисления выполняет Trino — open-source MPP SQL-движок, который сам по себе ничего не хранит и обрабатывает данные из даталейков и других внешних систем, — а хранение обеспечивает Ceph, open-source S3-совместимое объектное хранилище.

Нагрузку на наше объектное хранилище создают два крупных кластера Vertica по 50 нод и около двадцати кластеров Trino — от совсем небольших до крупных на десятки машин. В сумме это больше 30 000 ядер, которые в любой момент могут одновременно читать данные из Ceph. И именно такой уровень параллелизма формирует ту самую нагрузку, с которой приходится справляться объектному хранилищу.
Наш Ceph развёрнут в трёх дата-центрах. В каждом — по 15 нод, и каждая нода — это двенадцать HDD и один SSD, за работу каждого из которых отвечает отдельный OSD-демон. У каждого OSD есть собственная служебная база: write-ahead-log и база данных, в которой хранятся метаданные. На ноде также работает RGW — компонент, который обеспечивает S3-интерфейс и ведёт индекс, отображающий S3-ключи на реальные объекты в хранилище.
Когда мы записываем объект в Ceph, он разбивается на небольшие чанки, и каждый чанк хранится с репликацией на нескольких OSD, которые CRUSH-алгоритм распределяет по разным серверам и дата-центрам. Благодаря этому данные остаются доступными независимо от того, что происходит с отдельными дисками, нодами или даже целыми площадками.

Чтобы понять, как аналитическая нагрузка выглядит для объектного хранилища, важно разобраться, как табличные данные оказываются в S3 и как затем Trino их читает.
В Lakehouse таблицы хранятся в табличных форматах вроде Hive или Iceberg. Формат определяет структуру файлов, правила партиционирования и то, как вычислительный движок должен взаимодействовать с данными. Поверх формата всегда есть каталог (метастор), который хранит метаданные: схему таблицы, свойства и путь в объектном хранилище, где лежат данные.
Дальше всё происходит примерно так. Когда Trino выполняет SELECT * FROM my_table, он сначала запрашивает у каталога путь к данным. Затем идёт в S3 и делает листинг — получает список всех файлов, чьи ключи начинаются с этого префикса. После этого Trino распределяет эти файлы между worker-нодами, и каждая нода начинает читать и обрабатывать свой набор файлов.

Итог: такая архитектура даёт гибкость и масштабируемость вычислений, но одновременно создаёт сложный профиль нагрузки на общее хранилище, и именно в этом месте сторадж перестаёт быть «фоном» и начинает играть главную роль.
Как мы измеряем «здоровье» S3-хранилища
Теперь разберёмся, как мы измеряем эффективность взаимодействия между Trino и Ceph.
GET latency — главный термометр Ceph
Первая ключевая метрика, за которой мы следим, — GET latency. Это задержка до начала передачи данных от хранилища: время между запросом и моментом, когда RGW начинает отдавать первые байты.
С точки зрения классических реляционных систем несколько секунд могут выглядеть как критическая задержка, но в аналитике контекст другой: запросы читают большие объёмы данных, и такие операции выполняются тысячами параллельно. Поэтому мы для себя определили порог в 5 секунд по 95-му перцентилю: пока latency держится ниже этого значения, деградация на уровне Trino не ощущается.

Мы постепенно улучшаем архитектуру и работу со стораджем, поэтому ожидаем, что со временем сможем снижать этот порог. Но если 95-й перцентиль стабильно превышает пять секунд, проблемы становятся заметны сразу.
Throughput — сколько Ceph реально может отдавать
Вторая ключевая метрика — скорость отдачи данных хранилищем. Мы смотрим на общую пропускную способность Ceph под реальной нагрузкой: сколько гигабайт в секунду он способен отдавать, когда несколько вычислительных кластеров читают данные одновременно. Высокие значения здесь напрямую означают стабильную работу стораджа.
Минуты деградации — агрегированная метрика здоровья
Третья метрика — минуты деградации. Это по сути производная от GET latency: мы считаем минутой деградации любую минуту, в которой 95-й перцентиль задержки GET поднялся выше 5 секунд. Такой порог позволяет отслеживать общее «самочувствие» хранилища: растёт ли число проблемных минут или, наоборот, система становится стабильнее. Таких эпизодов у нас было немало — обычно всё начинается с роста задержек, после чего мы разбираем ситуацию и вносим изменения. На графике хорошо видно, как показатели сначала ухудшались, а затем — после наших доработок — пошли вниз.

Вывод: Чтобы строить быстрый Lakehouse, нужно также тщательно мониторить хранилище, как и compute.
Первый удар: листинги и балансировка
Проблемы с листингом
Использовать S3 вроде бы очень просто: прописал адрес хранилища — и всё, данные туда записываются и читаются. Но стоит дать реальную нагрузку — и быстро выясняется, что у объектного стораджа есть множество нюансов, о которых никто не пишет.
Сначала — про Vertica. Мы сделали собственный Lakehouse-движок с помощью Vertica UDx SDK: создаёшь внешнюю таблицу и работаешь с данными в S3 так, будто это обычная таблица в Vertica. Мы серьёзно его оптимизировали — настолько, что по скорости он был сопоставим с нативной Vertica.
И всё действительно работало отлично, пока мы не перевели на эту схему одну из самых популярных таблиц нашего хранилища — витрину dma.click_stream. Как только все запросы к ней пошли в Ceph, начали проявляться проблемы: стало видно, что объектное хранилище под такой нагрузкой ведёт себя иначе, чем локальное хранилище Vertica, и именно здесь впервые вылезли реальные ограничения S3.
Первая проблема проявилась на этапе планирования запроса. В Vertica планирование должно занимать меньше минуты, но вдруг стало выходить за этот предел — и запросы просто начинали отваливаться по таймауту.
Когда мы начали разбираться, оказалось, что дело в банальном листинге файлов: Vertica тратила больше минуты только на то, чтобы получить список объектов в S3. Причин было две:
данные лежали в большом количестве файлов;
одновременно на Ceph шла высокая нагрузка, и листинг становился всё медленнее.
В результате запросы к большим и популярным таблицам перестали выполняться совсем.

Первым шагом к исправлению стало кеширование результатов листинга на нодах Vertica. Это позволило снова укладываться в минуту на планирование, и запросы начали хотя бы стартовать. Но сама проблема никуда не исчезла: запросы выполнялись заметно медленнее, latency росла, аналитики долго ждали результаты, а регламентные витрины иногда просто не успевали пересчитаться вовремя.
Балансировка нагрузки
Теперь — про Trino. У нас есть 45 RGW-нод, и все они «спрятаны» за одним DNS-именем, которым управляет Consul. Казалось бы, Consul должен раздавать разные IP-адреса и равномерно распределять нагрузку.
Но Trino устроен так, что worker-ноды кешируют IP-адрес DNS-записи. При выполнении длинного запроса воркер один раз резолвит DNS, запоминает IP и дальше ходит строго в одну RGW-ноду, полностью обходя Consul. В итоге часть RGW перегружалась, а остальные простаивали.

Мы решили это просто: рядом с каждой compute-нодой развернули sidecar-HAProxy и полностью исключили Consul DNS из цепочки. В HAProxy прописали все 45 RGW с round-robin — и нагрузка сразу начала распределяться равномерно.
Это решение оказалось удивительно полезным. Почти сразу мы добавили сбор access-логов на HAProxy и получили детализацию: какие запросы выполняют Trino и Vertica, какие файлы читаются чаще всего и где возникают пики нагрузки. Благодаря этому мы намного лучше понимаем, что происходит в Lakehouse под реальной нагрузкой.
Практика: S3 — это не только чтение данных. Листинг и распределение нагрузки могут стать узким местом ещё до того, как начинается само чтение.
Топ-10 таблиц против вашего стораджа
Следующая проблема, знакомая любому аналитическому хранилищу, — это избыточное чтение популярных таблиц. В нашем случае ежедневно читается около петабайта данных из Ceph, и примерно треть этого объёма приходится всего на топ-10 таблиц. Остальные десятки тысяч таблиц в сумме дают оставшиеся две трети.

На практике это приводит к заметному перекосу нагрузки на дисках: при чтении популярных таблиц мы регулярно видим, что часть OSD оказывается нагружена значительно сильнее остальных. Вероятно, это связано с тем, что именно на этих дисках в конкретный момент времени сосредоточена большая доля «горячих» объектов, и основной поток чтения приходится именно на них. В результате даже при формально равномерном распределении данных отдельные диски начинают работать на пределе, а общая производительность хранилища проседает.
Задача аналитического хранилища — по максимуму сокращать лишнее чтение: использовать partition pruning, фильтры, колоночные форматы и любые другие способы не трогать то, что не нужно. Но полностью избежать full scan невозможно — и когда запрос действительно должен прочитать таблицу целиком, системе приходится адаптироваться к повышенной нагрузке.
Решением, которое сильно помогло нам снизить избыточное чтение, стало локальное кеширование на стороне Trino. В Trino есть собственный механизм кеша на базе Alluxio: если на worker-ноде есть локальный SSD (у нас это небольшие диски, около 512 ГБ), Trino использует его как прозрачный кеш для объектов из хранилища.

За счёт распределения данных по нодам кластер получает эффект «общего» кеша: разные worker-ноды скрывают у себя разные части Lakehouse, а суммарный объём SSD-дисков превращается в единый пул cache space для всего кластера.
Эффект оказался впечатляющим. На одном из самых нагруженных кластеров суточный объём чтения из Ceph упал с ~100 ТБ до ~10 ТБ после включения локального кеширования — почти десятикратное снижение нагрузки на сторадж.
Конечно, кеш работает не везде одинаково. На кластерах с предсказуемой нагрузкой — где запросы регулярно ходят в ограниченный набор таблиц — он даёт максимальный эффект. А на ad-hoc кластерах, которые постоянно обращаются к множеству разных датасетов, эффективность ниже. Но даже там локальный кеш помог нам пережить периоды деградации и стабилизировать работу системы.
Вывод: локальные кеши — must-have для больших Lakehouse. Они могут снизить нагрузку на S3 в разы.

Потолок Ceph и иллюзия линейного роста
Когда объёмы данных и нагрузка выросли, мы впервые заметили, что Ceph упирается в потолок пропускной способности. И это был не тот потолок, который мы ожидали увидеть.
На каждой ноде у нас по 12 HDD, плюс сеть между нодами Ceph. Казалось бы: общая пропускная способность должна примерно соответствовать сумме скоростей всех дисков (пока сеть не стала бы узким местом). Но на практике мы упирались примерно в 10 ГБ/с (гигабайт в секунду), хотя теоретически кластер из 27 нод мог выдавать в несколько раз больше.
И как только мы приближались к этому пределу, начиналось самое неприятное: GET latency начинала стремительно расти. То есть Ceph больше не мог отдавать данные быстрее, а вычислительная платформа всё ещё требовала. В результате:
операции на чтение резко замедлялись;
появлялись таймауты;
даже простые
SELECT … LIMIT 10выполнялись мучительно долго;аналитики видели задержки и «подвисшие» запросы.
По сути, мы достигали точки, где Ceph говорил: «выше головы я не прыгну», а всё остальное вокруг продолжало давить на газ.

Горизонтальное масштабирование
С проблемой потолка скорости нужно было что-то делать. Логичное первое решение — добавить новые ноды в Ceph, чтобы кластер мог масштабироваться горизонтально. Мы так и сделали: добавили ещё 9 нод к существующим.
Но у горизонтального масштабирования есть своя цена. После расширения Ceph запускает длительную ребалансировку данных — у нас она шла почти неделю. Мы рассчитывали, что за счёт дополнительных дисков и OSD пропускная способность вырастет примерно на 30%: с ~10 ГБ/с хотя бы до 13–14 ГБ/с.
Но в итоге… ничего не изменилось.
Скорость доступа осталась прежней, как будто новых нод вовсе не появилось.
Вывод оказался простым, но неприятным: если система упирается в другой bottleneck, горизонтальное масштабирование не даёт эффекта. А в какой именно — на тот момент нам ещё только предстояло разобраться.

Вторым сюрпризом стало то, что линейное масштабирование Ceph тоже имеет свой практический предел. Коллеги из команды, которая поддерживает Ceph-кластеры в Авито, уже сталкивались с подобными эффектами на крупных инсталляциях и для кластеров, растянутых на три дата-центра, эмпирически пришли к выводу, что оптимальный размер кластера — порядка 45 нод.
Почему так происходит, если Ceph считается горизонтально масштабируемой системой? Дело в том, что помимо OSD, которые масштабируются достаточно хорошо, в Ceph есть и управляющие компоненты — мониторы (MON) и менеджеры (MGR). Они запускаются в нескольких экземплярах, но работают в режиме active–standby. Вся нагрузка, связанная с поддержанием консистентного состояния кластера, оркестрацией, health check и координацией фоновых процессов, фактически ложится на один активный инстанс.
Начиная с определённого масштаба — в нашей конфигурации сети и топологии это как раз район нескольких десятков нод — эти процессы перестают укладываться в ожидаемые интервалы. Например, health check может не успеть выполниться вовремя: нода помечается как «упавшая», запускается цепочка восстановлений и ребалансировок, и кластер довольно быстро уходит в деградацию.
Для других конфигураций и топологий этот порог может быть иным, но для кластеров, растянутых на несколько площадок, размер порядка 45 нод оказался для нас разумным компромиссом между масштабированием и управляемостью.
Временное решение: стабилизировать, а не ускорять
Пока мы искали фундаментальное решение, нужно было хотя бы стабилизировать работу для пользователей. Для этого мы ввели жёсткие пределы скорости обращения к Ceph: на каждом Trino- и Vertica-кластере рядом с нодами стоял HAProxy, и именно через него мы ограничивали пропускную способность.
Мы распределили лимиты так, чтобы суммарная нагрузка не превышала тот самый потолок в ~10 ГБ/с. Да, отдельные кластеры стали работать медленнее, чем могли бы при идеальных условиях, зато мы полностью убрали экспоненциальный рост latency при перегрузке стораджа.
Это дало нам главное — предсказуемость и гарантии. Каждый вычислительный кластер получил стабильный SLA, а деградации Ceph на какое-то время исчезли из жизни пользователей.
Поиск новой конфигурации Ceph
Мы довольно быстро поняли, что синтетические бенчмарки S3 нам не помогают. Наша аналитическая нагрузка — тысячи параллельных GET-запросов, чтение byte-range из разных файлов и постоянные листинги — слишком далека от типовых тестов.
Поэтому мы пошли от реальности. Взяли по логам самые тяжёлые запросы, которые сильнее всего нагружают Ceph, выделили набор таблиц и собрали из этого собственный test suite. Для каждой новой конфигурации Ceph и внешнего S3-решения мы прогоняли этот набор запросов под сопоставимой нагрузкой и сравнивали результат с baseline на основном Ceph.
Именно так мы и нашли конфигурацию Ceph, которая оказалась почти в четыре раза быстрее текущей и заметно ближе к теоретическому потолку HDD.
Ключ к производительности оказался в устройстве OSD. У каждого OSD есть собственная внутренняя база: metadata, write-ahead log и служебная информация. И если вынести эту базу на отдельный NVMe-диск внутри Ceph-ноды, пропускная способность резко возрастает. Потолок в 10 ГБ/с превращается в потолок порядка 40 ГБ/с.
Мы пе��ешли на эту конфигурацию в продакшене — но сам переезд оказался нетривиальным. Прямая смена конфигурации или постепенная замена нод привели бы к длительной и крайне дорогой ребалансировке данных, растянутой на недели. Чтобы избежать этого, мы пошли другим путём: развернули отдельный Ceph-кластер уже с нужной конфигурацией и аккуратно перенесли данные, минимизировав влияние на рабочую нагрузку. В результате потолок пропускной способности Ceph наконец заметно сдвинулся, что сразу улучшило жизнь и ad-hoc аналитике, и регламентным расчётам.

Эксплуатационные мелочи
Есть вещи, которые легко недооценить, пока не начинаешь управлять объектным хранилищем самостоятельно. Но на самом деле они справедливы для любого S3 — и собственного, и облачного. У хранилища всегда есть собственная «внутренняя жизнь».
В Ceph, например, есть scrubbing — периодическая проверка данных на дисках, сверка реплик, поиск повреждений. Процесс полезный, но тяжёлый: он заметно нагружает HDD.
В какой-то момент эта фоновая нагрузка наложилась на нашу:
ночью — регламентные расчёты;
днём — ad-hoc и отчётность.
Вместе это начало ощутимо просаживать производительность. Оказалось, что расписание внутренних процессов Ceph можно настраивать. Мы вынесли активный scrubbing в вечерние окна — когда ни регламент, ни ad-hoc почти не давят на хранилище. После этого latency заметно снизилась.
Ещё одна вещь, которую легко недооценить, — заполненность дисков.
Для HDD после ~75% заполнения скорость чтения начинает падать: данные фрагментируются, растёт latency. Ceph это учитывает и вводит защитные пороги: уже при 85% заполнения кластер начинает сигнализировать о проблемах, а дальше быстро теряется запас для отказов и ребалансировки.
Вывод: в Lakehouse хранилище перестаёт быть пассивным слоем. Оно начинает диктовать правила — и чем больше масштаб, тем громче его голос.
Шумные соседи и почему LIMIT 20 может ждать полчаса
В какой-то момент мы обнаружили странную и очень раздражающую вещь: один тяжёлый запрос в Trino мог полностью «забрать» кластер — и оставить всех остальных аналитиков ждать.

Наш главный дефицитный ресурс — скорость чтения из Ceph. На уровне HAProxy мы ограничиваем общий throughput для каждого кластера. Но внутри самого кластера, особенно ad-hoc, где десятки аналитиков работают одновременно, достаточно одного человека, который запустит хитрый full scan — и он забивает весь выделенный канал.
Если кластеру выделено, например, 10 ГБ/с, один такой запрос может спокойно занять все 10 ГБ/с. Trino читает максимально агрессивно — использует всё, что есть. И в этот момент все остальные запросы, включая простой SELECT * FROM table LIMIT 20, оказываются в длинной очереди и могут ждать десятки минут.
Когда аналитиков в Trino стало много, такие ситуации начали возникать регулярно. Мы пробовали решать проблему средствами самого Trino — через resource groups, лимиты и приоритеты. Но на практике этот подход нам не подошёл: одного неудачного запроса всё равно оказывалось достаточно, чтобы забрать весь канал.
В итоге мы пошли другим путём. Мы доработали Trino так, чтобы он передавал в S3-запросах HTTP-заголовки с username и queryId. После этого на стороне HAProxy буквально в несколько строк конфигурации стало возможно ограничивать скорость — как на пользователя, так и на отдельный запрос.

Теперь даже тяжёлый full scan получает свой лимит (например, 4 ГБ/с) и больше не может забрать весь канал. Остальные аналитики спокойно выполняют лёгкие запросы — LIMIT 20 больше не ждёт окончания чужого скана. В результате проблема шумных соседей просто исчезла: соответствующая метрика ушла в ноль.
Практика: в shared ad-hoc кластере справедливость важнее скорости: лучше медленнее, но предсказуемо для всех.
Что делать дальше
Проблемы, конечно, никуда не исчезли.
Свободное место остаётся критичным фактором. Новый Ceph стал работать быстрее старого — но потолок всё равно есть, а нам хочется читать ещё быстрее. Плюс мы только начинаем по-настоящему понимать, какую нагрузку создаём на хранилище и как оно на неё реагирует.
Шардирование Ceph
Первое направление, которое мы сделали в MVP и сейчас внедряем в прод, — это шардирование Ceph.
Мы хотим масштабироваться линейно, но одиночный кластер ограничен примерно 45 нодами. Поэтому план такой: вместо одного большого Ceph — несколько кластеров по 45 нод, объединённых в единый логический S3.
Для клиентов и каталогов всё выглядит как один большой S3, но внутри — несколько независимых Ceph-к��астеров.

Так как у нас уже есть HAProxy, мы реализовали шардирование прямо на его стороне:
HAProxy держит кольцо хешей (consistent hashing).
Для GET и PUT запросов хеш вычисляется по ключу объекта, и HAProxy отправляет запрос на нужный шард.
С LIST и DELETE сложнее: результаты нужно либо объединять, либо отправлять сразу на все шарды.
Тут сильно помогли LUA-расширения в HAProxy — они позволили нам дописать нужную логику и собрать всё это в работающую систему.

Важно, что ключом шардирования мы используем путь к объекту. Это даёт простой, но очень мощный эффект: если таблица состоит из множества файлов, то при хешировании они равномерно разъезжаются по разным Ceph-кластерам.
В результате популярная таблица автоматически оказывается распределённой по нескольким шард-кластером, а не лежит целиком на одном. И когда Trino читает такую таблицу, оно параллельно тянет файлы сразу из нескольких Ceph, что даёт почти линейный прирост скорости. Например, при трёх кластерах мы можем читать данные примерно в три раза быстрее, чем с одного.
Глобальный троттлинг
Когда вычислительных кластеров стало много, вручную выставлять лимиты для каждого оказалось невозможным. Нам нужна единая точка принятия решения: можно ли сейчас пропускать запрос к Ceph или его нужно притормозить.
Для этого мы проектируем отдельный сервис-арбитр. Он получает от кластера минимальный набор данных — например, username, предполагаемый объём чтения, идентификатор кластера — и возвращает простой ответ: «разрешить» или «троттлить».
Преимущество в том, что этот сервис видит нагрузку на все Ceph-кластеры сразу, и может принимать динамические решения:
ограничивать скорость отдельного запроса;
ставить лимиты на пользователя;
на конкретный Trino- или Vertica-кластер;
на отдельный Ceph-кластер;
или применять глобальный потолок для всей системы.
Такой подход позволит гарантировать, что суммарное чтение со всех вычислительных кластеров никогда не превысит допустимый предел Ceph, но при этом каждое окно пропускной способности будет использовано максимально эффективно.
Кроме того, это создаёт честное распределение throughput между разными пользователями и задачами — будь то тяжёлые регламентные расчёты или лёгкий ad-hoc.

Умное локальное кеширование
Сейчас кеши в Trino работают довольно прямолинейно: как только SSD заполняется, новые данные записываются, а старые — просто вытесняются. На ad-hoc кластерах, где объёмы чтений огромные, eviction происходит постоянно — и даже популярные таблицы приходится скачивать заново. Мы хотим добавить поддержку white/black-листов: явно указывать, что нужно кешировать всегда, а что — никогда. Так мы избежим выпадения горячих таблиц из кеша.
Тиринг данных
Мы, конечно, строим дешёвое хранилище на HDD, но уже сейчас понимаем: не все сценарии дружат с жёсткими дисками. Поэтому наша задача — научиться эффективно использовать небольшое количество NVMe, чтобы ускорять чтение и снижать latency в самых критичных кейсах.
Первый шаг — вынести нагруженные таблицы с большим числом full scan в отдельный SSD-кластер.
Следующий — построить полноценный тиринг стораджей:
горячие и популярные данные — на быстрых NVMe;
основной объём — на обычных HDD;
холодные данные — на медленных, но вместительных дисках.
И главное: благодаря HAProxy между движком и хранилищем у нас есть гибкий слой абстракции, который позволяет реализовывать очень разные модели доступа и хранения данных, не трогая саму вычислительную платформу.
Вывод: архитектура хранилища — такой же живой организм, как и compute. Мы не «переехали на Trino», мы вступили на путь многолетней эволюции Lakehouse.
Главные мысли
1. Про сторадж нужно думать заранее
Если строите систему с нуля — закладывайте кеширование, горизонтальное масштабирование, рост объёмов и поведение под реальной нагрузкой. Даже облачные S3-сервисы имеют свои ограничения: например, AWS S3 гарантирует производительность только на уровне отдельных префиксов.
2. Теория ≠ практика
Простое перемножение характеристик HDD и расчёт «по паспорту» почти никогда не совпадают с реальными цифрами. Пустой кластер и кластер, заполненный на 85%, показывают совершенно разную скорость. Это важно учитывать, чтобы не строить иллюзий про «линейный рост».
3. Универсальных рецептов нет
Каждый большой Lakehouse — уникален. То, что отлично работает у одних, у других может вовсе не взлететь. Поэтому единственный надёжный путь — тестировать, профилировать, строить эксперименты и проверять гипотезы на собственной нагрузке.
Спасибо, что дочитали!
Пишите вопросы, делитесь опытом — давайте обсуждать, как строить быстрые и устойчивые Lakehouse поверх S3.
