Перейти к содержимому
Последнее обновление

Рекомендации по оптимизации запросов к 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_FLATN * D * w8 * NНетСтруктуры IVF + общие накладные расходы ниже
IVF_SQN * D для SQ88 * NНетСтруктуры IVF + метаданные SQ + общие накладные расходы
IVF_PQN * ceil(m * b / 8)8 * NНетСтруктуры IVF + кодовая книга PQ + общие накладные расходы
IVF_HNSW_FLATN * D * w8 * NДобавить GСтруктуры IVF + общие накладные расходы
IVF_HNSW_SQN * D для SQ88 * NДобавить GСтруктуры IVF + метаданные SQ + общие накладные расходы
IVF_HNSW_PQN * 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 / метаданные подындексовВсе шесть типовИзмерить удерживаемые смещения партиций, длины и структуры средства чтения; они учитываются отдельно от значений векторов и центроидов
Кодовая книга PQIVF_PQ, IVF_HNSW_PQСтандартная кодовая книга FP32: 2^b * D * 4 на каждую удерживаемую копию
Метаданные квантователя SQIVF_SQ, IVF_HNSW_SQИзмерить удерживаемые границы/параметры квантователя; не предполагайте, что формат одинаков во всех версиях
Нормы, структуры покрытия строк, дополнительные отображенияЕсли средство чтения их удерживаетИзмерить фактические массивы/множества; отображения строк для многовекторных данных требуют отдельного бюджета
Объекты индекса, буферы валидности Arrow, резервная ёмкость и копииВсе шесть типовИзмерить выделения памяти сверх логического объёма данных; совместно используемые буферы учитывайте один раз

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

Компоненты графа HNSW​

Граф учитывается дополнительно к кодам векторов и идентификаторам строк. Для графа Lance на основе Arrow с идентификаторами узлов/соседей UInt32 и необязательно удерживаемыми расстояниями рёбер Float32 обозначим через E число хранимых направленных записей о соседях на всех загруженных уровнях, через V — число вхождений узлов на этих уровнях, а через A — число независимо удерживаемых пакетов графа. Узел, присутствующий на нескольких уровнях, учитывается в V несколько раз.

Компонент графаОсновной объём данных в байтахПравило подсчёта
Идентификаторы соседей4 * EUInt32 на каждую направленную запись о соседе; учитывайте все уровни
Хранимые расстояния рёбер4 * EFloat32 на каждую запись о соседе; ноль, если средство чтения пропускает столбец расстояний
Идентификаторы узлов графа4 * VUInt32 на каждое вхождение узла; они отличаются от идентификаторов строк набора данных 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:

КомпонентОбъём данных в примере
Коды PQ96000000 байт
Идентификаторы строк набора данных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 / Float324 * RНет8 * R
Int64 / Float64 / 64-битная временная метка8 * RНет8 * R
Другие значения фиксированной длиныw * R; для Boolean с битовой упаковкой используйте фактические буферы bitmapНет8 * R
Arrow Utf8 / BinaryR * L4 * (R + H) для смещений Int328 * R
Arrow LargeUtf8 / LargeBinaryR * L8 * (R + H) для смещений Int648 * 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.

  1. Выполните прогрев с репрезентативными запросами и уровнем параллелизма, включая разные векторы запросов, фильтры и таблицы. Повторение только одного вектора занижает рабочий набор разнообразной нагрузки.
  2. Сравните 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 и потоковыми путями выполнения, которые не удерживают данные. Увеличивайте ёмкость только тогда, когда это оправдано повторным использованием и доступной памятью.
  3. Начните подбор объёма кеша метаданных со значения по умолчанию 1 GiB, если позволяет бюджет памяти BE. Корректируйте его по usage_bytes и метрикам попаданий/промахов. Потребность определяется числом файлов/Fragment, метаданными схемы/страниц, сведениями об удалениях и отображениями идентификаторов строк; она не составляет фиксированный процент от размера векторного индекса. Кеш метаданных BE отделён от кеша доступа к таблицам FE.
  4. Прежде чем увеличивать любой из кешей, проверьте память процесса BE и пики запросов при планируемом уровне параллелизма. Высокая доля попаданий в кеш при большой задержке может указывать на затраты на оценку релевантности, Prefilter, декодирование или выборку; дополнительный кеш не обязательно будет полезен.
  5. Подбирайте 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:

СегментыСреднее число векторов на сегментЧто оценить
812.5 миллионаДостаточно Split, чтобы задействовать BE, при меньшем числе операций на уровне сегментов
166.25 миллионаБольше гибкости планирования и меньше зависимость от одного большого сегмента
323.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. Проверьте смешанную нагрузку​

  1. Проверьте выбор индекса, физические Split и покрытие индекса. Включите резервные сканирования и выборки второй фазы в анализ задержки.
  2. Оцените объём удерживаемых данных каждого индекса и сложите совместные рабочие наборы. Резервируйте память запросов отдельно, включая параллельные загрузки, множества идентификаторов строк, кучи кандидатов и декодирование.
  3. Сравните смешанные нагрузки с холодным и прогретым кешем при реалистичном уровне параллелизма. Отслеживайте задержку P95, пропускную способность, recall, где применимо, пиковую память каждого BE, использование кеша и изменения попаданий/промахов, I/O загрузки индексов и неравномерность времени сканирования.
  4. Меняйте по одному параметру: ёмкость кеша, число сегментов или параметры поиска. Если перестроение сегментов меняет модель, повторно проверьте recall. Прекратите увеличивать число Split или объём кеша, когда пропускная способность, задержка или потребление памяти перестанут улучшаться.
  5. Повторяйте проверку после значительных добавлений данных, перестроения индексов, изменения числа 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 и пиковое потребление памяти.