TDB Documentation portal logo
Помощь
Обновлена 30 июля 2026 г. в 16:49

Охлаждение и нагрев данных в vinyl

Tarantool DB поддерживает два подхода к работе с данными на диске: архивация данных через модуль cooler и прямое хранение в дисковом движке vinyl. В этом разделе приведены рекомендации для второго варианта — прямого хранения данных в vinyl. При таком подходе большая часть данных хранится на диске, а часть в памяти в виде кэша и буфера на запись. Рекомендации приведены для типовых сценариев нагрузки (OLTP, витрины данных, архивация данных через cooler) и основаны на результатах нагрузочного тестирования vinyl на стендах с HDD и SSD. Общие рекомендации по настройке движка vinyl приведены в разделе Методика настройки движка vinyl в Tarantool DataBase.

Часто запрашиваемые кортежи называют горячими данными, а данные, к которым обращаются редко — холодными данными. В сценарии прямого хранения в vinyl нагрев и охлаждение данных происходят автоматически через LRU-кэш в памяти:

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

доступ к данным из памяти зависит от размера кэша (vinyl.cache) и рабочего набора данных. Чтобы данные оставались горячими, достаточно их регулярно запрашивать — нагрев происходит автоматически при каждом чтении. Размер кэша определяет, сколько горячих данных может одновременно находиться в памяти. Никаких дополнительных действий для нагрева или охлаждения не требуется, всем управляют частота чтения и размер кэша.

Для хранения данных на диске vinyl использует LSM-дерево (Log-Structured Merge Tree). В такой структуре данные сначала записываются в оперативную память на уровень L0. Размер уровня L0 задается параметром vinyl.memory. При заполнении уровня L0 происходит выгрузка уровня L0 на диск (dump). Вызвать выгрузку на диск можно также вручную, используя `box.snapshot. Данные при сбросе из памяти vinyl записывает в run-файлы, которые организованы по уровням L1, L2, ..., LN.

При чтении данных vinyl последовательно обращается к трём хранилищам. Каждый следующий шаг выполняется только в том случае, если кортеж не был найден на предыдущем:

  1. LRU-кэш в памяти (vinyl.cache) — оперативная память, в которой хранятся недавно прочитанные кортежи. Если кортеж недавно читали, он возвращается из кэша без обращения к диску (быстро). Если кортежа в кэше нет, поиск продолжается.
  2. In-memory уровень L0 (vinyl.memory) — первый уровень LSM-дерева, находится в оперативной памяти.
    Все новые кортежи сначала попадают сюда. Если кортежа нет в кэше, но данные ещё не выгружены на диск, чтение
    происходит из L0 без обращения к диску (быстро).
    Если кортежа на этом уровне нет, начинается чтение с диска.
  3. Диск (run-файлы) — постоянное хранилище. Если данных нет ни в кэше, ни в L0, они читаются с диска (медленно).

Устройство движка vinyl

LSM-дерево

Движок vinyl хранит данные на диске в виде LSM-дерева. Каждый индекс в спейсе имеет собственное LSM-дерево.

Ключевые элементы LSM-дерева:

  • Уровень L0 (первый уровень) находится в оперативной памяти, сюда попадают сначала все новые кортежи. Размер L0 определяется параметром vinyl.memory. Когда память заполнена, данные выгружаются на диск.
  • Уровни L1, L2, ..., LN находятся на диске в виде run-файлов. Каждый уровень иерархически больше предыдущего. Чем ниже уровень, тем более старые данные на нём находятся.

Индекс делится на диапазоны ключей (range), у каждого диапазона есть собственные поддерево и run-файлы. Размер диапазона вычисляется автоматически. Ключевым показателем, влияющим на задержку чтения, является количество run-файлов на диапазон. Среднее количество run-файлов на диапазон можно узнать через index:stat().run_avg. Использование диапазонов снижает нагрузку при чтении данных: если данные запрашиваются по узкому набору "горячих" ключей, затрагиваются только соответствующие диапазоны. Информация о методе index:stat() приведена в документации платформы Tarantool.

Слияние уровней

Когда данные выгружаются на диск, они попадают в run-файлы, и со временем vinyl запускает слияние уровней (compaction) — объединение нескольких run-файлов одного уровня в один файл, которое выполняется фоновыми задачами vinyl. В отличие от memtx, LSM-дерево в vinyl записывает операции над ключом, а не данные. Например, при замене кортежа добавляется запись о выполненной операции REPLACE c указанием ключа и данных.

При слиянии уровней операции над одним ключом объединяются: последовательные REPLACE, UPDATE, UPSERT объединятся в одну операцию REPLACE. Если после них было выполнено удаление, эти операции над ключом удаляются полностью. Слияние уровней оптимизирует поиск значений, удаляя лишние операции и освобождая место на диске. При этом слияние требует регулярной фоновой работы с диском и дополнительных операций чтения и записи, периодически выполняемых в фоне. Слияние уровней можно вызвать для индекса вручную через :compact.

Количество run-файлов на один диапазон ключей, которое может накопиться на уровне до запуска слияния, определяется параметром run_count_per_level (по умолчанию 2). Диапазонов при этом может быть много, и их количество может меняться. Параметр run_size_ratio (по умолчанию 3.5) определяет, во сколько раз следующий уровень больше предыдущего. На последнем уровне после слияния остаётся один run-файл. Чем ниже run_count_per_level и выше run_size_ratio, тем быстрее будет выполняться чтение, поскольку при чтении понадобится просматривать меньше файлов. В этом случае растут расходы на слияние уровней, поскольку при этом нужно выполнять больше оперций чтения и записи. И наоборот, чем выше run_count_per_level и ниже run_size_ratio, тем менее затратно слияние уровней, но медленнее чтении.

Размер уровня N относительно vinyl.memory (M) можно оценить по формуле:

Размер уровня N ≈ M × run_size_ratio^(N-1)

Например, при значениях vinyl.memory = 6 ГБ и run_size_ratio = 3.5 получится следующий размер:

Уровень L1: ~6 × 3.5 × 2 ≈ 42 ГБУровень L2: ~6 × 3.5² × 2 ≈ 147 ГБУровень L3: ~6 × 3.5³ × 2 ≈ 515 ГБУровень L4: ~6 × 3.5⁴ × 2 ≈ 1.8 ТБУровень L5: ~6 × 3.5⁵ × 2 ≈ 6.3 ТБ

Использование фильтров Блума

Фильтры Блума (Bloom filter) позволяют избежать лишнего чтения run-файлов при точечных запросах. Для операций с точечным чтением фильтр Блума эффективно отсеивает файлы, в которых нет искомого ключа. При использовании фильтра Блума возможны ложноположительные срабатывания, при которых файл читается без необходимости. Коэффициент ложноположительного срабатывания задается параметром bloom_fpr (по умолчанию 0.05). Чем ниже значение параметра, тем точнее фильтр и меньше выполняется лишних чтений, но выше потребление оперативной памяти. Для диапазонных запросов (select, pairs) фильтры Блума неэффективны, поскольку при этом просматриваются все файлы независимо.

Индексы страниц

В каждом run-файле есть индекс страниц, который хранится в памяти. Такой индекс позволяет сразу перейти к нужной странице без сканирования всего файла. Память под индексы и фильтры Блума можно оценить по следующей формуле:

RAM_index = expected_disk_bytes × K_idx × 1.2K_idx = (disk.index_size + disk.bloom_size) / disk.bytes

Здесь disk.index_size и disk.bloom_size — статистика из index:stat(). Коэффициент K_idx можно использовать для оценки памяти под конкретный спейс, умножив на ожидаемый объём данных и добавив запас в 20 %.

Сжатие данных

Движок vinyl сжимает данные на диске постранично с помощью алгоритма zstd. Размер страницы задаётся параметром vinyl.page_size.

Метрика box.stat.vinyl().disk.data показывает логический объём данных без сжатия. Фактический размер на диске может быть значительно меньше. Коэффициент сжатия сильно зависит от структуры данных и профиля нагрузки. Чем больше повторяющихся данных в странице, тем лучше работает сжатие, и наоборот. В тестах с полностью уникальными данными сжатие было близко к нулю.

Порядок настройки

При настройке охлаждения и нагрева данных в vinyl рекомендуется соблюдать порядок работы ниже:

  1. Определить тип носителя — HDD или SSD.

  2. Определить профиль нагрузки — OLTP, витрины или архивация данных.

  3. Задать необходимые значения параметров vinyl, в частности:

    • выбрать стартовое значение vinyl.memory, оно составляет 3–6 ГБ для большинства сценариев;
    • настроить параметр page_size в зависимости от профиля чтения.

    Рекомендованные значения параметров для типовых сценариев работы с vinyl (OLTP, витрины и архивация данных) описаны в разделе Типовые сценарии.

  4. Выполнить тестовую запись и чтение на репрезентативных данных.

  5. Дождаться завершения фоновых задач vinyl — выгрузки данных и слияния уровней.

  6. Измерить фактические метрики:

    • disk.data_compacted, disk.index_size, disk.bloom_size;
    • run_avg, run_count, rate_limit;
    • задержка p50, p99, p99.9.
  7. Скорректировать параметры с учётом замеров и целевой задержки, которая меньше или равна 50 мс.

Настройка параметров vinyl

В разделе перечислены параметры, непосредственно влияющие на охлаждение и нагрев данных в vinyl. Общие рекомендации по настройке всех параметров движка vinyl приведены в разделе Методика настройки движка vinyl в Tarantool DataBase. Рекомендованные значения параметров для типовых сценариев работы с vinyl приведены в разделе Типовые сценарии.

  • vinyl.memory — самый важный параметр, размер первого уровня LSM-дерева (L0) в оперативной памяти. Стартовое значение: 3–6 ГБ. Чем больше значение vinyl.memory, тем реже происходит выгрузка данных на диск и тем выше объём данных, которые можно хранить без деградации.

  • vinyl.page_size — размер страницы хранения данных на диске. Влияет на эффективность чтения и сжатия.

    • Для современных SSD рекомендуется увеличивать значение с 8 КБ до 64–128 КБ.
    • Для OLTP-сценариев (много точечных запросов с одним кортежем) выбирайте меньшее значение page_size.
    • Для витрин и отчётов (диапазонные выборки, агрегации) выбирайте большее значение page_size.
  • vinyl.run_count_per_level и vinyl.run_size_ratio — максимальное количество run-файлов, которое может накопиться на уровне до запуска слияния, и соотношение размеров соседних уровней соответственно.

  • vinyl.bloom_fpr — коэффициент ложноположительного срабатывания фильтра Блума. Значение по умолчанию: 0.05 (5%). Снижение значения уменьшает количество лишних чтений run-файлов при точечных запросах, но увеличивает потребление памяти. На практике при стандартных объёмах данных изменение значения опции значимого улучшения не выявило.

Типовые сценарии

В подразделах ниже приведены рекомендованные значения параметров vinyl для каждого профиля нагрузки:

OLTP: точечные чтения и запись

Много операций get, update, delete, insert по одному ключу. Размер кортежа составляет 1–10 КБ.

Рекомендации:

  • vinyl.memory: 3–6 ГБ;
  • page_size: 8–32 КБ, чтобы минимизировать избыточное чтение;
  • bloom_fpr: 0.05 или ниже при высокой нагрузке на чтение;
  • Рекомендуемое количество индексов составляет не более 1–2, поскольку каждый вторичный индекс добавляет отдельное LSM-дерево.

Витрины данных и отчётность

Диапазонные выборки (select с большим лимитом, pairs), агрегации. Объём запросов небольшой, но каждый запрос затрагивает много данных.

Рекомендации:

  • vinyl.memory: 3–6 ГБ;
  • page_size: 64–128 КБ;
  • read_threads (количество потоков чтения с диска): можно увеличить для ускорения больших выборок.

Фильтры Блума в данном сценарии менее эффективны, поскольку точечные чтения выполняются редко.

Архивация данных через cooler

Автоматический перенос устаревших кортежей из memtx в vinyl. После переноса кортежи остаются в vinyl для долгосрочного хранения, нагрев не предполагается.

Рекомендации:

  • Для обхода рекомендуется выбрать вторичный индекс, это снизит нагрузку сканирования;
  • Для full_scan_time и tuples_per_iteration используйте значения по умолчанию. Увеличивайте значения параметров при отставании архивации от роста данных;
  • Для page_size рекомендуется значение в 128 КБ, поскольку архивные данные редко читаются точечно;
  • Данные в vinyl не удаляются автоматически. Рекомендуется предусмотреть процесс финального удаления устаревших кортежей из vinyl.

Подробная информация об архивации данных приведена в разделах Архивация данных по их времени жизни и Настройка и запуск архивации.

Системные требования и рекомендации

Количество открытых файлов

Движок vinyl при работе создаёт большое количество run-файлов. Рекомендуется отслеживать текущее число run-файлов — их может быть много, поэтому рекомендуется увеличивать лимит на количество открытых файлов, иначе Tarantool станет доступным на запись.

Проверить текущий лимит можно, используя команду ниже:

ulimit -n

Установить лимит можно тремя способами:

  • в systemd-конфигурации сервиса Tarantool;
  • через /etc/security/limits.conf
  • при установке Tarantool DB через инсталлятор ATE, подробности указаны в документации ATE в разделе Предварительная настройка сервера под Tarantool (изменение Unix-лимитов для пользователя tarantool - max open files). Значения в 64000, рекомендованного в документации ATE, может быть недостаточно.

Память под индексы

Индексы и фильтры Блума run-файлов выгружаются в оперативную память. Для оценки необходимого объема памяти выполните запись репрезентативных данных, дождитесь завершения выгрузки и слияния, а затем снимите статистику.

Пример

-- Статистика по индексуlocal s = box.space.my_space.index[0]:stat()-- Память под индексы и фильтры Блумаlocal index_size = s.disk.index_sizelocal bloom_size = s.disk.bloom_size

Коэффициент K_idx = (index_size + bloom_size) / s.disk.bytes можно использовать для оценки: RAM_index = Expected_disk_bytes × K_idx × 1.2 (с запасом в 20%).

Масштабирование

О необходимости масштабирования сигнализируют следующие признаки:

  • При чтении:

    • стабильно растет значение index:stat().run_avg;
    • задержка p99.9 приближается к значению в 50 мс;
    • для задач модуля cooler растет значение метрики cooler_full_scan_elapsed.
  • При записи:

    • значение box.stat.vinyl().regulator.rate_limit становится ниже входящего потока записи. box.stat.vinyl().regulator.rate_limit — это максимальный лимит на запись на диск. При достижении этого лимита будет выполняться ограничение скорости записи на диск. Чтобы поднять этот лимит, используйте более быстрый диск или увеличьте значение vinyl.memory;
    • в записях журнала появляются ошибки too many open files. Это означает, что необходимо увеличить лимит количества открытых run-файлов;
    • появляются ошибки waited for N bytes of vinyl memory quota for too long.

При превышении пороговых объёмов данных, приведённых в разделе Влияние объёма данных и потока записи на производительность, возможны следующие варианты масштабирования:

  • Увеличить vinyl.memory — это увеличивает порог деградации. На SSD при значении vinyl.memory 128 МБ деградация началась с 27 ГБ. При поднятии vinyl.memory до значения 6 ГБ деградации не было вплоть до 7 ТБ;
  • Шардировать данные — распределить нагрузку на несколько экземпляров. Уменьшает объём данных и поток записи на один узел;
  • Увеличить write_threads, если деградация связана с недостаточной скоростью слияния.
  • Использовать более быстрый диск — скорость записи на SSD выше, что увеличивает порог деградации.

Влияние объёма данных и потока записи на производительность

При диапазонных запросах (например, select с большим лимитом) необходимо обойти все run-файлы, чтобы собрать ключи. Чем глубже дерево и чем больше среднее количество run-файлов на один диапазон ключей (index:stat().run_avg), тем больше в среднем необходимо прочитать файлов с диска и соответственно дольше будет чтение. Чем больше среднее количество run-файлов на один диапазон ключей (index:stat().run_avg),

Ниже приведены результаты тестов для vinyl.memory = 6 ГБ. Выполнялся select по 1000 записей со случайным UUID, размер кортежа ~10 КБ, page_size = 128 КБ, cache_size = 0 (кэш кортежей vinyl отключён).

HDD

run_avg

data_compacted (логический объём)

p99

p99.9

1

22.2 ГБ

3.2 мс

13.5 мс

2

74.6 ГБ

3.7 мс

15.3 мс

3

259.7 ГБ

4.4 мс

13.5 мс

4

904.2 ГБ

6.6 мс

14.3 мс

5

3.08 ТБ

6.6 мс

14.7 мс

SSD

run_avg

data_compacted (логический объём)

p99

p99.9

1

43.5 ГБ

3.7 мс

8.6 мс

2

71.7 ГБ

4.8 мс

10.0 мс

3

262.4 ГБ

4.0 мс

8.5 мс

4

901.9 ГБ

4.1 мс

9.2 мс

5

3.08 ТБ

5.3 мс

10.7 мс

7

7.82 ТБ

14.0 мс

21.9 мс

Поток записи

Поток записи в vinyl ограничен двумя факторами:

  1. Пропускная способность фоновой выгрузки на диск.
  2. Стоимость слияний при росте дерева и типе нагрузки. Когда дерево становится глубже, количество и стоимость слияний возрастают. При вставке уникальных данных слияние идёт быстрее, а при большом количестве операций над одними и теми же ключами требует больше ресурсов.

При превышении допустимого потока записи vinyl выполняет ограничение скорости записи для пишущих файберов. Текущее ограничение скорости отражает метрика box.stat.vinyl().regulator.rate_limit . Если значение этого ограничения становится ниже входящего потока, начинается деградация задержки записи.

Ниже приведены объёмы данных, при которых в ходе тестирования начиналась деградация записи данных для разных значений vinyl.memory. Поток записи в тесте составлял 64 МБ/с, выполнялась операция replace по уникальным ключам:

Носитель

vinyl_memory

Объём данных к началу деградации

HDD

128 МБ

11 ГБ

HDD

1 ГБ

128 ГБ

HDD

6 ГБ

1.64 ТБ

SSD

128 МБ

27 ГБ

SSD

6 ГБ

7.33 ТБ без заметного снижения

Операции с точечным чтением

Операции insert, update и delete перед записью выполняют точечное чтение для проверки уникальности ключа. В отличие от replace и upsert, которые пишут сразу в уровень L0, эти операции дополнительно нагружают подсистему чтения. При их интенсивном использовании может потребоваться увеличить значение параметра read_threads или уменьшить значение коэффициэнта bloom_fpr.

Мониторинг работы движка vinyl

Для своевременного обнаружения деградации производительности рекомендуется отслеживать значение приведенных ниже метрик.

Метрики состояния:

  • index:stat().run_avg — среднее количество run-файлов на диапазон. Рост этого показателя — ранний признак возможной деградации чтения;
  • index:stat().run_count — общее количество run-файлов;
  • box.stat.vinyl().disk.data — логический объём данных без сжатия;
  • box.stat.vinyl().disk.data_compacted — объём данных с учётом сжатия.

Метрики чтения:

  • index:stat().cache.get.rows — количество кортежей, обслуженных из кэша (cache hit);
  • index:stat().get.rows — общее количество кортежей, возвращённых через этот индекс. Cache miss — количество запросов, при которых кортеж не был найден в кэше, данные пришлось читать с диска. Количество таких запросов расчитывается по формуле miss= get.rows - cache.get.rows.

Метрики записи:

  • box.stat.vinyl().regulator.rate_limit — текущее ограничение скорости записи, установленное регулятором vinyl;
  • tnt_vinyl_regulator_dump_bandwidth — максимальная скорость записи на диск, которую может обеспечить vinyl (байты в секунду).

Метрики cooler (для сценария архивации):

  • cooler_on, cooler_full_scan_elapsed, cooler_inefficiency, cooler_mismatches, cooler_errors — описаны в разделе Метрики Tarantool DB.