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

Рекомендации по оптимизации производительности гибридного поиска в Lance

Используйте это руководство, когда при векторном или полнотекстовом поиске по данным Lance применяются скалярные условия. В нём объясняется, где размещать фильтры, как организовывать сегменты индексов и выявлять повторную работу по фильтрации. Здесь под гибридным поиском понимается поиск в сочетании со скалярной фильтрацией; раздельный вызов TVF для векторного и полнотекстового поиска сам по себе не объединяет результаты их ранжирования.

примечание

Lance Catalog находится на экспериментальной стадии и поддерживается начиная с Apache Doris 4.2. Сначала настройте доступ и проверьте требования к совместимости средств чтения и записи в разделе Lance Catalog. Doris читает существующие индексы Lance; создавайте и обслуживайте их совместимыми инструментами Lance. Наличие возможности в последней версии Lance SDK или в предложении upstream-проекта не гарантирует её поддержку механизмом чтения, входящим в вашу сборку Doris.

В сборках Doris 4.2 с автоматическим поиском Lance подходящие обычные SQL-запросы могут использовать тот же нативный механизм поиска, что и явные TVF. Эта возможность не зависит от прямого вызова TVF: работающий vector_search() сам по себе не означает, что она поддерживается в более ранней сборке. Отдельно включать автоматический поиск на уровне сеанса не требуется.

Соответствие функций​

SQL-выражениеТребуемый порядокМетрика Lance и результат SQL
l2_distance_approximateASCl2; возвращает CAST(sqrt(_distance) AS FLOAT) — евклидово расстояние, а не нативный квадрат расстояния L2.
inner_product_approximateDESCdot; возвращает 1.0f - _distance — сходство по скалярному произведению.
cosine_distance_approximateASCcosine; возвращает нативное косинусное расстояние.
cosine_similarity_approximateDESCcosine; возвращает 1.0f - _distance.
hamming_distance_approximateASChamming; возвращает число различающихся битов.
score() с MATCH_ANY, MATCH_ALL или MATCH_PHRASEDESCLance BM25; соответственно Match OR, Match AND или Phrase.

Точные функции расстояния и сходства, включая cosine_distance(), cosine_similarity() и hamming_distance, продолжают выполняться как скалярные функции. l1_distance() тоже остаётся скалярной: API векторного поиска Lance, входящий в сборку, не предоставляет метрику L1.

Примеры векторного поиска и BM25​

После настройки Catalog предположим, что таблица lance_catalog.default.items содержит id, category, content, четырёхмерный embedding типа Float32 fixed-size-list и трёхбайтовый binary_embedding типа UInt8 fixed-size-list. Если требуется ускорение за счёт индексов, создайте совместимые векторные индексы и зафиксированный индекс FTS для content средствами Lance.

SELECT id, cosine_distance_approximate(embedding, [0.1,0.2,0.3,0.4]) AS distance
FROM lance_catalog.default.items
WHERE category = 'book'
ORDER BY distance ASC LIMIT 10 OFFSET 5;

SELECT id, cosine_similarity_approximate(embedding, [0.1,0.2,0.3,0.4]) AS similarity
FROM lance_catalog.default.items
ORDER BY similarity DESC LIMIT 10;

SELECT id, hamming_distance_approximate(binary_embedding, [0,255,170]) AS distance
FROM lance_catalog.default.items
ORDER BY distance ASC LIMIT 10;

SELECT id, score() AS relevance
FROM lance_catalog.default.items
WHERE content MATCH_ALL 'storage engine' AND category = 'book'
ORDER BY relevance DESC LIMIT 10;

Первый запрос ищет строки с category = 'book' до отсечения кандидатов. Каждый сплит запрашивает 15 кандидатов с нулевым смещением; Doris объединяет кандидатов и применяет итоговое OFFSET 5 LIMIT 10. Остальные запросы возвращают до 10 соседей или совпадений BM25 в соответствующем порядке. Поиск с использованием индекса может быть приближённым и вернуть меньше строк, чем запрошено. Существующие совместимые индексы используются повторно; для данных, не покрытых индексами, применяется существующий механизм Flat Search. Для BM25 требуется существующий индекс FTS.

Условия применения и резервный вариант​

  • Запрос должен содержать прямое сканирование таблицы Lance Catalog, необязательный фильтр, проекцию и Top-N с одним ключом сортировки по результату поиска и положительным LIMIT. JOIN/EXISTS, дополнительные ключи сортировки, DISTINCT, выборка из таблицы по образцу и выражения проекции, которые нельзя перенести, препятствуют этому переписыванию.
  • Для метрик с плавающей точкой требуются физические векторы типа Float32 fixed-size-list, а для Hamming — физические векторы типа UInt8 fixed-size-list. Векторы запроса должны быть константными массивами соответствующей размерности. Float16/Float64, многовекторные столбцы и произвольные приведения типов, изменяющие элементы, автоматически не переписываются. UInt8 в Doris представлен как SMALLINT; его расширение до BIGINT для аргументов функции Hamming безопасно.
  • Векторные запросы с неподдерживаемыми остаточными предикатами, обратным порядком сортировки результатов поиска, отсутствующими нативными метаданными или не прошедшие нативную проверку вектора либо схемы продолжают вычисляться скалярно. Для переписывания значение LIMIT + OFFSET должно укладываться в нативный лимит числа кандидатов UINT32; обычные скалярные SQL-запросы этим лимитом не ограничены.
  • Для BM25 требуются один поддерживаемый предикат MATCH верхнего уровня для непосредственного строкового столбца, литеральный поисковый запрос, сохранённый в индексе анализатор и сортировка по score() по убыванию. Остальные скалярные предикаты должны полностью проталкиваться. Условия score() > literal и score() >= literal поддерживаются как остаточные нижние границы; верхние границы и другие предикаты по оценке отклоняются. Неподдерживаемые формы запросов с оценкой вызывают ошибку, а не переходят незаметно к скалярному вычислению score().
  • Оценки рассчитываются на основе сохранённого анализатора и индекса Lance, а также статистики по всему снимку. Численно они могут не совпадать с оценками отдельно проиндексированной внутренней таблицы Doris. Это переписывание не поддерживает нечёткий поиск, переопределение анализатора и выражения SEARCH() по нескольким столбцам. Для поддерживаемых многовекторных запросов и настройки параметров, таких как nprobes, refine_factor или ef, используйте явные TVF.

Семантика нативного приближённого поиска​

Суффикс _approximate позволяет подходящему запросу Top-N использовать семантику отбора кандидатов нативным поиском Lance. Если переписывание не применяется, функции выполняют скалярные вычисления. Не используйте приближённые функции, когда требуется исчерпывающее ранжирование или скалярная проверка каждой сохранённой строки.

Входные данныеСкалярное вычислениеПодходящий нативный поиск
Сохранённый вектор NULLОшибкаИсключается из числа кандидатов.
Сохранённый вектор, для которого расстояние равно NaNМожет вернуть NaNИсключается, поэтому запрос может вернуть меньше строк.
Элемент вектора равен NULLОшибкаНе входит в поддерживаемую область входных данных нативного поиска; убедитесь, что элементы не содержат NULL.
Сохранённый вектор с нулевой нормой для косинусной метрикиРасстояние 2, сходство 0Исключается, если нативное расстояние оказывается неопределённым или равно NaN.
Нулевая или неконечная норма вектора запроса для косинусной метрики, включая выход квадрата нормы за нижнюю или верхнюю границу диапазона Float32Скалярное поведениеАвтоматическое переписывание пропускается.
Входные данные для HammingЦелочисленные массивы, каждый элемент в [0,255]Физические векторы UInt8 и корректный байтовый вектор запроса.

Результаты нативных вычислений и оценок индекса для косинусной метрики могут отличаться от результатов скалярных вычислений. Hamming считает различающиеся биты, а не байты: hamming_distance([0,255,170], [255,0,85]) возвращает 24. Hamming отклоняет массивы с числами с плавающей точкой, десятичными числами и LARGEINT до неявного приведения типов, а значения вне допустимого диапазона — до преобразования в байты. Для скалярного выполнения по-прежнему доступны точные функции.

Предварительная фильтрация WHERE и граница JOIN​

Предикаты таблицы Lance, которые можно полностью протолкнуть, вычисляются перед поиском на одном и том же закреплённом снимке. Doris сначала пытается выполнить сканирование только идентификаторов строк и передаёт полный список разрешённых идентификаторов всем поисковым считывателям в запросе. Пустой список не выбирает ни одной строки. Если идентификаторов больше 65,536, Doris отбрасывает частичный список и сохраняет полную нативную предварительную фильтрацию по предикату. Объём как временного, так и кэшированного списка учитывается в лимитах памяти запроса; сбор списков, прерванный или завершившийся ошибкой, не кэшируется. Этот механизм отличается от проталкивания Runtime Filter при соединении в обычных сканированиях.

В этом примере с ACL предположим, что таблица internal.auth.allowed_items содержит item_id и principal_id.

SELECT d.id,
l2_distance_approximate(d.embedding, [0.1,0.2,0.3,0.4]) AS distance
FROM lance_catalog.default.items d
WHERE EXISTS (
SELECT 1 FROM internal.auth.allowed_items a
WHERE a.item_id = d.id AND a.principal_id = 42
)
ORDER BY distance ASC
LIMIT 10;

При этом сохраняются соединение, скалярное вычисление расстояния и глобальный Top-N. Переписывание не пересекает полусоединение ACL и не сопоставляет бизнес-идентификаторы с нативным _rowid Lance. Если выбрать Top-K без фильтрации до применения ACL, можно пропустить ближайшие строки, к которым есть доступ. Добавление второго ключа сортировки также препятствует автоматическому переписыванию.

Выполните EXPLAIN для конкретного запроса и проверьте наличие externalSearchType=VECTOR с lanceMetric=l2|dot|cosine|hamming либо externalSearchType=FULL_TEXT. Метрики LanceRowIdPrefilterTime, LanceRowIdPrefilterRows, LanceRowIdPrefilterCacheHits и LanceRowIdPrefilterFallbacks отражают работу Doris со списком разрешённых идентификаторов; они отличаются от приведённых ниже метрик нативного загрузчика идентификаторов строк. Для настройки поиска или поддерживаемых форм запросов, не подпадающих под это правило, используйте явные TVF. Переписанные поисковые запросы могут использовать существующее двухэтапное чтение выходных данных, если выполняются его собственные условия применения.

1. Задавайте фильтры отбора кандидатов внутри поиска​

Для настроенной таблицы с целочисленным столбцом id, строковыми столбцами category и content и четырёхмерным вектором embedding используйте параметр filter в TVF для условий, которые должны влиять на формирование кандидатов. Векторный индекс должен соответствовать размерности и метрике; для текстового столбца должен быть зафиксирован FTS-индекс.

SELECT id, _distance
FROM vector_search(
"table" = "lance_catalog.default.items",
"column" = "embedding",
"query_vector" = "[0.1,0.2,0.3,0.4]",
"metric" = "l2",
"top_k" = "20",
"filter" = "category = 'book'",
"use_index" = "true"
)
ORDER BY _distance ASC, id;

Этот запрос ищет до 20 соседей среди строк, удовлетворяющих условию. Это приближённый поиск: возврат 20 строк не подтверждает полноту поиска (recall). Для полнотекстового поиска с фильтрацией:

SELECT id, _score
FROM full_text_search(
"table" = "lance_catalog.default.items",
"column" = "content",
"query" = "storage engine",
"query_type" = "match",
"operator" = "and",
"top_k" = "20",
"filter" = "category = 'book'",
"coverage_mode" = "strict"
)
ORDER BY _score DESC, id;

Этот запрос возвращает до 20 подходящих документов, ранжированных по BM25. Внешнее условие WHERE category = 'book' имеет другую семантику: Doris фильтрует кандидатов, уже сформированных Lance, перед глобальным TopN. Удалённые кандидаты автоматически не восполняются. За пределами TVF оставляйте только те условия, которые намеренно применяются после поиска, даже если EXPLAIN показывает их внутри оператора Scan в Doris.

2. Учитывайте единицу параллельной работы​

Query snapshot
-> FE plans physical search-index segment splits
-> Each split: scalar Prefilter -> vector or full-text candidates
-> Doris residual filtering -> local TopN -> global TopN
-> Optional deferred fetch of output columns
Путь выполнения запросаОснова разбиения на задачиСледствие
Векторный поиск по индексуВыбранные физические сегменты векторного индекса; для непокрытых фрагментов создаются задачи Flat SearchСегменты скалярного индекса сами по себе не определяют число задач векторного запроса. Данные без индекса могут определять основную часть задержки.
Полнотекстовый поискВыбранные физические сегменты FTS-индексаРежим покрытия strict, используемый по умолчанию, отклоняет запросы при наличии непокрытых фрагментов; index_only пропускает их. Резервного пути через Flat Search для полнотекстового поиска нет.
Обычное скалярное сканированиеСобственное планирование сканирования с возможным использованием сегментов скалярного индекса или групп фрагментовПо плану запроса только со скалярными условиями нельзя судить о параллелизме векторного запроса с фильтрацией.

lance_fragments_per_split управляет группировкой фрагментов при обычном сканировании, а не количеством физических сегментов векторного индекса. Увеличение числа экземпляров pipeline или параллелизма сканеров не создаёт больше задач по векторным сегментам, чем сформировал планировщик. Сканер Lance также может выполнять работу параллельно внутри себя, поэтому один сканер Doris не равнозначен одному потоку CPU.

При S поисковых задачах каждая может вернуть не более top_k + offset кандидатов. Поэтому верхняя граница числа кандидатов до слияния равна S * (top_k + offset), до остаточной фильтрации и глобального TopN. Увеличение числа сегментов может улучшить распределение работы, одновременно увеличивая затраты на подготовку каждого сегмента, фильтрацию, слияние кандидатов и память для параллельного выполнения.

3. Согласуйте покрытие фрагментов, а не количество сегментов​

Фрагмент представляет собой единицу данных. Сегмент индекса представляет собой самостоятельный индекс, покрывающий набор фрагментов. Логический индекс объединяет физические сегменты под одним именем индекса. Раздел IVF, страница BTree или блок списков вхождений FTS являются внутренними единицами индекса; ни одна из них не определяет поисковую задачу Doris.

В следующем примере используются восемь фрагментов данных:

Схема размещенияПокрытие векторного индексаПокрытие скалярного индексаВлияние на фильтрацию при чтении скалярного индекса с учётом фрагментов
СогласованнаяV0={0,1,2,3}, V1={4,5,6,7}B0={0,1,2,3}, B1={4,5,6,7}Каждой задаче векторного поиска нужен только соответствующий скалярный сегмент.
Скалярные сегменты мельчеКак вышеВосемь сегментов, по одному на фрагментКаждая задача выбирает четыре скалярных сегмента; ни один не пересекает границу векторного сегмента.
Скалярные сегменты крупнееКак вышеB0={0,1,2,3,4,5,6,7}Обе задачи векторного поиска могут обращаться к одному скалярному сегменту.
Одинаковое количество, но границы пересекаютсяКак вышеB0={0,1,4,5}, B1={2,3,6,7}Оба скалярных сегмента пересекаются с обеими задачами векторного поиска. Равное количество не обеспечивает согласованности.

Отсечение по фрагментам пропускает скалярные сегменты без пересечений. При частичном пересечении сегмент сохраняется: это не приводит к автоматическому построению меньшего BTree или обрезке каждого списка вхождений перед вычислением предиката. Повторные запросы могут повторно использовать содержимое индекса из кэша, но при этом снова выполнять поиск и строить множества идентификаторов строк. Поиск в BTree не обязательно сканирует все страницы сохранённого сегмента; стоимость зависит от предиката и числа совпадений.

important

Эта возможность зависит от фактического пути чтения. В пути чтения с фильтрацией V2 в Lance, где скалярные индексы загружаются с учётом фрагментов, не относящиеся к нужным фрагментам скалярные сегменты могут исключаться до поиска. Устаревшие пути чтения файлов данных V1 могут обрабатывать логический скалярный индекс в более широкой области, а затем ограничивать идентификаторы строк. V1/V2 здесь относятся к формату файлов данных, а не к версии Doris или типу векторного индекса. Не предполагайте, что отсечение одинаково работает во всех версиях механизма чтения или в пути FTS; проверяйте план и профиль каждой группы запросов.

Для столбцов, которые часто используются совместно, применяйте одинаковые группы фрагментов при построении векторных, скалярных и FTS-индексов. Скалярные сегменты могут быть мельче, если каждый из них целиком находится внутри обслуживаемого им поискового сегмента. Если векторным и FTS-сегментам нужны разные размеры, рассмотрите объединение общих базовых групп в более крупные и следите, чтобы скалярные сегменты не пересекали границы ни в одном из вариантов. Такая политика размещения уменьшает возможные пересечения, но не гарантирует, что каждый путь выполнения использует это преимущество.

Выберите начальную гранулярность​

Универсального оптимального числа строк или размера сегмента в байтах нет. Начните с BE, которые могут обслуживать эту нагрузку, а затем сравните один сбалансированный поисковый сегмент на BE с несколькими сегментами на BE при фиксированном recall и целевом числе одновременных запросов. Это варианты для эксперимента, а не обязательные соотношения.

НагрузкаПриоритет при построенииОсновной компромисс для оценки
Векторный поиск с фильтрацией, чувствительный к задержкамСбалансированные группы векторного индекса и скалярное покрытие, не выходящее за их границыВремя самой медленной задачи в сравнении с повторной фильтрацией и ростом числа кандидатов от всех задач
Поиск при большом числе одновременных запросовИзбегайте чрезмерного числа задач на запрос; сохраняйте разделы, пригодные для повторного использованияQPS, задержка P95, очереди CPU и пиковое потребление памяти в совокупности
Широкие скалярные фильтры или частые термины FTSПри балансировке групп учитывайте число совпадений, длину текста и размеры списков вхожденийДаже при равном числе строк затраты на фильтрацию и поиск могут различаться
Частое добавление данныхСтройте все связанные индексы для одних и тех же новых групп фрагментовПокрытие свежих данных в сравнении с накоплением мелких сегментов
Очень крупные отдельные фрагментыПланируйте размеры файлов данных до построения индексовПокрытие на основе фрагментов не позволяет разделить один фрагмент на несколько непересекающихся групп покрытия

Помимо числа строк измеряйте размер закодированных векторов и графов, распределение скалярных значений, размеры текста и списков вхождений. В каждом векторном сегменте должно быть достаточно обучающих данных для его конфигурации IVF/PQ. nlist (число разделов IVF) и количество физических сегментов выбираются независимо; изменение одного параметра не приводит к автоматической корректировке другого.

4. Стройте связанные индексы по единому плану группировки​

В Doris нет параметра, который автоматически согласует индексы разных столбцов. Используйте API распределённого построения Lance, если они поддерживаются вашей совместимой версией SDK. Ниже приведена схема оркестрации, а не готовый к запуску генератор набора данных или конфигурация для тестирования производительности:

# ds is an existing LanceDataset at the chosen build snapshot.
# groups contains non-empty, disjoint lists of actual fragment IDs.
# vector_params and fts_params are validated for the schema and SDK version.
# Keep writes and compaction paused for this simple coordinator example.
visible = {fragment.fragment_id for fragment in ds.get_fragments()}
assigned = [fragment_id for group in groups for fragment_id in group]
assert groups and all(groups)
assert len(assigned) == len(set(assigned))
assert set(assigned) == visible

specs = [
("embedding_idx", "embedding", "IVF_PQ", vector_params),
("category_idx", "category", "BTREE", {}),
("content_idx", "content", "INVERTED", fts_params),
]
segments = {name: [] for name, _, _, _ in specs}
for fragment_ids in groups:
for name, column, index_type, params in specs:
segment = ds.create_index_uncommitted(
column,
index_type,
name=name,
fragment_ids=fragment_ids,
**params,
)
segments[name].append(segment)

# These are three separate commits, not an atomic multi-index publication.
for name, column, _, _ in specs:
ds.commit_existing_index_segments(name, column, segments[name])

Подготовьте и проверьте входные данные следующим образом:

  1. Зафиксируйте план построения. Получите фактические идентификаторы фрагментов из одного снимка; идентификаторы не обязательно идут подряд. Сбалансируйте группы по числу строк и оценочному размеру индексов в байтах. Используйте именно эти группы для каждого связанного столбца. Все распределённые рабочие процессы должны открывать одну и ту же версию для построения.
  2. Выберите совместимые конфигурации. Для IVF_PQ задайте подходящую метрику, num_partitions и num_sub_vectors с учётом размерности вектора и размера выборки. Поддерживайте единообразие конфигураций в пределах логического индекса. Для FTS используйте одинаковые настройки анализатора и токенизатора и включайте with_position, если нужен фразовый поиск.
  3. Выберите стратегию работы с векторными моделями. Поддерживаемые варианты построения нескольких сегментов могут обучать каждый сегмент независимо. Если последующее физическое слияние требует общих моделей, передайте рабочим процессам одинаковые центроиды IVF и кодовую книгу PQ. Не объединяйте независимо обученные сегменты без проверки и не предполагайте, что графы HNSW сохраняются при слиянии; проверьте поддерживаемый SDK порядок действий.
  4. Сначала постройте индексы, затем опубликуйте. Каждое построение возвращает физический сегмент с собственным UUID. Соберите успешные результаты и проверьте покрытие и совместимость перед фиксацией каждого логического индекса. Не назначайте один общий физический UUID разным столбцам или результатам разных рабочих процессов. В примере предполагается использование новых имён логических индексов; замена, повторные попытки и очистка после сбоев требуют отдельной политики координатора.
  5. Проверьте полученный манифест. Для каждого индекса сравните фактическое покрытие фрагментов сегментами с запланированными группами и проверьте, нет ли пропусков или пересечений покрытия. Одного совпадения количества сегментов недостаточно. Используйте метаданные индексов Lance; команда Doris SHOW INDEX для таблиц Filesystem Catalog помогает проверить имена, типы и поля логических индексов, но не заменяет проверку покрытия.

Единый план группировки не требует слияния результатов всех рабочих процессов в один сегмент. Такое слияние удалило бы границы, которые вы намеревались сохранить. Сведения об API построения и фиксации и поддерживаемых процессах слияния см. в разделе Распределённое построение индексов Lance.

Сохраняйте согласованность при обслуживании​

  • Добавляйте связанные векторные, скалярные и FTS-сегменты для одних и тех же новых групп фрагментов. После каждой публикации проверяйте покрытие снимка, используемого запросом. Режим FTS strict может отклонять запросы в промежутке между добавлением данных и завершением публикации индекса.
  • Рассматривайте уплотнение, перезапись и оптимизацию индексов как изменения схемы размещения. В зависимости от операции и версии механизма чтения индексы могут переназначаться или перестраиваться; проверяйте полученное покрытие, не предполагая, что прежние границы сохраняются.
  • Объединяйте мелкие группы по единому плану размещения. Не оптимизируйте каждый индекс независимо, создавая несогласованные группы, если производительность смешанных запросов зависит от согласованности.
  • Для обслуживания без остановки работы используйте координатор с обработкой снимков, конфликтов и восстановлением. Отдельные фиксации индексов не обеспечивают атомарного переключения всех столбцов. TVF читают последний снимок main; их аргумент table не позволяет выбрать историческую версию или тег.

Логические индексы и планы развития upstream-проекта​

Формат индексов Lance определяет логические индексы и физические сегменты. В задаче по распределённому построению индексов отслеживается общий жизненный цикл сегментов для разных семейств индексов. Эта модель не требует одинаковых границ фрагментов у разных столбцов. Группировка между столбцами и обслуживание остаются задачами оркестрации; не считайте планы upstream-проекта автоматически доступной возможностью согласования индексов в Doris.

5. Настраивайте фильтры и память совместно​

НаблюдениеСледующий экспериментОграничение по корректности или ресурсам
Очень избирательный фильтр, слишком мало результатов векторного поиска или низкий recallПроверьте расположение фильтра и покрытие индекса; сравните настройки пробирования и точный эталонный поиск на ограниченном наборе данныхУменьшение числа проб ради снижения задержки может снизить recall. Увеличение числа проб не может добавить строки, не проходящие фильтр.
Широкий фильтр формирует большие множества PrefilterСравните согласованные схемы размещения и фактическую селективность скалярного предикатаИспользование индекса не устраняет затраты на материализацию большого числа идентификаторов подходящих строк.
При увеличении числа задач векторного поиска повторяется работа со скалярными условиямиСравните пересечения покрытий сегментов и пути чтенияУвеличение числа задач может усиливать фильтрацию даже при прогретом кэше.
Запросы с холодным кэшем выполняются медленноСравните профили при холодном и прогретом кэше на репрезентативных векторах и терминахПрогрев одним запросом не охватывает рабочий набор производственной нагрузки.
Смешанная нагрузка при прогретом кэше начинает замедлятьсяИзмерьте промахи общего кэша и очереди CPU при целевом числе одновременных запросовСодержимое скалярных, векторных и FTS-индексов может конкурировать за один и тот же лимит кэша индексов сессии на BE.
Большой top_k, глубокий offset или широкий набор выходных столбцовУменьшите запрашиваемое число кандидатов, если это допускает семантика; проверьте отложенное получение данныхКучи кандидатов, маски идентификаторов строк, уточнение результатов и буферы получения данных требуют дополнительной памяти запроса.

Оцените размер совокупного рабочего набора индексов, пригодного для повторного использования, и оставьте запас памяти для одновременных запросов. Ёмкость кэша не является ограничением памяти процесса, а увеличение кэша метаданных не увеличивает кэш индексов. Формулы расчёта объёма данных для разных индексов, затраты на графы HNSW, область действия кэшей и примеры конфигурации Doris BE см. в разделе Рекомендации по выполнению запросов к Lance.

6. Проверяйте результат по планам и профилям​

Сравнивайте один и тот же набор запросов, снимок набора данных, тип и конфигурацию индекса, top_k и целевой recall. Изменяйте по одному аспекту размещения или параметру за раз. Включайте избирательные и широкие фильтры, разнообразные векторы и термины, запуски с холодным и прогретым кэшем и запланированное число одновременных запросов. Фиксируйте P50/P95, пропускную способность, число результатов, recall и пиковое потребление памяти на каждом BE.

ЭтапДанные для проверкиИнтерпретация
Метаданные и планирование на FEExecution Summary → Lance Metadata Metrics: MetadataReadTime, IndexMetadataReadTime, SplitPlanningTime и соответствующие CallsОтделяйте доступ к метаданным Catalog и планирование задач от поиска на BE. Суммарное время работы с метаданными включает время отдельных этапов.
Планирование и покрытиеEXPLAIN: lanceSearchIndexSegments, lanceSearchIndexFragments, lanceSearchUnindexedFragments; LanceSearchPlan: LancePlannedIndexSegmentCount, LancePlannedIndexedFragmentCount, LancePlannedFlatSearchFragmentCountЭти показатели описывают запланированную или назначенную поисковую работу, а не завершённый поиск. Счётчики Flat Search относятся к векторным запросам, а не к FTS.
Подготовка механизма чтения и пакетное чтениеLanceTiming: LanceDatasetOpenTime, LanceScannerConfigureTime, LanceScannerNextTime, LanceArrowToDorisBlockTimeРазличайте открытие и настройку, чтение Lance и преобразование в Doris. Время пакетного чтения включает внутренний поиск, ввод-вывод, декодирование и ожидания.
Prefilter по идентификаторам строкLanceScan: RowIdPrefilterLoads, RowIdPrefilterInputRows, RowIdPrefilterInputBatches, RowIdPrefilterIds, RowIdPrefilterLoadTime, RowIdPrefilterInputTime, RowIdPrefilterBuildTimeОжидание и чтение входных данных и построение маски входят в общее время загрузки. Эти показатели описывают загрузчик идентификаторов строк, а не все скалярные предикаты или итоговый набор кандидатов.
Путь обработки скалярных сегментовLanceScalarIndex: LanceScalarIndexSegmentsRequested, LanceScalarIndexSegmentsSearched, LanceScalarIndexSegmentFallbacks, LanceScalarIndexCandidateRows, LanceScalarIndexCandidateEvaluationTime, LanceScalarIndexSegmentSearchTimeОценка кандидатов включает поиск по сегментам. Кандидаты подсчитываются до ограничения по фрагментам и полной фильтрации; повторные задачи могут учитывать кандидатов повторно, а при недоступных данных об их количестве счётчик может оставаться нулевым.
Повторное использование индексов и ввод-выводLanceIndex: LanceIndexCacheHits, LanceIndexCacheMisses, LanceIndexObjectLoads, LanceIndexComponentsLoaded, LanceIndexComparisons; LanceIO: LanceIOReadBytes, LanceIOReadOps, LanceIOReadRequestsРазличайте загрузку объектов и компонентов, обращения к кэшу и поисковую работу. Число загрузок не равно числу промахов; показатели ввода-вывода при выполнении не отражают весь удалённый трафик.
Выбор разделов векторного индекса и поискLanceIVFPartitionsConsidered, LanceIVFPartitionSelectionTime, LanceIndexPartitionsSearched, LanceVectorIndexDeltasSearched, LanceIndexPartitionLoadTime, LanceIndexSearchTime, LanceIndexDistanceTopKTimeРазличайте выбор разделов и фактический поиск. Время расчёта расстояний и Top-K включает фильтрацию и работу с кучей и перекрывается со временем поиска. FTS также сообщает число разделов, в которых выполнялся поиск.
Ожидания и параллелизмLanceIndexPrefilterWaitTime, LanceIndexCpuQueueWaitTime, LanceScanTaskWaitTimeРазличайте ожидание Prefilter индекса, ожидание в очереди задач CPU и ожидание задач FilteredRead. Ни один показатель не отражает полностью время ожидания ввода-вывода или планирования Doris.
Получение выходных данныхСведения о получении строк по идентификаторам и материализации: LanceRowIdFetchCalls, LanceRowIdFetchRows, LanceRowIdFetchTotalTime, LanceRowIdTakeReadTime, LanceArrowToDorisBlockTime и счётчики байтов кэша данныхСуммарные показатели получения данных включают этапы чтения и преобразования и собираются отдельным путём статистики. Не прибавляйте те же экспортируемые значения повторно как независимую работу.
Полная задержкаСамый медленный Scanner, ожидания зависимостей, ожидание, получение и запись на FE и время по часам клиентаНе складывайте времена перекрывающихся операторов или всех параллельных Scanner для расчёта задержки запроса.

Определения метрик и их область действия см. в Справочнике по профилю запросов Lance, а сведения о счётчиках байтов кэша данных Dataset и метриках Session на уровне всего BE см. в разделе Проверка эффективности кэша. Счётчики кэша индексов на уровне запроса не являются разностями суммарных значений Session.

Детализация профиля зависит от работающих FE/BE и средств инструментирования Lance, входящих в сборку. Метрики сканеров регистрируются заранее; нулевое значение может означать, что нативный путь не сообщил метрику. Обратные вызовы выполнения также требуют завершения потоков данных, поэтому при ошибке, отмене или досрочном завершении сканирования счётчики могут быть неполными. Отсутствующие или нулевые значения сами по себе не доказывают, что индекс или фильтр не использовался. LanceScannerNextTime перекрывается с внутренними таймерами поиска и ожидания; высокое значение не доказывает, что узкое место находится в хранилище. Если эти метрики не объясняют длительность чтения, соберите трассировку CPU и ввода-вывода, прежде чем относить оставшееся время к хранилищу.

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