Рекомендации по оптимизации запросов к Lance
Это руководство поможет выбрать объём кеша на каждом BE и структуру сегментов индекса для запросов к Lance. Начните с анализа нагрузки, оцените объём данных, которые должны оставаться в кеше, и проверьте расчёты при планируемом уровне параллелизма. Примеры служат отправной точкой для планирования, а не оптимальными конфигурациями, подтверждёнными тестами, или ограничениями памяти BE.
Lance Catalog — экспериментальная функция, поддерживаемая начиная с Apache Doris 4.2. Сначала настройте доступ по инструкции Lance Catalog и проверьте совместимость средства чтения. Doris читает существующие индексы Lance; для их создания и обслуживания используйте совместимые инструменты Lance.
Если скалярные фильтры сочетаются с векторным или полнотекстовым поиском, рекомендации по размещению фильтров, согласованному построению индексов и диагностике по профилю приведены в документе Рекомендации по производительности гибридного поиска Lance.
1. Выберите направление настройки
| Ваша нагрузка | С чего начать | Что проверить |
|---|---|---|
| Векторный поиск с низкой задержкой отдельного запроса | Удерживайте в кеше часто опрашиваемые векторные партиции; сравните варианты со сбалансированным числом сегментов относительно числа доступных BE, используя руководство по сегментам | Задержку P95 при сопоставимом recall; самый медленный сканер; пиковое потребление памяти |
| Векторный поиск с большим числом одновременных запросов | Предусмотрите память для совокупности горячих партиций, используемых разными векторами запросов; оставьте память и CPU для одновременного поиска, а не увеличивайте число Split каждого запроса до максимума | Пропускную способность и задержку P95 при целевом уровне параллелизма, повторные загрузки в кеш, потребление памяти на каждом BE |
| Диапазонные/точечные фильтры BTree или фильтры Bitmap/LabelList | Оцените объём горячих страниц или записей bitmap, включая структуры поиска; проверьте использование предикатов и индексов | Загрузки страниц, селективность, временные множества идентификаторов строк и чтение выходных столбцов |
| Полнотекстовый или фразовый поиск | Оцените объём словарей, метаданных документов, списков вхождений и позиций, если они включены; используйте разнообразные термы при прогреве | Запросы с распространёнными термами, потребление памяти фразовыми запросами и объединение кандидатов |
| Векторные, скалярные и полнотекстовые запросы на общих BE | Сложите их совместные рабочие наборы и протестируйте их вместе | Не вытесняют ли запросы одного семейства записи другого семейства |
| Основное время занимают выборки выходных столбцов или сканирование | Изучите чтение файлов данных и отдельный дисковый кеш данных | Объём полезных данных по отношению к избыточному чтению; что занимает больше времени — декодирование или выборка |
Скалярный фильтр также может входить в Prefilter векторного запроса, поэтому одному запросу могут понадобиться данные как скалярного, так и векторного индекса. Выберите все подходящие строки таблицы: эти сценарии не исключают друг друга.
2. Соберите исходные данные и задайте бюджет памяти
Соберите следующие данные для каждого BE или для репрезентативного BE при реалистичном размещении нагрузки:
- Нагрузка: целевые QPS/параллелизм, целевая задержка, требуемый recall,
top_k, фильтры, фразовые запросы и выходные столбцы. - Ресурсы: BE, доступные для выполнения запросов, вычислительная мощность CPU и память, остающаяся после учёта других нагрузок Doris и потребностей процесса/ОС.
- Индексы: типы, версии, число физических сегментов, покрытие, размерность/кодирование векторов, число партиций IVF, а также размер скалярных значений или длина строк.
- Повторное использование: уникальные партиции, страницы, термы или записи bitmap, к которым обращаются запросы за репрезентативный интервал. Учитывайте рабочий набор всех запросов, а не строки, возвращаемые одним запросом.
- Пики запросов: память для активных загрузок партиций, множеств идентификаторов строк Prefilter/результата, куч кандидатов, декодирования, уточнения результатов и выборок второй фазы при планируемом уровне параллелизма.
Используйте формулы для индексов, чтобы оценить объём полезных данных, а затем проверьте, укладывается ли суммарный бюджет в доступную память:
available_Lance_cache_budget_on_BE
= BE memory budget
- other BE memory needs
- peak concurrent query working memory
- safety headroom
index_cache_target + metadata_cache_target
<= available_Lance_cache_budget_on_BE
Это проверка для планирования, а не принудительное резервирование памяти. Ёмкость кешей не ограничивает память запросов или RSS процесса. Не умножайте повторно используемый кешируемый рабочий набор на число одновременных запросов; при этом резервируйте дополнительную рабочую память для этих запросов и учитывайте увеличение совокупного объёма кешируемых данных, если одновременные запросы обращаются к разным данным.
Полные определения параметров и их значения по умолчанию приведены в разделе Конфигурация BE. Задайте параметры BE lance_* в файле be.conf каждого BE и перезапустите этот BE. Это не свойства Catalog и не переменные сеанса SQL. Число сегментов индекса и параметры построения IVF задаются средствами построения индексов Lance, а не этими параметрами кеша.
3. Оцените рабочий набор индексов
Подберите объём кеша индексов для тех партиций индекса, которые каждому BE нужно использовать повторно. Полный объём индекса в хранилище, кешируемый рабочий набор и пиковая память запросов — разные величины. Приведённые ниже оценки охватывают шесть типов векторных индексов, поддерживаемых Doris; это оценки для планирования, а не точный учёт памяти кеша или ограничения памяти.
Объём векторных данных по типам индексов
Используются следующие обозначения. Все формулы дают результат в байтах; 1 GiB = 1073741824 bytes.
| Обозначение | Значение |
|---|---|
N | Число индексированных векторов в оцениваемых партициях. Для обычного столбца с одним вектором на строку это число индексированных строк. Для многовекторных столбцов считайте индексированные подвекторы и отдельно предусмотрите память для отображений на строки и уточнения результатов. |
D | Размерность вектора. |
w | Число байт на хранимый неквантованный элемент вектора: 2 для Float16, 4 для Float32 или 8 для Float64. Для бинарных векторов uint8 используйте фактическую длину в байтах в качестве D и w=1. |
m, b | Число подвекторов PQ и число бит в коде каждого подвектора. Это настройки построения индекса, а не параметры запроса. |
G | Память, занимаемая загруженным графом HNSW, включая все уровни и вспомогательные структуры. |
Складывайте все применимые компоненты, а не только коды векторов. В первой таблице выделены данные, приходящиеся на каждый вектор; в следующих таблицах перечислены общие структуры и компоненты графа HNSW. Все формулы оценивают загруженные буферы. Строки с пометкой Измерить также требуют бюджета памяти: это не бесплатные компоненты. Каждое совместно используемое выделение памяти учитывайте один раз.
| Тип индекса | Значения / коды векторов | Идентификаторы строк UInt64 | Граф HNSW | Дополнительный обязательный бюджет |
|---|---|---|---|---|
IVF_FLAT | N * D * w | 8 * N | Нет | Структуры IVF + общие накладные расходы ниже |
IVF_SQ | N * D для SQ8 | 8 * N | Нет | Структуры IVF + метаданные SQ + общие накладные расходы |
IVF_PQ | N * ceil(m * b / 8) | 8 * N | Нет | Структуры IVF + кодовая книга PQ + общие накладные расходы |
IVF_HNSW_FLAT | N * D * w | 8 * N | Добавить G | Структуры IVF + общие накладные расходы |
IVF_HNSW_SQ | N * D для SQ8 | 8 * N | Добавить G | Структуры IVF + метаданные SQ + общие накладные расходы |
IVF_HNSW_PQ | N * ceil(m * b / 8) | 8 * N | Добавить G | Структуры IVF + кодовая книга PQ + общие накладные расходы |
Для PQ при b=8 код занимает m байт на вектор; при b=4 он занимает m/2 байт для поддерживаемых чётных значений m. Например, 1 миллион векторов с m=96 и b=8 содержит 96000000 байт кодов и 8000000 байт идентификаторов строк: суммарно 104000000 байт (около 99.2 MiB). При оценке сильно сжатых индексов нельзя опускать идентификаторы строк.
| Дополнительный компонент | Где применяется | Загруженные данные / как оценить бюджет |
|---|---|---|
| Центроиды IVF | Все шесть типов | FP32: num_partitions * D * 4 на каждый удерживаемый набор центроидов; для других форматов используйте фактический размер элемента |
| Каталог партиций IVF / метаданные подындексов | Все шесть типов | Измерить удерживаемые смещения партиций, длины и структуры средства чтения; они учитываются отдельно от значений векторов и центроидов |
| Кодовая книга PQ | IVF_PQ, IVF_HNSW_PQ | Стандартная кодовая книга FP32: 2^b * D * 4 на каждую удерживаемую копию |
| Метаданные квантователя SQ | IVF_SQ, IVF_HNSW_SQ | Измерить удерживаемые границы/параметры квантователя; не предполагайте, что формат одинаков во всех версиях |
| Нормы, структуры покрытия строк, дополнительные отображения | Если средство чтения их удерживает | Измерить фактические массивы/множества; отображения строк для многовекторных данных требуют отдельного бюджета |
| Объекты индекса, буферы валидности Arrow, резервная ёмкость и копии | Все шесть типов | Измерить выделения памяти сверх логического объёма данных; совместно используемые буферы учитывайте один раз |
Независимые сегменты индекса могут удерживать отдельные наборы центроидов и кодовые книги. Не умножайте совместно используемые выделения памяти на число всех партиций и не предполагайте, что все копии используются совместно. Дополнительные структуры зависят от встроенной версии Lance.
Компоненты графа HNSW
Граф учитывается дополнительно к кодам векторов и идентификаторам строк. Для графа Lance на основе Arrow с идентификаторами узлов/соседей UInt32 и необязательно удерживаемыми расстояниями рёбер Float32 обозначим через E число хранимых направленных записей о соседях на всех загруженных уровнях, через V — число вхождений узлов на этих уровнях, а через A — число независимо удерживаемых пакетов графа. Узел, присутствующий на нескольких уровнях, учитывается в V несколько раз.
| Компонент графа | Основной объём данных в байтах | Правило подсчёта |
|---|---|---|
| Идентификаторы соседей | 4 * E | UInt32 на каждую направленную запись о соседе; учитывайте все уровни |
| Хранимые расстояния рёбер | 4 * E | Float32 на каждую запись о соседе; ноль, если средство чтения пропускает столбец расстояний |
| Идентификаторы узлов графа | 4 * V | UInt32 на каждое вхождение узла; они отличаются от идентификаторов строк набора данных UInt64 |
| Смещения списков соседей | 4 * (V + A) | Смещения списков Int32, включая одно конечное смещение на пакет |
| Смещения списков расстояний | 4 * (V + A) | Второй массив смещений списков Int32; ноль, если средство чтения пропускает столбец расстояний |
| Промежуточная сумма с расстояниями | 8 * E + 12 * V + 8 * A | Приблизительно 8 * E + 12 * V для больших пакетов; это не полное значение G |
| Промежуточная сумма без расстояний | 4 * E + 8 * V + 4 * A | Используйте, если удерживаются только идентификаторы узлов и списки соседей; выберите одну промежуточную сумму, а не обе |
| Смещения уровней, точки входа и структуры поиска узлов | Измерить | Включите удерживаемые метаданные и выделения памяти для поиска по каждому загруженному графу |
| Буферы валидности, объекты графа и резервная ёмкость выделений памяти | Измерить | Добавьте фактические выделения памяти сверх перечисленных буферов, не учитывая совместные буферы дважды |
Полный бюджет графа G | Основная промежуточная сумма + измеренные дополнительные расходы графа | Добавьте его к объёму векторов/идентификаторов строк и других структур индекса |
Эта оценка относится к конкретному формату хранения, а не гарантирует фиксированное число байт на узел. Параметр m в HNSW управляет связностью графа и не связан с m в PQ. Не подставляйте N * m * 4 вместо G: важны фактическая степень узлов, верхние уровни, удерживаемые расстояния и версия средства чтения. Уточните оценку, загрузив репрезентативные партиции и измерив удерживаемую ими память.
Например, предположим, что кешируемые партиции IVF_HNSW_PQ содержат 1 миллион векторов, PQ m=96, b=8, E=32000000, V=1100000 и A=1. Допустим, это средство чтения удерживает столбец расстояний. Эти характеристики графа приведены для примера, а не выведены из параметра m в HNSW:
| Компонент | Объём данных в примере |
|---|---|
| Коды PQ | 96000000 байт |
| Идентификаторы строк набора данных | 8000000 байт |
| Основные буферы графа | 8 * 32000000 + 12 * 1100000 + 8 = 269200008 байт, около 256.7 MiB |
| Коды + идентификаторы строк + основные буферы графа | 373200008 байт, около 355.9 MiB |
| Альтернатива: основные буферы графа при пропуске расстояний | 4 * 32000000 + 8 * 1100000 + 4 = 136800004 байт, около 130.5 MiB |
| Альтернативный итог: коды + идентификаторы строк + буферы графа без расстояний | 240800004 байт, около 229.6 MiB |
| Что ещё нужно добавить в обоих случаях | Дополнительные расходы графа, центроиды/каталог IVF, кодовую книгу PQ и общие накладные расходы из таблиц выше |
Выберите пример, соответствующий столбцам, которые удерживает средство чтения. Ни одна из промежуточных сумм не является полной рекомендацией по ёмкости кеша.
В руководстве по производительности Lance от разработчиков Lance описаны оценки объёма векторных данных и памяти для построения индекса. В описании формата векторного индекса Lance объясняется разделение на партиции IVF, подындексы и квантованное хранилище. Актуальные версии этих документов могут описывать возможности более новых версий Lance, чем встроенная в Doris; для проверки совместимости используйте матрицу поддержки Lance Catalog.
Объём данных скалярных и полнотекстовых индексов
Объём скалярных индексов также можно оценить, но исходные данные зависят от семейства индекса. Считайте записи, фактически удерживаемые на одном BE, а не только строки, возвращаемые запросом. Следующие методы описывают форматы хранения Lance; они не расширяют гарантии Doris по проталкиванию предикатов или совместимости индексов. Убедитесь, что встроенная версия Lance поддерживает индекс и что запрос его использует.
BTree. Загруженная листовая страница содержит значения и идентификаторы строк UInt64. Пусть R — число записей в кешируемых листовых страницах, H — число этих страниц, w — размер значения фиксированной длины, а L — средняя длина строки в байтах на этих страницах:
| Компонент / представление листовой страницы | Буфер значений | Смещения | Идентификаторы строк UInt64 |
|---|---|---|---|
| Int32 / Float32 | 4 * R | Нет | 8 * R |
| Int64 / Float64 / 64-битная временная метка | 8 * R | Нет | 8 * R |
| Другие значения фиксированной длины | w * R; для Boolean с битовой упаковкой используйте фактические буферы bitmap | Нет | 8 * R |
| Arrow Utf8 / Binary | R * L | 4 * (R + H) для смещений Int32 | 8 * R |
| Arrow LargeUtf8 / LargeBinary | R * L | 8 * (R + H) для смещений Int64 | 8 * R |
Промежуточный объём листовых страниц равен сумме трёх столбцов с объёмами данных. Добавьте следующие компоненты для каждого открытого сегмента; P ниже обозначает число всех листовых страниц в этом сегменте, а не только кешируемых:
| Дополнительный компонент BTree | Основной объём данных / как оценить бюджет |
|---|---|
| Минимальные значения в структуре поиска | P * w для значений фиксированной длины; фактические буферы значений/смещений для значений переменной длины |
| Максимальные значения в структуре поиска | P * w для значений фиксированной длины; фактические буферы значений/смещений для значений переменной длины |
| Число NULL в структуре поиска | 4 * P для формата UInt32 |
| Номера страниц в структуре поиска | 4 * P для формата UInt32 |
| Промежуточный объём буферов поиска для значений фиксированной длины | P * (2 * w + 8); не включает структуры ниже |
| Списки страниц с NULL и объекты поиска | Измерить удерживаемые списки, объекты и копии, зависящие от реализации |
| Буферы валидности листовых страниц/структур поиска, объекты Arrow и резервная ёмкость | Измерить выделения памяти сверх логического объёма значений/смещений/идентификаторов строк |
Формулы для строк предполагают указанные форматы Arrow; для словарных представлений или представлений view нужны их фактические буферы. Сжатие на диске не определяет размер декодированной страницы. Размер страницы BTree по умолчанию составляет 4096 записей, а не байт; используйте фактический размер страницы индекса. Структура поиска относится к рабочему набору индекса; то, что она описывает страницы, не означает, что её нужно относить к бюджету кеша метаданных BE.
Например, 100 миллионов значений Int64 содержат 100000000 * 16 = 1600000000 байт (около 1.49 GiB) данных значений и идентификаторов строк листовых страниц. Если кешируемые страницы охватывают 10 миллионов записей, этот объём составляет около 152.6 MiB. При 4096 записях на страницу полный индекс содержит около 24415 страниц; объём минимальных/максимальных значений, счётчиков и номеров страниц в пакете структуры поиска составляет около 572 KiB без дополнительных структур. Возврат десяти подходящих строк не означает кеширование только десяти записей: средство чтения загружает страницы.
Bitmap, LabelList и NGram. Сложите применимые строки компонентов ниже. Битовые карты принадлежности не обязательно занимают восемь байт на каждую подходящую строку. LabelList учитывает принадлежность для каждой уникальной метки; одна строка может присутствовать в битовых картах нескольких меток. NGram учитывает уникальные пары «грамма/строка»; повторные вхождения в одной строке не добавляют новых пар. Этот индекс NGram отличается от индекса FTS с токенизатором n-gram.
| Семейство индекса | Компонент | Загруженные данные / как оценить бюджет |
|---|---|---|
| Bitmap | Структура поиска ключей | Измерить удерживаемые значения ключей, ссылки на записи и объекты поиска |
| LabelList | Структура поиска меток / обёртка | Измерить уникальные ключи меток, ссылки на записи и объекты обёртки |
| NGram | Структура поиска грамм | Измерить удерживаемые граммы, ссылки на записи и объекты поиска |
| Все три | Битовая карта принадлежности: контейнеры-массивы | Около 2 * c на обычный контейнер-массив Roaring с c записями UInt16 в диапазоне из 65536 значений |
| Все три | Битовая карта принадлежности: плотные контейнеры | 8192 байт на обычный плотный контейнер, охватывающий диапазон из 65536 значений |
| Все три | Битовая карта принадлежности: представления сериями / целыми фрагментами | Измерить фактическое представление; используйте представление, выбранное для каждого контейнера, а не складывайте все варианты |
| Все три | Каталоги битовых карт / ёмкость | Измерить каталоги фрагментов/старших битов, объекты контейнеров и резервную ёмкость |
| Если присутствует | Учёт NULL / списков NULL | Измерить удерживаемые структуры принадлежности NULL отдельно |
| Итог для каждого семейства | Структура поиска + загруженные представления принадлежности + каталоги + учёт NULL | Совместно используемые выделения памяти учитывайте один раз; размер сериализованной битовой карты не гарантирует такой же объём резидентной памяти |
Например, 256 загруженных плотных контейнеров содержат 2 MiB данных битовых наборов без учёта ключей, каталогов, учёта NULL и накладных расходов объектов.
Другие скалярные и полнотекстовые индексы. Каждая строка обозначает отдельный компонент; строки с промежуточными суммами обобщают предыдущие компоненты и не должны прибавляться повторно. Учитывайте только данные, удерживаемые на BE. Измерить означает, что нужны фактические выделения памяти средства чтения, а не что компонент можно опустить.
| Семейство индекса | Компонент | Загруженные данные / как оценить бюджет |
|---|---|---|
| FTS / Inverted | Словарь термов | Измерить загруженные термы, структуру словаря и выделения памяти для поиска |
| FTS / Inverted | Метаданные документов | Измерить удерживаемые отображения документов, длины и другие метаданные средства чтения |
| FTS / Inverted | Идентификаторы строк в несжатых списках вхождений | 8 * T для T записей терм/документ с UInt64 |
| FTS / Inverted | Частоты в несжатых списках вхождений | 4 * T для частот Float32; идентификаторы + частоты суммарно 12 * T |
| FTS / Inverted | Позиции, если включены | 4 * O для O хранимых вхождений Int32 |
| FTS / Inverted | Смещения позиций | Фактический объём удерживаемых массивов смещений в байтах; отдельно от 4 * O |
| FTS / Inverted | Сжатый формат списков вхождений | Используйте фактические удерживаемые сжатые блоки вместо соответствующих формул для несжатого формата |
| FTS / Inverted | Данные оценки релевантности / метаданные блоков и накладные расходы выделения памяти | Измерить удерживаемые дескрипторы блоков, данные оценки релевантности, объекты и резервную ёмкость |
| ZoneMap | Минимальные значения | Z * w логических байт для Z загруженных зон и значений фиксированной длины размером w |
| ZoneMap | Максимальные значения | Z * w логических байт; минимум + максимум суммарно 2 * Z * w |
| ZoneMap | Счётчики и границы зон | Измерить удерживаемые счётчики и границы; считайте зоны для каждого Fragment, включая неполные зоны |
| ZoneMap | Учёт NULL и накладные расходы упакованных значений/объектов | Измерить необязательные множества строк NULL и фактические выделения памяти для скалярных значений; они могут значительно превышать логический объём минимумов/максимумов |
| BloomFilter | Битовые массивы фильтров | Сумма фактически выделенных байтов фильтров; Z * F для Z фильтров одинакового размера по F байт каждый |
| BloomFilter | Дескрипторы зон / объекты фильтров | Измерить удерживаемые дескрипторы, параметры фильтров и накладные расходы объектов |
| BloomFilter | Учёт NULL | Измерить необязательные множества строк NULL |
| RTree | Координаты ограничивающих прямоугольников | 32 * Q для четырёх координат Float64 на каждую запись 2D; считайте Q записей на всех кешируемых уровнях дерева |
| RTree | Идентификаторы строк / дочерних страниц | 8 * Q для идентификаторов UInt64; координаты + идентификаторы суммарно 40 * Q |
| RTree | Метаданные дерева и смещения страниц | Измерить удерживаемые дескрипторы дерева и буферы смещений страниц |
| RTree | Учёт NULL, Arrow/объекты и резервная ёмкость | Измерить фактические дополнительные выделения памяти |
| Обёртки JSON-path | Базовый индекс | Используйте полный бюджет компонентов выбранного семейства скалярного индекса |
| Обёртки JSON-path | Структуры путей и обёрток | Измерить удерживаемые пути и выделения памяти обёрток в дополнение к базовому индексу |
Для фильтров Блума нужны фактические выделения памяти, округлённые до размера блока при построении; число строк и вероятность ложноположительного срабатывания сами по себе не описывают все форматы. Для незнакомых или экспериментальных форматов измеряйте объём загруженных данных в памяти, а не применяйте формулу другого семейства. Даже небольшое число подходящих строк может требовать значительного объёма словаря, дерева или метаданных документов.
Учитывайте рабочую память запросов отдельно от всех таблиц кеша: временные пересечения/объединения, множества идентификаторов строк результата, повторные проверки приближённых фильтров, чтение исходной геометрии для точных проверок RTree и чтение выходных столбцов требуют собственного бюджета.
Описание базовых структур приведено в документации разработчиков по формату BTree, формату Bitmap и формату FTS. Формулы выше оценивают объём загруженных данных, а не размер сжатого файла или полный RSS запроса.
От размера индекса к рабочему набору на каждом BE
Для сегмента s обозначим через N_s число индексированных векторов, через P_s — число его партиций IVF, а через H_s — число уникальных партиций, которые нужно удерживать на конкретном BE. При достаточно сбалансированных партициях:
cached_vectors_on_BE ≈ sum over segments (N_s * H_s / P_s)
index_cache_target ≈ cached vector/row-ID payload
+ cached HNSW graphs
+ centroids, codebooks and other index allocations
+ measured headroom
Если партиции распределены неравномерно, используйте фактическое число строк в них. H_s описывает рабочий набор нескольких запросов, а не только nprobes одного запроса. Разнообразные запросы со временем могут обратиться ко всем партициям; повторные или одновременные запросы могут совместно использовать одну и ту же кешируемую партицию. Считайте уникальные записи (dataset, index segment, partition) и включайте все таблицы, индексы и версии, к которым обращается этот BE. Наличие данных в кеше одного BE не прогревает другой BE, а планировщик не гарантирует, что каждый сегмент индекса останется на одном BE. Не делите полный размер индекса на число BE, если наблюдаемые размещение и повторное использование этого не оправдывают.
Память во время выполнения запросов учитывается дополнительно: текущие загрузки партиций, объекты, которые запросы продолжают удерживать после вытеснения из кеша, состояние Prefilter, таблицы расстояний, кучи кандидатов, декодированные пакеты, уточнение результатов и выборки второй фазы. Параметры top_k, ef, refine_factor, параллелизм сканирования и nprobes влияют на эту память или на рабочий набор, к которому обращаются запросы; они не меняют число хранимых байт на вектор. Настроенная ёмкость кеша не является жёстким ограничением RSS Lance или BE. Увеличение кеша также не устраняет вычисление расстояний и фильтрацию для каждого запроса.
Пример конфигурации и настройка
Предположим, что индекс IVF_PQ содержит 100 миллионов векторов FP32 размерности 768, имеет m=96, b=8 и 4096 сбалансированных партиций в одном сегменте индекса. Репрезентативная нагрузка на одном BE многократно обращается к 1024 уникальным партициям:
Full code/row-ID payload = 100000000 * (96 + 8) = 10400000000 bytes ≈ 9.69 GiB
Cached payload = 10400000000 * 1024 / 4096 = 2600000000 bytes ≈ 2.42 GiB
IVF centroids = 4096 * 768 * 4 = 12582912 bytes = 12 MiB
PQ codebook = 256 * 768 * 4 = 786432 bytes = 0.75 MiB
Если это единственный значительный рабочий набор индекса на BE и бюджет памяти позволяет, 4 GiB — разумный начальный объём кеша индексов для этого примера: сверх рассчитанного объёма данных остаётся место для дополнительных выделений памяти индекса. Это начальное значение для проверки, а не универсальный коэффициент накладных расходов. Если же нагрузке нужен весь индекс в памяти, 4 GiB недостаточно, и даже значение по умолчанию 10 GiB близко к объёму данных 9.69 GiB ещё до учёта других выделений памяти.
Для BE, на котором 4 GiB кеша индексов и 1 GiB кеша метаданных помещаются вместе с пиковой памятью запросов, другими нагрузками Doris и запасом для процесса/ОС, задайте:
lance_index_cache_size_bytes = 4294967296
lance_metadata_cache_size_bytes = 1073741824
Перезапустите этот BE, чтобы настройки вступили в силу. Применяйте бюджет отдельно к каждому BE; это параметры конфигурации BE, а не свойства Catalog или переменные сеанса SQL.
- Выполните прогрев с репрезентативными запросами и уровнем параллелизма, включая разные векторы запросов, фильтры и таблицы. Повторение только одного вектора занижает рабочий набор разнообразной нагрузки.
- Сравните
doris_be_lance_session_index_cache_usage_bytesсdoris_be_lance_session_index_cache_capacity_bytesи наблюдайте приростhits_total/misses_totalза интервалы. Для каждого завершённого запроса также изучитеLanceIndexCacheHits,LanceIndexCacheMisses,LanceIndexObjectLoadsиLanceIndexComponentsLoadedв разделеLanceIndex. Обращения к кешу запросов и загрузки компонентов имеют разные области учёта; они не взаимозаменяемы с изменениями метрик всего BE. Постоянные промахи при использовании кеша, близком к его ёмкости, могут указывать на частое вытеснение и повторную загрузку, но промахи также могут быть вызваны новыми партициями, версиями индексов, другими BE и потоковыми путями выполнения, которые не удерживают данные. Увеличивайте ёмкость только тогда, когда это оправдано повторным использованием и доступной памятью. - Начните подбор объёма кеша метаданных со значения по умолчанию 1 GiB, если позволяет бюджет памяти BE. Корректируйте его по
usage_bytesи метрикам попаданий/промахов. Потребность определяется числом файлов/Fragment, метаданными схемы/страниц, сведениями об удалениях и отображениями идентификаторов строк; она не составляет фиксированный процент от размера векторного индекса. Кеш метаданных BE отделён от кеша доступа к таблицам FE. - Прежде чем увеличивать любой из кешей, проверьте память процесса BE и пики запросов при планируемом уровне параллелизма. Высокая доля попаданий в кеш при большой задержке может указывать на затраты на оценку релевантности, Prefilter, декодирование или выборку; дополнительный кеш не обязательно будет полезен.
- Подбирайте
lance_data_cache_disk_capacity_bytesнезависимо для повторного чтения файлов данных, например при уточнении результатов и выборке выходных столбцов. Его увеличение не расширяет кеш индексов и не компенсирует промахи в нём: файлы индексов обходят этот дисковый кеш данных. Сначала оставьте дляlance_data_cache_read_block_size_bytesзначение по умолчанию 1 MiB, а затем меняйте его только после измерения полезного и избыточного объёма чтения.
4. Рассчитайте бюджет общих кешей
На каждом BE Lance использует общий сеанс и единую ёмкость кеша индексов. Векторные партиции, страницы BTree, записи Bitmap/LabelList и данные полнотекстовых индексов могут конкурировать за это пространство, в том числе индексы разных таблиц и Catalog, к которым обращается один BE. Ключи кеша различают их, но не резервируют ёмкость. Настройки кеша Lance на BE не предоставляют квот или закрепления записей для отдельных таблиц или типов индексов.
Место в кеше занимают только загруженные данные; само создание нескольких индексов не загружает их все. Когда запросы начинают их использовать, загрузка одного индекса может вытеснять записи другого. Например, широкий диапазонный скан BTree или разнообразные запросы FTS могут заменить уже прогретые векторные партиции. Следующий векторный запрос затем загрузит их повторно. Изменение версии индекса также может добавлять новые записи, пока старые остаются в памяти. Вытеснение не освобождает объект немедленно, если выполняющийся запрос всё ещё на него ссылается.
Планируйте смешанный рабочий набор, не учитывая совместно используемые выделения памяти дважды:
index_cache_target_on_BE ≈ vector working set
+ BTree working set
+ Bitmap / LabelList / NGram working sets
+ FTS and other index working sets
+ measured headroom
Например, предположим, что измерения и оценки объёма данных дают 2.5 GiB для векторов, 0.25 GiB для BTree, 0.5 GiB для Bitmap и 0.75 GiB для FTS, включая структуры этих индексов. Их совокупный рабочий набор составляет 4 GiB. Начальный кеш индексов объёмом 5 GiB оставляет 1 GiB на рост и погрешность оценки; это не гарантирует, что любая нагрузка в него поместится. Если на BE также хватает места для 1 GiB кеша метаданных и пиковой памяти запросов, других нагрузок Doris и запаса для процесса/ОС:
lance_index_cache_size_bytes = 5368709120
lance_metadata_cache_size_bytes = 1073741824
Кеши индексов и метаданных имеют отдельные ёмкости: записи метаданных напрямую не вытесняют записи индексов, а увеличение одного кеша не расширяет другой. При этом оба потребляют память одного процесса BE. Дисковый кеш данных — третий бюджет; он не кеширует файлы индексов. Раздельные Catalog на одном BE не обеспечивают изоляцию кеша индексов; нагрузкам, которым нужна изоляция, требуются отдельные ресурсы BE и поддерживаемая в развёртывании маршрутизация.
Для диагностики конкуренции сначала прогрейте одно семейство запросов и запишите задержку и изменения метрик кеша. Запустите второе семейство, затем повторите первое. Возобновившиеся промахи или повторные загрузки при использовании кеша, близком к его ёмкости, указывают на давление вытеснения, если версии индексов и размещение на BE оставались сопоставимыми. Метрики попаданий/промахов сеанса агрегируют нагрузки, поэтому сопоставляйте их с LanceIndexCacheHits/LanceIndexCacheMisses, LanceIndexComponentsLoaded и LanceIOReadBytes на уровне запросов. Загрузки компонентов не равны числу промахов, а число прочитанных байт при выполнении не является измерением удалённого трафика. Предварительный прогрев всех индексов сам по себе может вытеснить рабочий набор, который вы хотели удержать. Предпочитайте прогрев репрезентативной смешанной нагрузкой и увеличивайте ёмкость только тогда, когда это оправдано повторным использованием и доступной памятью.
5. Выберите размер сегментов индекса
Index Segment, Fragment данных, партиция IVF и листовая страница BTree — разные единицы. В текущем пути векторного поиска Doris каждый выбранный физический Index Segment становится одним Split сканирования; непокрытые Fragment добавляют Split для Flat Search. FTS также использует физические сегменты индекса. Партиции IVF одного векторного сегмента не становятся отдельными Split Doris, распределяемыми между BE. При этом Lance может выполнять работу параллельно внутри себя. Для обычных скалярных сканирований группировка по сегментам зависит от выбранного индекса и плана; не предполагайте, что все скалярные индексы следуют политике разделения векторного поиска на Split.
Пусть B — число BE, фактически доступных для этого запроса, а S — число выбранных векторных сегментов. Если S меньше B, индексированных Split сканирования слишком мало, чтобы задействовать все эти BE для данного запроса. Увеличение S создаёт больше возможностей для планирования, но не гарантирует одновременного выполнения всех Split. Также важны ограничения сканеров, внутренний параллелизм Lance, CPU, I/O, наличие данных в кеше и одновременные запросы. Одно лишь увеличение числа экземпляров pipeline Doris не делит сегмент индекса на части.
Для большого набора данных сравните B, 2 * B и 4 * B достаточно сбалансированных векторных сегментов в качестве начального эксперимента, а не официального оптимума или требования для небольших наборов данных. Пример для набора из 100 миллионов векторов и восьми доступных BE:
| Сегменты | Среднее число векторов на сегмент | Что оценить |
|---|---|---|
| 8 | 12.5 миллиона | Достаточно Split, чтобы задействовать BE, при меньшем числе операций на уровне сегментов |
| 16 | 6.25 миллиона | Больше гибкости планирования и меньше зависимость от одного большого сегмента |
| 32 | 3.125 миллиона | Больше задач, но потенциально выше накладные расходы на поиск, загрузку и объединение |
Балансируйте ожидаемую поисковую работу и объём загружаемых данных индекса, а не только число Fragment. Одинаковое число строк — полезная отправная точка для векторов одинаковой размерности и кодирования; размер графа HNSW, перекос IVF, селективность скалярных фильтров и длина документов могут изменить баланс. Универсального порога числа строк на сегмент или размера файла нет. Для высокой пропускной способности может быть выгоднее меньше задач на запрос, чтобы сохранить ресурсы для одновременных запросов; для снижения задержки отдельного запроса может быть полезно больше Split.
Большее число сегментов также означает больше операций подготовки для каждого сегмента, объектов метаданных/квантователей и локально формируемых кандидатов. Каждый Split векторного поиска/FTS удерживает до top_k + offset кандидатов; Doris не делит эту границу на S. Поэтому верхняя граница числа кандидатов на стороне сканирования равна S * (top_k + offset) с учётом доступного числа совпадений. При top_k=1000, без смещения и с 32 Split это до 32000 кандидатов до сокращения их числа в Doris. Локальный TopN может уменьшить сетевой трафик, поэтому эта граница не гарантирует число строк, передаваемых по сети. Обработка кандидатов особенно важна при больших top_k.
Настраивайте число сегментов вместе с IVF num_partitions каждого сегмента и nprobes запроса. Для сбалансированных партиций грубая оценка числа проверяемых векторов — sum(N_s * nprobes_s / P_s), не более N_s для каждого сегмента. Это не формула числа сравнений HNSW. Перестроение сегментов или изменение числа партиций может изменить recall даже при том же nprobes; сравнивайте задержку при сопоставимом recall. Параметр HNSW ef и настройки уточнения результатов также влияют на сравнение.
Создавайте и обслуживайте сегменты инструментами Lance, затем фиксируйте их как нужный логический индекс. Несколько отдельно именованных индексов на всю таблицу не заменяют один индекс с несколькими физическими сегментами. Предпочитайте сбалансированные группы целых Fragment, избегайте накопления множества мелких сегментов из-за небольших добавлений и используйте API обслуживания, поддерживаемые встроенной версией Lance. Проверяйте итоговое число физических сегментов и непокрытых Fragment в EXPLAIN/Profile Doris; не выводите его из числа строк в SHOW INDEX. Объединение всех сегментов в один может снизить параллелизм сканирования Doris. Принципы построения и фиксации сегментов описаны в документе Распределённое индексирование Lance.
Для точечных поисков BTree или селективных запросов Bitmap разбиение на множество сегментов может добавить поисковые обращения и открытия, почти не сокращая работу на каждом сегменте. Для FTS добавляются поиск по словарям/спискам вхождений каждого сегмента и объединение кандидатов. Оценивайте эти нагрузки отдельно, а не применяйте число сегментов для векторного поиска ко всем семействам индексов. Разбиение также не устраняет ненужное построение Prefilter для каждой строки; отдельно проверяйте поведение фильтрации в средстве чтения.
6. Проверьте смешанную нагрузку
- Проверьте выбор индекса, физические Split и покрытие индекса. Включите резервные сканирования и выборки второй фазы в анализ задержки.
- Оцените объём удерживаемых данных каждого индекса и сложите совместные рабочие наборы. Резервируйте память запросов отдельно, включая параллельные загрузки, множества идентификаторов строк, кучи кандидатов и декодирование.
- Сравните смешанные нагрузки с холодным и прогретым кешем при реалистичном уровне параллелизма. Отслеживайте задержку P95, пропускную способность, recall, где применимо, пиковую память каждого BE, использование кеша и изменения попаданий/промахов, I/O загрузки индексов и неравномерность времени сканирования.
- Меняйте по одному параметру: ёмкость кеша, число сегментов или параметры поиска. Если перестроение сегментов меняет модель, повторно проверьте recall. Прекратите увеличивать число Split или объём кеша, когда пропускная способность, задержка или потребление памяти перестанут улучшаться.
- Повторяйте проверку после значительных добавлений данных, перестроения индексов, изменения числа BE или состава запросов. Конфигурация, подобранная для одного вектора или таблицы, не является стабильным бюджетом для всех нагрузок.
7. Проведите диагностику перед увеличением ресурсов
| Наблюдение | Что проверить сначала | Какое действие оценить |
|---|---|---|
| Повторные промахи индексов при использовании кеша, близком к его ёмкости | Изменения индексов/версий, размещение на BE и совокупный рабочий набор | Увеличьте lance_index_cache_size_bytes, если позволяет память, сократите ненужный прогрев или изолируйте нагрузки на отдельных ресурсах BE |
| Векторные запросы замедляются после скалярных запросов или FTS | Повторите проверку взаимного вытеснения нагрузок из раздела бюджетирования общего кеша | Рассчитайте совместный бюджет для обоих семейств запросов; отдельные Catalog на одном BE не изолируют кеш |
| Большой индексированный набор данных сканируют лишь немногие BE | Число Split физических сегментов, доступные BE и планирование сканеров | Сравните варианты с большим числом сбалансированных сегментов; одно лишь увеличение числа экземпляров pipeline не может разделить сегмент |
| Большее число сегментов увеличивает нагрузку на CPU, потребление памяти или время объединения | Число кандидатов на Split, top_k + offset и параллелизм запросов | Уменьшите число сегментов или не запрашивайте больше кандидатов, чем нужно приложению; повторно проверьте recall при изменении настроек поиска |
| Доля попаданий в кеш высока, но задержка остаётся большой | Оценку релевантности, работу Prefilter, декодирование и время выборки второй фазы | Оптимизируйте этап, занимающий основное время; увеличение кеша индексов не устраняет вычисления или выборки |
| Метаданные продолжают загружаться повторно | Использование кеша метаданных и изменения попаданий/промахов; число Fragment/файлов и частоту смены версий | Настройте lance_metadata_cache_size_bytes независимо, если это позволяют повторное использование и память |
| Память процесса велика, хотя использование кеша ниже его ёмкости | Выделения памяти одновременных запросов, активные загрузки, удерживаемые объекты и другие нагрузки BE | Уменьшите параллелизм или бюджеты кешей на основе измеренных пиков; ёмкость кеша не ограничивает память процесса |
| I/O файлов данных остаётся высоким | Счётчики дискового кеша, повторное использование, размер блока чтения и выходные столбцы запросов | Скорректируйте бюджет кеша данных или размер блоков чтения; файлы индексов не используют этот дисковый кеш данных |
Используйте справочник по Query Profile, чтобы разделить работу FE с метаданными и планированием, работу BE с индексами и сканированием и выборку по идентификаторам строк во второй фазе. Счётчики кеша и метрики всего BE описаны в разделе Проверка эффективности кеша. Одна лишь совокупная доля попаданий не показывает, какой индекс был вытеснен или какой этап определяет задержку. Меняйте по одной настройке и для каждого сравнения фиксируйте нагрузку, версию индекса, размещение на BE, задержку, пропускную способность, recall и пиковое потребление памяти.