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

Lance Catalog

примечание

Это экспериментальная возможность.

Lance Catalog поддерживается начиная с Apache Doris 4.1.4.

Lance — колоночный формат данных, разработанный для аналитических задач и нагрузок ИИ. Doris может использовать Lance Catalog для обнаружения баз данных и таблиц в Lance Namespace и напрямую запрашивать наборы данных Lance, хранящиеся в локальной файловой системе, S3-совместимом объектном хранилище или Alibaba Cloud OSS.

В настоящее время Doris предоставляет доступ к Lance только для чтения. Создание, запись, обновление и удаление таблиц Lance не поддерживаются.

Сценарии использования​

СценарийОписание
Прямые запросы к данным LanceАнализ существующих наборов данных Lance с помощью Doris SQL без предварительного переноса или импорта данных.
Векторный поиск для ИИПовторное использование существующих векторных индексов Lance, выполнение поиска через vector_search() и его сочетание со скалярной фильтрацией и аналитикой Doris SQL.
Полнотекстовый поискПовторное использование существующих инвертированных индексов FTS в Lance, выполнение поиска BM25 через full_text_search() и его сочетание со скалярной фильтрацией и аналитикой Doris SQL.
Интеграция данныхЧтение данных Lance во внутренние таблицы Doris для дальнейшей обработки, соединений или долговременного хранения.
Управление NamespaceFilesystem Catalog подходит для простой структуры на основе каталогов файловой системы. REST Catalog используется, когда требуется централизованно управлять Namespace, расположением таблиц или временными учётными данными для хранилища.

Обзор возможностей​

ВозможностьПоддержка
Filesystem CatalogПоддерживает хранилища в локальной файловой системе, file://, s3:// и oss://
REST CatalogПоддерживает Lance REST Namespace без аутентификации, с Bearer Token, API Key или пользовательскими HTTP-заголовками
Доступ к метаданнымПоддерживает SHOW DATABASES, SHOW TABLES, DESC, SHOW INDEX и TVF lance_index_entries (последние два доступны только для Filesystem Catalog)
Системные таблицы (table$...)Не поддерживаются; для метаданных индексов используйте SHOW INDEX и TVF lance_index_entries
Запросы к даннымПоддерживает отсечение столбцов, параллельное сканирование Fragment в Lance и чтение текущей версии с согласованным снимком
Проталкивание предикатовПоддерживает проталкивание совместимых статических скалярных предикатов и Runtime Filter соединений в Lance
Файловые TVFПоддерживает прямые запросы к наборам данных Lance через s3() и local()
Векторный поискПоддерживает одновекторные и мультивекторные столбцы, использует физические сегменты индексов Lance как параллельные части сканирования, сохраняет неохваченные Fragment как части Flat Search и выполняет глобальное слияние Top-K в Doris
Полнотекстовый поискИспользует существующие инвертированные индексы Lance FTS через full_text_search(), поддерживает запросы Match OR/AND и Phrase и выполняет глобальное слияние Top-K в Doris
Кэш LanceСовместно использует кэши индексов и метаданных, а также локальный дисковый кэш данных в пределах каждого BE
Двухфазное чтение TopNПо умолчанию включено для подходящих выходных столбцов векторного и полнотекстового поиска; независимо управляется параметром enable_lance_lazy_materialization
Запись в LanceНе поддерживается
Time TravelПоддерживает FOR VERSION AS OF, FOR TIME AS OF, @tag(...) и @branch(...), в том числе для таблиц, версиями которых управляет REST Namespace
Гибридный поискНе поддерживается

Версия Lance и совместимость​

Модуль чтения данных в Doris BE собран с lance-c v0.1.9 и патчами Doris, которые добавляют поддержку мультивекторного поиска и обновляют встроенные крейты Lance Rust до 11.0.0 (коммит ab6b5bbe). Doris FE читает метаданные Namespace и наборов данных с помощью org.lance:lance-core:12.0.0; FE только читает метаданные и никогда не записывает наборы данных, поэтому более новая версия Lance в FE не влияет на возможности чтения BE. Набор данных должен быть совместим и с загрузкой метаданных в FE, и с чтением данных в BE. Эти версии реализации отличаются от версии Lance data_storage_version, записанной в наборе данных.

В следующей таблице описана совместимость этого модуля чтения с форматами файлов:

data_storage_versionПоддержка чтенияПримечания
0.1 / legacyПоддерживаетсяИсходный формат файлов Lance.
2.0 (псевдоним параметра записи 0.3)ПоддерживаетсяБолее ранняя версия формата файлов Lance v2.
2.1 / stableПоддерживается; стабильный формат по умолчаниюВо встроенной версии Lance параметр записи stable и формат по умолчанию для новых наборов данных соответствуют 2.1.
2.2ПоддерживаетсяВстроенная версия Lance считает этот формат стабильным, но он не является форматом записи по умолчанию.
2.3 / nextЭкспериментальная поддержка; без гарантийВстроенная версия Lance помечает 2.3 как нестабильный формат, а параметр записи next соответствует 2.3.
Более поздняя или неизвестная версияНе поддерживаетсяОткрытие или сканирование набора данных может завершиться ошибкой неподдерживаемой версии формата хранения.

Номера выпусков Lance SDK и версии формата файлов независимы. Набор данных, записанный более старым или новым Lance SDK, доступен для чтения только в том случае, если встроенные в Doris версии понимают его формат хранения, обязательные флаги возможностей таблицы, формат индекса и типы данных Arrow/Lance. Поэтому:

  • Ожидается, что Doris сможет читать наборы данных в форматах хранения 0.1, 2.0, 2.1 и 2.2 с учётом ограничений типов, описанных ниже.
  • Совместимость с будущими версиями не гарантируется. Набор данных, записанный или изменённый более поздним выпуском Lance, может оказаться недоступным для чтения, если он использует более новый формат хранения, неизвестную обязательную возможность манифеста, более новый формат индекса или неподдерживаемый тип расширения.
  • Для наборов данных, которые должны оставаться доступными для чтения этим выпуском Doris, используйте текущий стабильный формат по умолчанию 2.1 и не используйте next. При внедрении более нового модуля записи или дополнительной возможности Lance проверьте полученный набор данных на целевом выпуске Doris перед использованием в рабочей среде.

Настройка Catalog​

Синтаксис​

CREATE CATALOG [IF NOT EXISTS] catalog_name PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "<filesystem|rest>",
{CatalogProperties},
{StorageProperties},
{CommonProperties}
);

Общие свойства​

СвойствоОбязательноеПо умолчаниюОписание
typeДа-Должно быть lance.
lance.catalog.typeНетfilesystemТип Catalog. Допустимые значения: filesystem и rest.
lance.namespace.parentНетПустоОграничивает доступ указанным Lance Namespace и его дочерними Namespace. Например, с разделителем по умолчанию production$analytics обозначает двухуровневый Namespace.
lance.namespace.delimiterНет$Разделитель для разбора lance.namespace.parent. Также передаётся клиенту REST Namespace. Это свойство не меняет отображение многоуровневых Namespace в Doris.
lance.namespace.root_databaseНетdefaultИмя базы данных Doris, в которую отображается корневой Lance Namespace.
lance.table_access_cache_ttl_secondsНет60Неотрицательное целое число. Максимальное время жизни кэша доступа к таблицам FE в секундах; 0 отключает кэш. Применяется к Filesystem Catalog и REST Catalog. Таблицы с управлением версиями через Namespace, а также результаты с предоставляемыми параметрами хранилища или URI, содержащими учётные данные, не кэшируются; см. Кэш доступа к таблицам FE.

Filesystem Catalog​

Filesystem Catalog обнаруживает Lance Namespace и таблицы непосредственно в каталоге хранилища.

СвойствоОбязательноеОписание
warehouseДаКорневой путь хранилища Lance. Поддерживаются локальные абсолютные пути, URI file://, URI s3:// и URI oss://.

Эти два свойства выполняют разные задачи:

  • fs.s3.support=true явно выбирает разбор конфигурации S3. AWS S3 обычно определяется по его свойствам. Для MinIO, пользовательских конечных точек или OSS с доступом через S3-совместимый API задавайте это свойство явно, чтобы избежать ошибки определения или выбора другого типа хранилища на основе домена. Все приведённые ниже примеры S3 включают это свойство и готовы к непосредственному использованию.
  • use_path_style определяет, где в URL запросов указывается бакет, и по умолчанию равно false. Значение true использует endpoint/bucket/object-path; значение false включает адресацию virtual-hosted-style. В текущем клиенте S3 по умолчанию в Lance явно заданная конечная точка virtual-hosted-style должна включать бакет. Задавайте это свойство вместе с конечной точкой в соответствии со способом доступа, поддерживаемым сервисом.
Сценарий доступаНастройка типа хранилищаuse_path_styleФормат конечной точки
AWS S3Рекомендуется: fs.s3.support=truefalse; можно не указыватьВключайте бакет, например https://my-bucket.s3.us-east-1.amazonaws.com.
MinIO (доступ через домен сервиса или IP)Задайте fs.s3.support=trueЯвно задайте trueАдрес сервиса без бакета, например http://minio.example.com:9000.
S3-совместимый API OSSЗадайте fs.s3.support=truefalse; явно задано в примереВключайте бакет, например https://my-bucket.oss-cn-beijing.aliyuncs.com.
Нативный API OSSРекомендуется: fs.oss.support=true; не задавайте fs.s3.supportОбщий параметр use_path_style задавать не требуетсяИспользуйте oss.endpoint без бакета, например https://oss-cn-beijing.aliyuncs.com.

Эти правила для конечных точек S3 применяются к способу доступа S3 по умолчанию в Lance. Для Filesystem Catalog с одним типом хранилища задавайте только соответствующее свойство fs.*.support=true; не включайте несколько типов хранилищ ради «совместимости». Это свойство выбирает способ разбора конфигурации и не добавляет сервису хранения поддержку протоколов.

Выберите пример для системы хранения, в которой размещено хранилище, и замените бакет, путь, адрес и учётные данные:

CREATE CATALOG lance_fs_s3 PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "filesystem",
"warehouse" = "s3://my-bucket/lance",
"fs.s3.support" = "true",
"use_path_style" = "false",
"s3.endpoint" = "https://my-bucket.s3.us-east-1.amazonaws.com",
"s3.region" = "us-east-1",
"s3.access_key" = "<ak>",
"s3.secret_key" = "<sk>"
);

В этом примере используется конечная точка AWS S3, содержащая бакет, с use_path_style=false. Текущая проверка свойств S3 в Doris требует указания конечной точки; при использовании показанного URI s3://my-bucket/... одного региона недостаточно. Обновляйте конечную точку при каждом изменении бакета или региона.

Для S3 и OSS значение warehouse должно включать бакет, например s3://bucket/path или oss://bucket/path.

REST Catalog​

REST Catalog получает Namespace, расположение таблиц и параметры доступа к хранилищу через Lance REST Namespace. Свойство warehouse для REST Catalog не требуется и не допускается.

СвойствоОбязательноеПо умолчаниюОписание
lance.rest.uriДа-URI REST-сервиса. Должен использовать http:// или https://.
lance.rest.security.typeНетnoneТип аутентификации. Допустимые значения: none, bearer и api_key.
lance.rest.bearer-tokenДа, для аутентификации Bearer-Bearer Token.
lance.rest.api-keyДа, для аутентификации API Key-API Key, передаваемый в заголовке x-api-key.
lance.rest.header.<header-name>Нет-Пользовательский HTTP-заголовок, отправляемый REST-сервису. Для заголовков аутентификации используйте специальные свойства аутентификации, указанные выше.

Аутентификация REST-сервиса и объектного хранилища настраивается отдельно: lance.rest.* обеспечивает подключение к сервису Namespace, а s3.* / oss.* позволяет FE и BE читать данные таблиц. URI таблицы, возвращаемый Namespace, определяет способ доступа: s3:// использует S3, а oss:// — нативный доступ к OSS. Выбирайте свойства хранилища по умолчанию в соответствии с возвращаемыми URI таблиц:

CREATE CATALOG lance_rest_s3 PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "rest",
"lance.rest.uri" = "https://lance.example.com",
"lance.rest.security.type" = "bearer",
"lance.rest.bearer-token" = "<token>",
"fs.s3.support" = "true",
"use_path_style" = "false",
"s3.endpoint" = "https://my-bucket.s3.us-east-1.amazonaws.com",
"s3.region" = "us-east-1",
"s3.access_key" = "<ak>",
"s3.secret_key" = "<sk>"
);

Для таблиц AWS s3://my-bucket/..., возвращаемых Namespace. Конечная точка по умолчанию должна включать этот бакет и соответствовать региону. Таблицам в разных бакетах нужны соответствующие конечные точки, как описано ниже.

Для доступа к REST без аутентификации опустите lance.rest.security.type и свойство аутентификации. Для аутентификации API Key замените их на "lance.rest.security.type" = "api_key" и "lance.rest.api-key" = "<api-key>".

Если REST Namespace предоставляет учётные данные или конечную точку в storage_options каждой таблицы, Doris по отдельности переопределяет соответствующие параметры Catalog по умолчанию и использует объединённую конфигурацию как в FE, так и в BE. Если предоставляются только учётные данные, конечная точка и способ доступа из Catalog продолжают применяться. Все свойства хранилища в Catalog можно опустить только тогда, когда Namespace предоставляет полную конфигурацию доступа.

Для S3 Namespace может использовать aws_endpoint, aws_region, aws_access_key_id, aws_secret_access_key, aws_session_token и aws_virtual_hosted_style_request. Последний параметр противоположен use_path_style в Doris: доступ virtual-hosted-style использует aws_virtual_hosted_style_request=true, а доступ path-style — false. fs.s3.support — свойство Catalog в Doris, а не параметр Lance storage_options.

Для нативного доступа к OSS Namespace может предоставлять oss_endpoint, oss_access_key_id, oss_secret_access_key, oss_region и oss_security_token либо соответствующие нативные имена endpoint, access_key_id, access_key_secret, region и security_token.

Если таблицы находятся в разных бакетах, не используйте для них общую конечную точку S3 virtual-hosted-style, привязанную к my-bucket, как в примере. Настройте Namespace так, чтобы он предоставлял соответствующую конечную точку и способ доступа для каждой таблицы, либо создайте отдельные Catalog для разных бакетов. Для сервисов, поддерживающих доступ path-style, можно также использовать конечную точку сервиса без бакета и задать use_path_style=true, чтобы клиент получал бакет из URI каждой таблицы.

Если REST Namespace управляет версиями таблицы (managed_versioning = true в DescribeTable), Doris определяет последнюю версию и любой селектор Time Travel через API версий Namespace и читает соответствующий манифест из хранилища. Подробности и ограничения см. в разделе Time Travel.

Отображение Namespace​

Lance поддерживает многоуровневые Namespace, а Doris Catalog представляет каждый Namespace как имя базы данных:

Lance NamespaceИмя базы данных Doris
Корневой Namespacedefault; настраивается через lance.namespace.root_database
dorisdoris
doris.analyticsdoris.analytics

Doris объединяет уровни многоуровневого Namespace через . и получает имя базы данных. При обращении к имени базы данных, содержащему ., используйте обратные кавычки:

SHOW TABLES FROM lance_catalog.`doris.analytics`;

SELECT *
FROM lance_catalog.`doris.analytics`.user_features;

Используйте lance.namespace.parent, чтобы ограничить Catalog поддеревом Namespace. Например:

CREATE CATALOG lance_analytics PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "filesystem",
"warehouse" = "s3://my-bucket/lance",
"lance.namespace.parent" = "production$analytics",
"fs.s3.support" = "true",
"s3.endpoint" = "https://my-bucket.s3.us-east-1.amazonaws.com",
"use_path_style" = "false",
"s3.region" = "us-east-1",
"s3.access_key" = "<ak>",
"s3.secret_key" = "<sk>"
);

После этого Doris отображает только таблицы и дочерние Namespace внутри production.analytics.

Запросы к таблицам Lance​

После создания Catalog вы можете просматривать таблицы Lance и выполнять к ним запросы так же, как к другим внешним таблицам:

SHOW DATABASES FROM lance_catalog;

SHOW TABLES FROM lance_catalog.default;

DESC lance_catalog.default.user_profiles;

SELECT user_id, name, age
FROM lance_catalog.default.user_profiles
WHERE age >= 18
ORDER BY user_id
LIMIT 100;

Также можно загрузить данные из Lance во внутреннюю таблицу Doris:

INSERT INTO internal.demo.user_profiles
SELECT user_id, name, age
FROM lance_catalog.default.user_profiles;

Для обычного запроса через Catalog Doris фиксирует версию набора данных Lance при планировании и создаёт задачи сканирования по Fragment. Поэтому запрос читает согласованный снимок, а несколько Scanner могут параллельно читать разные Fragment, не сканируя весь набор данных повторно в каждом Scanner.

Time Travel​

Каждый коммит в набор данных Lance создаёт новую версию; нумерация начинается с 1. Запрос может читать историческую версию вместо последней:

-- Read version 2 of the table.
SELECT * FROM lance_catalog.db.tbl FOR VERSION AS OF 2;

-- Read the latest version committed at or before the given time.
SELECT * FROM lance_catalog.db.tbl FOR TIME AS OF '2026-09-19 13:06:10';
  • FOR VERSION AS OF принимает положительный целочисленный номер версии или, как для таблиц Paimon и имён тегов в Iceberg, имя тега в кавычках (FOR VERSION AS OF 'v2' эквивалентно @tag(v2)).
  • FOR TIME AS OF принимает временную метку в часовом поясе сеанса с точностью до секунд или миллисекунд (yyyy-MM-dd HH:mm:ss или yyyy-MM-dd HH:mm:ss.SSS). Среди версий, зафиксированных не позднее указанной временной метки, Doris выбирает версию с самым поздним временем коммита, записанным в её манифесте (если несколько версий зафиксированы в один момент, выбирается более новая). Как и при выборе снимка по времени в Iceberg, времена коммитов сравниваются в том виде, в котором они записаны; не предполагается, что они возрастают вместе с номерами версий. Lance записывает время коммита с точностью выше миллисекунды, и оно сравнивается полностью: версия, зафиксированная позже в пределах запрошенной миллисекунды, не выбирается. При удалении версии очисткой удаляется и время её коммита. Поэтому, подобно тому как Iceberg удаляет журнал снимков перед истёкшим снимком, Doris рассматривает только версии новее последней удалённой. Временная метка, предшествующая всем этим версиям, вызывает ошибку: она относится либо ко времени до первой версии, либо к истории, удалённой очисткой (например, к промежутку между версией, сохранённой тегом, и следующей оставшейся версией), когда состояние таблицы неизвестно.
  • Выбранная версия фиксируется для всего оператора: схема, планирование Fragment, проталкивание предикатов и сканирование BE используют её. Два обращения к одной таблице в одном операторе могут выбирать разные версии. EXPLAIN показывает выбранную версию в lanceVersion.
  • Версии, удалённые cleanup_old_versions в Lance, читать нельзя; они отображаются так же, как никогда не существовавшие версии. Для таблиц, версиями которых управляет REST Namespace, см. ниже раздел об управлении версиями через REST Namespace.
  • vector_search() и full_text_search() выбирают версию, время, тег или ветку с помощью собственных параметров version, timestamp, tag и branch; см. Поиск в исторической версии или ветке. Их аргумент table не может содержать FOR VERSION AS OF, @tag или @branch.
  • Просмотр индексов (SHOW INDEX, lance_index_entries()) всегда использует последнюю версию main и не показывает индексы исторической версии или ветки.

Теги и ветки Lance выбираются с тем же синтаксисом, что и для таблиц Iceberg и Paimon:

-- Read the version the tag "v2" points at.
SELECT * FROM lance_catalog.db.tbl@tag(v2);

-- Read the latest version of the branch "dev".
SELECT * FROM lance_catalog.db.tbl@branch(dev);

-- Read version 2 of the branch "dev".
SELECT * FROM lance_catalog.db.tbl@branch(dev) FOR VERSION AS OF 2;
  • Тег указывает на версию в определённой ветке, и чтение по тегу выбирает эту версию этой ветки: тег, созданный в dev, читает dev, а не версию с тем же номером в main. @tag нельзя сочетать с FOR VERSION AS OF или FOR TIME AS OF.
  • @branch(main) читает main, как и запрос без @branch.
  • У каждой ветки своя последовательность версий, начинающаяся с версии, из которой она была создана. FOR VERSION AS OF / FOR TIME AS OF после @branch выбирают только среди версий этой ветки, поэтому временная метка до создания ветки ничего в ней не выбирает. После @branch, включая @branch(main), в FOR VERSION AS OF нельзя указывать имя тега, поскольку тег уже определяет свою ветку.
  • Ветка хранится в <table>/tree/<branch>/ как поверхностный клон таблицы, в манифестах которого расположение таблицы записано абсолютным URI. Поэтому ветка доступна для чтения только в месте её создания: после копирования или перемещения набора данных в другое место main остаётся доступной для чтения, а её ветки — нет.
  • Ветка, метаданные которой удалены или создание которой было прервано, остаётся доступной для чтения через @branch, пока её каталог в tree/ не будет очищен, поскольку Lance SDK открывает ветку по её каталогу. Для таблицы, версиями которой управляет REST Namespace, существование ветки определяет Namespace, а ветка читается из своего каталога без чтения main.
  • EXPLAIN показывает читаемую ветку в lanceBranch. Несуществующий тег или ветка вызывают ошибку.

Управление версиями через REST Namespace​

REST Namespace может самостоятельно управлять версиями таблицы (managed_versioning = true в ответе DescribeTable). Для такой таблицы Namespace определяет, какие версии существуют и какая из них последняя: последняя версия и FOR VERSION AS OF следуют записям Namespace (ListTableVersions, DescribeTableVersion); FOR TIME AS OF определяется по времени коммитов в манифестах с выбором только среди версий, перечисленных Namespace; тег читается из _refs/tags/ набора данных, как для любой таблицы Lance, и должен указывать на версию, зарегистрированную в Namespace; версии ветки также определяются Namespace. После выбора версии FE и BE читают её из хранилища по расположению таблицы и номеру версии, как для любой другой таблицы Lance.

  • Версия, не зарегистрированная в Namespace, считается ненайденной, даже если её манифест ещё существует в хранилище; последней считается последняя версия Namespace, даже если в хранилище уже есть более новый манифест.
  • Версия, которую Namespace больше не регистрирует и которая находится между зарегистрированными версиями, считается удалённой историей для FOR TIME AS OF, как версия, удалённая очисткой: временная метка, которую могла охватывать эта версия, вызывает ошибку вместо выбора более старой версии.
  • Таблица или ветка, для которой Namespace не регистрирует ни одной версии, вызывает ошибку, даже если в хранилище есть её манифесты.
  • EXPLAIN показывает lanceManagedVersioning=true для таких таблиц.

Ограничения:

  • Doris читает набор данных по table_uri, возвращаемому DescribeTable, или по location, если table_uri отсутствует; в обоих случаях требуется полный URI хранилища. Если location пусто или возвращённый вместе с ним table_uri указывает на другое место (за исключением строки запроса, например предварительно подписанных учётных данных), запрос завершается ошибкой и ничего не читает. URI s3+ddb также отклоняется: его обработчик коммитов DynamoDB записывал бы и завершал версии при чтении. Doris запрашивает описание управляемой таблицы при каждом чтении вместо кэширования, а FE и BE используют расположение и конфигурацию хранилища из полученного описания.
  • Doris читает версию по её номеру, по каноническому пути _versions/<u64::MAX - version>.manifest в каталоге таблицы (или в tree/<branch>/ для ветки) либо _versions/<version>.manifest в старой схеме именования Lance. Манифест, зарегистрированный Namespace для выбранной версии, должен находиться по этому пути либо быть промежуточным манифестом рядом с ним (<canonical path>-<id>); для FOR TIME AS OF это требование относится к манифестам всех зарегистрированных версий в данной main или ветке, поскольку выбор определяется временем их коммитов. Версию, зарегистрированную по другому пути, прочитать нельзя: запрос завершается ошибкой с указанием зарегистрированного и канонического путей. Если Namespace переместит таблицу во время выполнения запроса, запрос может завершиться такой же ошибкой, а при повторном запуске выполниться успешно.
  • Версию, для которой Namespace зарегистрировал только промежуточный манифест, а канонический манифест отсутствует, прочитать нельзя: её коммит не был завершён либо версия была удалена очисткой. Запрос завершается ошибкой с указанием обеих возможных причин. FOR TIME AS OF в этой main или ветке завершается ошибкой для любой временной метки, которую могла охватывать версия, поскольку время её коммита неизвестно; для самой новой версии это любая временная метка, пока модуль записи или другой модуль чтения, использующий Namespace, не завершит её. Чтение таблицы без FOR VERSION AS OF и загрузка её схемы в этом случае также завершаются ошибкой. Собственные реализации Namespace в Lance завершают версию до её регистрации, поэтому незавершённая версия появляется только после прерванного коммита или при использовании Namespace, регистрирующего промежуточные манифесты. Doris никогда не записывает данные в таблицу во время её чтения.

Просмотр индексов Lance​

Для таблицы в Filesystem Catalog оператор SHOW INDEX отображает логические скалярные индексы (включая инвертированные индексы FTS) и векторные индексы, записанные в наборе данных Lance. Варианты SHOW INDEXES, SHOW KEY и SHOW KEYS дают тот же результат:

SHOW INDEX FROM lance_catalog.default.items;

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

SHOW INDEX возвращает стандартный набор результатов из 13 столбцов. Для таблицы Lance значения содержат только следующие столбцы; остальные всегда пусты:

СтолбецЗначение для таблицы Lance
TableИмя таблицы.
Key_nameИмя логического индекса Lance.
Seq_in_indexПозиция столбца в индексе, начиная с 1. Индекс по нескольким полям создаёт отдельную строку для каждого поля.
Column_nameИндексируемое поле. Для индекса по вложенному полю отображается путь к полю, объединённый через ., а сегмент пути с символами, отличными от букв, цифр и _, заключается в обратные кавычки, например attributes.`child.with.dot`.
Index_typeТип индекса, сообщаемый Lance SDK, например BTree, INVERTED, IVF_FLAT, IVF_SQ, IVF_PQ, IVF_HNSW_FLAT, IVF_HNSW_SQ или IVF_HNSW_PQ.
PropertiesОбъект JSON с фиксированным набором сведений об индексе: metric_type и target_partition_size на верхнем уровне, type, num_bits, num_sub_vectors и rotation_type внутри compression, а также construction_ef, max_connections и max_level внутри hnsw. Ключи отсортированы; если сведения об индексе не содержат ни одного из этих полей, значение равно {}.

Следующие строки иллюстрируют векторный индекс IVF_PQ и индекс BTree по вложенному полю:

Table          Key_name              Seq_in_index  Column_name                  Index_type  Properties
vs_ivf_pq_f32 embedding_ivf_pq_f32 1 embedding IVF_PQ {"compression":{"num_bits":4,"num_sub_vectors":4,"type":"pq"},"metric_type":"L2"}
nested_index nested_label_btree 1 attributes.`child.with.dot` BTree {}

SHOW INDEX только просматривает существующие индексы и не создаёт их. Создавайте индексы Lance с помощью Lance SDK или другого модуля записи Lance вне Doris. Если записанные метаданные индексов противоречивы, например индекс ссылается на неизвестное поле или два индекса имеют одинаковое имя, оператор завершается ошибкой вместо возврата частичных метаданных.

SHOW INDEX требует привилегии SHOW для таблицы. Для REST Catalog оператор отклоняется с ошибкой SHOW INDEX is not supported for Lance REST catalogs.

Список физических записей индексов с lance_index_entries​

примечание

lance_index_entries поддерживается начиная с Apache Doris 4.1.5.

SHOW INDEX предоставляет логическое представление индексов таблицы Lance: имя индекса, индексируемые столбцы, тип индекса и свойства в фиксированном наборе результатов из 13 столбцов в стиле MySQL. Физическая идентификация записи индекса — её UUID и версия набора данных — не вписывается в эту фиксированную схему. Табличная функция lance_index_entries, доступная только для чтения, предоставляет такое физическое представление. Она возвращает по одной строке для каждой физической записи индекса, зарегистрированной в последнем снимке набора данных:

SELECT *
FROM lance_index_entries("table" = "lance_catalog.default.vs_ivf_pq_f32");

table должно быть полным именем из трёх частей catalog.database.table. Идентификаторы поддерживают заключение в обратные кавычки, поэтому, если многоуровневый Namespace отображается в имя базы данных, содержащее ., заключите часть с базой данных в обратные кавычки.

TVF возвращает следующие фиксированные столбцы:

СтолбецТипОписание
CatalogNameSTRINGИмя Catalog.
DatabaseNameSTRINGИмя базы данных.
TableNameSTRINGИмя таблицы.
IndexNameSTRINGИмя логического индекса Lance. Один логический индекс создаёт несколько строк, если у него есть несколько физических записей с разными UUID.
IndexUuidSTRINGUUID этой физической записи индекса.
DatasetVersionBIGINTВерсия набора данных, записанная в этой физической записи индекса, то есть версия набора данных на момент записи. Физические записи одного логического индекса могут содержать разные версии набора данных. Значение всегда равно 1 или больше.

Следующая строка иллюстрирует результат для показанного выше индекса IVF_PQ. Значения IndexUuid и DatasetVersion приведены для примера:

CatalogName    DatabaseName  TableName      IndexName             IndexUuid                             DatasetVersion
lance_catalog default vs_ivf_pq_f32 embedding_ivf_pq_f32 3f9c2a1e-7b4d-4e6f-9a1c-2d5e8f0b3a47 12

lance_index_entries читает только метаданные индексов через Dataset.getIndexes(). Функция не читает количество строк или статистику и не изменяет набор данных. Результат можно фильтровать обычными предикатами, например WHERE IndexName = .... Строки сортируются по (IndexName, IndexUuid). Чтение полностью проверяется и либо возвращает весь результат, либо завершается ошибкой: пустое или слишком длинное имя, отсутствующий UUID, неположительный DatasetVersion, повторяющийся UUID или исходное количество записей свыше 16,384 приводят к ошибке всего запроса вместо возврата усечённого результата. Зарезервированные системные индексы, которые Lance поддерживает внутренне, __lance_frag_reuse и __lance_mem_wal, проверяются, но не отображаются.

lance_index_entries требует привилегии SHOW для таблицы. Для REST Catalog TVF отклоняется с ошибкой lance_index_entries is not supported for Lance REST catalogs.

Сопоставление типов​

Полную таблицу сопоставления типов для Doris 4.2, правила точности и неподдерживаемые типы см. в разделе Сопоставление типов данных Lance и Doris.

Проталкивание предикатов​

Doris преобразует семантически совместимые предикаты в выражения Substrait и передаёт их Lance для вычисления при чтении. Doris BE не вычисляет условие повторно, если оно было полностью протолкнуто. Условия, которые нельзя безопасно протолкнуть, остаются в Doris.

Типы данных, поддерживающие проталкивание​

Тип Lance / ArrowПоддержка проталкивания
boolРавенство, проверки на null и логические операции; сравнения порядка не поддерживаются
int8/16/32/64Поддерживается
uint8/16/32/64Поддерживается
float32/64Поддерживается
decimal128Точность от 1 до 38, масштаб от 0 до значения точности
utf8, large_utf8Поддерживается
date32(day)Поддерживается
timestamp(s/ms/us) без часового поясаПоддерживается
list<utf8>Встроенная функция array_contains со строковым литералом, отличным от NULL; см. Предикаты для меток в массивах

Предикаты для других доступных для чтения типов, включая float16, decimal256, Binary, date64, Time, Timestamp с наносекундной точностью, Timestamp с часовым поясом и сложные типы, кроме описанной выше проверки вхождения в массив, в настоящее время остаются в Doris.

Операторы, поддерживающие проталкивание​

SQL-предикатУсловие проталкивания
=, !=, <>, <, <=, >, >=Прямое сравнение столбца с константой. Константа может находиться слева.
<=>Прямое сравнение столбца с константой на равенство с учётом null; сохраняет двухзначный результат, отличный от NULL, внутри NOT, AND или OR
IN, NOT INНепустой список констант без NULL
IS NULL, IS NOT NULLПрямая ссылка на столбец
Логический столбец, NOT логический столбецПрямая ссылка на логический столбец
LIKE, NOT LIKEНепосредственно столбец utf8 или large_utf8 и строковый литерал шаблона без экранирования обратной косой чертой, явного предложения ESCAPE или встроенного символа NUL
starts_with, ends_withВстроенная функция с непосредственным столбцом utf8 или large_utf8 и строковым литералом
array_contains(labels, 'red')Непосредственно столбец list<utf8> и строковый литерал, отличный от NULL
ANDКонъюнкты верхнего уровня могут проталкиваться независимо; неподдерживаемые конъюнкты остаются в Doris
ORОбе ветви должны полностью преобразовываться
NOTОперанд должен полностью преобразовываться

Следующие формы обычно не проталкиваются:

  • Функции, кроме поддерживаемых встроенных строковых функций и функций проверки вхождения в массив, а также арифметические выражения над столбцом. Пользовательская функция с именем like, starts_with, ends_with или array_contains не считается соответствующей встроенной функцией.
  • Строковые предикаты, шаблон которых содержит встроенный символ NUL. Шаблоны LIKE и NOT LIKE с экранированием обратной косой чертой или явным предложением ESCAPE также остаются в Doris.
  • Пустой список IN или список IN, содержащий NULL.
  • Выражение OR или NOT, в котором можно преобразовать только часть выражения.
  • Тип данных или значение константы, которые невозможно преобразовать в Lance без потерь.

Используйте lancePushdownPredicate в EXPLAIN, чтобы посмотреть, какие условия действительно проталкиваются:

EXPLAIN
SELECT user_id
FROM lance_catalog.default.user_profiles
WHERE active
AND starts_with(name, 'A')
AND country LIKE 'C%';

Предикаты для меток в массивах​

Начиная с Apache Doris 4.1.5 проталкивание предикатов для меток в массивах поддерживает array_contains над непосредственным столбцом Lance list<utf8> со строковым литералом, отличным от NULL. Более ранние сборки могут оставлять эти условия в Doris; проверьте EXPLAIN, прежде чем рассчитывать на эту оптимизацию. Массив NULL по-прежнему даёт NULL, пустой массив не соответствует условию, а повторяющиеся метки не дублируют строки результата.

Для таблицы Lance со столбцом id и столбцом labels типа list<utf8>:

EXPLAIN
SELECT id
FROM lance_catalog.default.items
WHERE array_contains(labels, 'red')
AND array_contains(labels, 'blue');

Это условие находит массивы, содержащие обе метки, независимо от их порядка и соседства. Замена AND на OR находит массивы с любой из меток. Выражение NOT (array_contains(labels, 'red')) также преобразуется и сохраняет семантику SQL NULL. Эти примеры относятся к обычному сканированию таблиц; они не меняют различие между параметром filter поисковой TVF и внешним условием WHERE.

Следующие предикаты остаются для обработки в Doris:

  • array_contains(labels, NULL): Doris может находить элемент NULL; функция проверки вхождения Lance иначе обрабатывает искомое значение NULL.
  • array_contains_all(labels, ['red', 'blue']): Doris проверяет упорядоченную непрерывную подпоследовательность. Её нельзя заменить функцией проверки включения множества в Lance или двумя независимыми предикатами вхождения.
  • Динамические искомые значения, выражения или приведения типов вокруг столбца массива, а также другие типы элементов и контейнеров массивов, включая large_list, списки фиксированного размера и list<large_utf8>.

Проталкивание предикатов и использование скалярных индексов​

Проталкивание предикатов означает, что условие вычисляет Lance; оно не гарантирует использования индекса. Для обычного сканирования Doris может назначить каждой задаче сканирования физический сегмент BTree, Bitmap или LabelList. Комбинации IN, AND, OR и NOT по одному столбцу могут использовать выбранный индекс, если Lance поддерживает выражение целиком. Для OR нужны безопасные кандидаты из обеих ветвей; NOT не может инвертировать результат неполностью вычисленного поддерева. Неподдерживаемые индексные выражения переходят к сканированию в пределах назначенных Fragment.

Каждая задача выбирает один логический скалярный индекс. Запрос, объединяющий предикаты по меткам, категориям и числам, не означает, что Doris пересекает все три индекса. Полное протолкнутое условие проверяется повторно до применения ограничения сканирования, а Fragment вне покрытия индекса всё равно читаются. Даже без пригодного индекса совместимые предикаты можно протолкнуть и вычислить при сканировании Lance.

Рассматривайте следующие сведения совместно:

СведенияЗначение
lancePushdownPredicate в EXPLAINУсловия, переданные Lance. Оставшиеся условия predicates: вычисляются в Doris.
lanceScalarIndexScan=SEGMENT и lanceGroupingIndex в EXPLAINЗапланировано сканирование скалярного сегмента; указан выбранный логический индекс. Само по себе это не доказывает использование индекса при выполнении.
LanceScalarIndexSegmentsSearched в ProfileФактические поиски по инструментированному пути скалярных сегментов.
LanceScalarIndexCandidateRows в ProfileСтроки-кандидаты, сообщаемые этим путём, не обязательно строки итогового результата. Маски дополнения, например в некоторых выражениях NOT, могут не сообщать количество кандидатов.
LanceScalarIndexSegmentFallbacks в ProfileЗадачи, отказавшиеся от запланированного пути скалярных сегментов в пользу резервного способа.

Включите enable_profile, чтобы просмотреть эти счётчики. При измерении эффекта оптимизации сравнивайте одинаковые SQL, проекцию, версию набора данных и состояние кэша. Эти счётчики описывают обычное сканирование скалярных сегментов; по ним одним нельзя судить об использовании скалярных индексов внутри ANN или предварительной фильтрации полнотекстового поиска.

Проталкивание Runtime Filter​

Когда обычная таблица Lance участвует в соединении, Doris может преобразовать некоторые Runtime Filter, созданные стороной построения соединения, в условия Lance SQL и применять их при чтении данных Lance. Это может уменьшить объём данных, возвращаемых в Doris, особенно при соединении большой таблицы с высокоселективной таблицей на стороне построения.

SELECT l.user_id, l.name
FROM lance_catalog.default.user_profiles l
JOIN internal.demo.allowed_users a
ON l.user_id = a.user_id;

В настоящее время можно проталкивать следующие Runtime Filter:

Runtime FilterФорма, передаваемая в Lance
IN Filtercolumn IN (...)
Min/Max Filtercolumn >= min_value, column <= max_value или оба условия

Столбец и значения Runtime Filter должны безопасно преобразовываться в скалярные типы, поддерживаемые Lance SQL. Другие типы Runtime Filter, IN Filter с NULL, а также типы данных или значения, которые нельзя безопасно преобразовать, не проталкиваются. Doris продолжает вычислять любой непротолкнутый Runtime Filter, поэтому корректность запроса сохраняется; теряется только выигрыш в производительности от ранней фильтрации в Lance.

В протолкнутые условия Lance Scanner можно включить только Runtime Filter, готовый до создания этого Scanner. Runtime Filter, поступивший позже, по-прежнему вычисляется в Doris, но может не попасть в уже созданный Scanner.

Условия Runtime Filter объединяются с условиями Substrait, созданными из обычных SQL-предикатов; они не заменяют друг друга. Запись runtime filters в EXPLAIN показывает запланированные Runtime Filter. Если протолкнут хотя бы один Runtime Filter, LanceRuntimeFilterPushedIds и LanceRuntimeFilterSkippedIds в Runtime Profile указывают идентификаторы протолкнутых и пропущенных фильтров.

Проталкивание Runtime Filter в настоящее время применяется только к обычному сканированию таблиц Lance. Оно не используется при формировании кандидатов в vector_search() или полнотекстовом поиске.

Запросы к Lance через файловые TVF​

Если вам нужно только прочитать набор данных Lance по известному пути, можно использовать TVF s3() или local() без создания Catalog. uri или file_path должны указывать на корневой каталог набора данных Lance, а не на внутренний файл данных.

TVF S3​

TVF s3() использует S3-совместимый API. Для OSS используйте свойства s3.* и конечную точку virtual-hosted-style, содержащую бакет. Для нативного доступа к OSS используйте приведённый выше пример OSS Catalog. Следующий SQL читает первые 10 строк примера набора данных; перед выполнением замените адрес, учётные данные и имена столбцов:

SELECT user_id, name
FROM s3(
"uri" = "s3://my-bucket/lance/user_profiles.lance",
"format" = "lance",
"fs.s3.support" = "true",
"use_path_style" = "false",
"s3.endpoint" = "https://my-bucket.s3.us-east-1.amazonaws.com",
"s3.region" = "us-east-1",
"s3.access_key" = "<ak>",
"s3.secret_key" = "<sk>"
)
ORDER BY user_id
LIMIT 10;

Для AWS S3 также явно задавайте конечную точку. В этом примере используется доступ virtual-hosted-style; бакет и регион в конечной точке должны соответствовать URI и s3.region.

Для OSS значение uri из примера также можно записать как oss://my-bucket/lance/user_profiles.lance, сохранив те же свойства s3.* и fs.s3.support=true. TVF нормализует URI в s3://; переключения на нативный провайдер OSS не происходит. Не копируйте напрямую конфигурацию oss.* из нативного OSS Catalog: конечная точка без бакета в сочетании с доступом virtual-hosted-style может привести к сообщению, что набор данных не существует, а переключение на доступ path-style может быть отклонено OSS с ошибкой SecondLevelDomainForbidden.

Для TVF S3 узел FE получает схему, текущую версию и список Fragment. Doris фиксирует эту версию и параллельно сканирует её Fragment. EXPLAIN показывает VTVF_SCAN_NODE; несколько частей сканирования могут иметь общий URI набора данных и различаться идентификаторами Fragment. totalFileSize=0 или length=0 — условное значение для частей сканирования Lance, а не признак пустого набора данных или повторного сканирования всей таблицы каждой частью.

Локальная TVF​

SELECT user_id, name
FROM local(
"file_path" = "/data/lance/user_profiles.lance",
"backend_id" = "10001",
"format" = "lance"
);

file_path передаётся без изменений в Lance Reader на целевом BE. Doris не добавляет к нему user_files_secure_path и не разворачивает этот путь как Glob, поэтому он должен напрямую указывать на корневой каталог одного набора данных Lance, доступного целевому BE. Рекомендуется абсолютный путь.

Определение схемы и выполнение локальной TVF независимо открывают последнюю версию набора данных. Версия, определённая при обнаружении схемы, в настоящее время не фиксируется для последующего сканирования. Если набор данных изменится между анализом и выполнением запроса, обнаруженная схема и сканируемый снимок могут различаться. Не изменяйте набор данных во время анализа и выполнения запроса локальной TVF. Текущая локальная TVF Lance использует один Scanner.

Файловые TVF Lance имеют следующие дополнительные ограничения:

  • Поддерживаются только s3() и local(). Другие файловые TVF, например HDFS и HTTP, в настоящее время не поддерживаются.
  • path_partition_keys не поддерживается.
  • Один путь TVF может представлять только один набор данных Lance.
  • DESC FUNCTION может отображать схему с неподдерживаемыми типами, но SQL не может включать неподдерживаемые столбцы в проекцию.

vector_search() — реляционная TVF, выполняющая поиск Top-K по векторному столбцу таблицы Lance. Она может использовать существующий векторный индекс Lance или выполнять Flat Search.

Синтаксис и пример​

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

Схема отношения vector_search() содержит все столбцы исходной таблицы Lance и столбец _distance, который Lance Scanner создаёт для запроса ближайших соседей. Итоговый результат SQL содержит только столбцы, включённые в проекцию SELECT. Doris предоставляет _distance с типом FLOAT. Это расстояние, а не универсальная оценка сходства: меньшее значение означает, что два вектора ближе друг к другу. Исходная таблица не должна уже содержать столбец с именем _distance. SQL-отношение не гарантирует итоговый порядок отображения, поэтому явно указывайте ORDER BY _distance ASC, когда требуется детерминированный порядок ближайших соседей. Для строк с одинаковым расстоянием рекомендуется добавлять уникальный столбец как дополнительный критерий сортировки.

table должно разбираться ровно на три части имени catalog.database.table. Многоуровневый Lance Namespace отображается в одно имя базы данных Doris, содержащее ., поэтому заключайте часть с базой данных в обратные кавычки. Например, для таблицы items в Namespace doris.analytics используйте следующее значение:

"table" = "lance_catalog.`doris.analytics`.items"

Не используйте форму без кавычек lance_catalog.doris.analytics.items: она разбирается как четыре части имени и отклоняется. Только имя таблицы или имя database.table также отклоняются.

Параметры​

ПараметрОбязательныйПо умолчаниюОписание
tableДа-Полное имя из трёх частей catalog.database.table. Если многоуровневый Namespace отображается в имя базы данных, содержащее ., заключите часть с базой данных в обратные кавычки. Имя должно указывать на таблицу в Lance Catalog, и у пользователя должна быть привилегия SELECT для этой таблицы.
columnДа-Имя векторного столбца. Поддерживает одновекторные столбцы fixed_size_list<float16|float32|float64|uint8|int8> и мультивекторные столбцы list<fixed_size_list<float16|float32|float64, D>>. См. Мультивекторный поиск.
query_vectorДа-JSON-массив чисел для одновекторного столбца или непустая JSON-матрица для мультивекторного столбца. Размерность каждого подвектора должна соответствовать столбцу, а каждое значение должно быть конечным и представимым типом элемента.
top_kНет10Количество результатов, возвращаемых после пропуска offset. Должно быть положительным целым числом.
offsetНет0Количество ближайших соседей, пропускаемых внутри векторного поиска. Должно быть неотрицательным целым числом. top_k + offset не должно превышать максимальное значение беззнакового 32-битного целого числа.
metricНетl2Метрика расстояния: l2, cosine, dot или hamming. dot_product — псевдоним dot. Векторы uint8 поддерживают только hamming; остальные поддерживаемые сейчас типы элементов векторов поддерживают l2, cosine и dot. Явно задавайте metric при запросе к индексу cosine, dot или hamming. Целочисленные мультивекторные столбцы и мультивекторный hamming не поддерживаются.
distance_lower_boundНетБез ограниченияВключительная нижняя граница расстояния для одновекторного поиска. Должна быть конечным значением FLOAT.
distance_upper_boundНетБез ограниченияИсключительная верхняя граница расстояния для одновекторного поиска. Должна быть конечным значением FLOAT. Если заданы обе границы, после преобразования в FLOAT нижняя должна быть меньше верхней.
filterНет-Условие Lance SQL, вычисляемое Lance. Момент выполнения определяется параметром prefilter.
prefilterНетfalseОпределяет момент выполнения TVF filter. При true фильтрация выполняется до формирования векторных кандидатов; при false (по умолчанию) — после формирования кандидатов, поэтому результат может содержать меньше top_k строк. Этот параметр не влияет на внешний WHERE в Doris.
nprobesНетМинимум 1, без максимумаКоличество разделов индекса IVF для проверки. Должно быть положительным целым числом. Если не задано, Lance начинает с одного раздела и может проверить дополнительные, если при prefilter=true остаётся слишком мало кандидатов. Явное значение N фиксирует и минимальное, и максимальное количество проверок равным N.
refine_factorНетОдновекторный поиск: отключено; мультивекторный: 1Множитель уточнения кандидатов. Должен быть положительным целым числом. Если не задан для одновекторного поиска, Lance не пересчитывает расстояния по исходным векторам, поэтому _distance из квантованного индекса может быть приближённым. При значении N Lance сначала извлекает (top_k + offset) × N кандидатов, пересчитывает их точные расстояния по исходным векторам и переупорядочивает их. Уточнение читает исходные векторные данные этих кандидатов. С ростом N читается и оценивается больше кандидатов, что может значительно увеличить ввод-вывод и снизить производительность запроса. Для одновекторного поиска значение 1 включает уточнение и отличается от отсутствующего параметра. Мультивекторный поиск всегда уточняет кандидатов по исходным значениям с множителем по умолчанию 1.
efНетfloor(1.5 × (top_k + offset))Количество кандидатов, сохраняемых при поиске по графу HNSW. Должно быть положительным целым числом. Если также задан refine_factor, значение по умолчанию равно floor(1.5 × (top_k + offset) × refine_factor). Не влияет на индексы без HNSW.
use_indexНетtrueПри true Doris предпочитает векторный индекс Lance, совместимый с векторным столбцом и метрикой расстояния, и автоматически использует Flat Search, если пригодного индекса нет. При false Doris отключает векторные индексы и выполняет Flat Search по данным.
version, timestamp, tag, branchНет-Поиск в исторической версии, версии на определённый момент времени, теге или ветке вместо последней версии main. См. Поиск в исторической версии или ветке.

Если metric не задан, выбирается l2 как для индексного поиска, так и для Flat Search. Для столбцов uint8 явно задавайте "metric" = "hamming"; значение по умолчанию l2 не поддерживается. Для мультивекторного поиска также действуют описанные ниже ограничения количества кандидатов.

Задайте distance_upper_bound для запроса по радиусу или объедините обе границы для интервала [lower, upper). Отсутствующие границы не ограничивают диапазон. Расстояния используют выбранную метрику: l2 — квадрат евклидова расстояния, поэтому геометрическому радиусу r соответствует верхняя граница r * r. Отрицательные границы допускаются, что полезно для метрик вроде dot. Границы используют точность FLOAT, даже если сохранённые векторы имеют тип Float64; неконечные границы, переполнение, равные границы и обратные интервалы отклоняются.

Например, пусть существует таблица Lance Catalog lance_catalog.default.items со столбцом user_id и трёхмерным столбцом Float32 embedding:

user_idembeddingКвадрат расстояния L2 до [0, 0, 0]
1[0, 0, 0]0
2[1, 0, 0]1
3[2, 0, 0]4
SELECT user_id, _distance
FROM vector_search(
"table" = "lance_catalog.default.items",
"column" = "embedding",
"query_vector" = "[0, 0, 0]",
"metric" = "l2",
"top_k" = "10",
"use_index" = "false",
"distance_lower_bound" = "1",
"distance_upper_bound" = "4"
)
ORDER BY _distance;
+---------+-----------+
| user_id | _distance |
+---------+-----------+
| 2 | 1 |
+---------+-----------+

Если убрать нижнюю границу, также вернётся строка 1. Если задать только верхнюю границу 0, строки не возвращаются. При prefilter=true параметр filter ограничивает кандидатов до векторного поиска; при prefilter=false по умолчанию фильтрует их после формирования. offset пропускает результаты после применения диапазона расстояний. Выходная схема не меняется: столбцы таблицы и _distance типа FLOAT. EXPLAIN отображает lanceDistanceRange=[1.0, 4.0).

Границы расстояния поддерживают параметры ? в подготовленных операторах. Каждое выполнение проверяет и использует собственные значения границ; границы NULL отклоняются. Опустите свойство, чтобы оставить соответствующую сторону неограниченной.

Это по-прежнему запрос Top-K, а не API для возврата всех строк в радиусе: результаты ограничены top_k и могут содержать меньше строк.

Для запросов по диапазону Doris использует индексный поиск только тогда, когда заданы обе границы и выбранный индекс явно определён как IVF_FLAT. Односторонние диапазоны, квантованные индексы и индексы с неизвестными подтипами переходят к точному Flat Search даже при use_index=true. EXPLAIN сообщает lanceVectorIndexStatus=DISTANCE_RANGE_FALLBACK, когда требования безопасности диапазона не позволяют выбрать индекс. Такой переход может увеличить затраты на сканирование.

Индексные запросы IVF_FLAT по-прежнему зависят от разделов, выбранных через nprobes; используйте use_index=false для исчерпывающей оценки кандидатов. Добавленные неиндексированные данные просматриваются через Flat Search. Мультивекторные столбцы не поддерживают границы расстояния.

Поддерживаемые типы векторных индексов​

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

Тип индексаОписаниеОсновные параметры запроса
IVF_FLATРазделы IVF с вычислением расстояний по исходным векторам внутри каждого разделаnprobes
IVF_SQIVF со Scalar Quantizationnprobes, refine_factor
IVF_PQIVF с Product Quantizationnprobes, refine_factor
IVF_HNSW_FLATIVF с HNSW, в котором узлы графа сохраняют исходные векторыnprobes, ef
IVF_HNSW_SQIVF и HNSW со Scalar Quantizationnprobes, ef, refine_factor
IVF_HNSW_PQIVF и HNSW с Product Quantizationnprobes, ef, refine_factor

vector_search() использует только векторные индексы, уже существующие в Lance. Функция не создаёт индексы и не позволяет пользователям задавать тип или имя индекса. При use_index=true Doris автоматически выбирает индекс, совместимый с векторным столбцом и метрикой расстояния. Данные без пригодного индекса, включая данные вне покрытия выбранного индекса, автоматически обрабатываются через Flat Search и не пропускаются. При use_index=false все данные обрабатываются через Flat Search.

Если со столбцом совместимы несколько индексов, Doris выбирает один на запрос — первый по имени индекса. Для Fragment, не охваченных этим индексом, используется Flat Search, даже если их охватывает другой индекс, поэтому выполнение всегда соответствует покрытию индекса, показанному в EXPLAIN. Индекс, с которым Doris не может построить план, например без метаданных покрытия Fragment (lanceVectorIndexStatus=UNKNOWN_COVERAGE), без записанной метрики (METRIC_MISMATCH) или старый индекс без сведений об индексе, которые Lance не может определить из его файлов и который Doris пропускает (NO_MATCH), не используется вовсе, а весь поиск выполняется как Flat Search. Старый индекс, сведения о котором Lance может определить, используется как обычно.

Поддерживаемые типы элементов векторов и метрики расстояния​

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

Тип элементов вектораl2cosinedothamming
float16Поддерживается [1]ПоддерживаетсяПоддерживаетсяНе поддерживается
float32ПоддерживаетсяПоддерживаетсяПоддерживаетсяНе поддерживается
float64ПоддерживаетсяПоддерживаетсяПоддерживаетсяНе поддерживается
uint8Не поддерживаетсяНе поддерживаетсяНе поддерживаетсяТолько IVF_FLAT и IVF_HNSW_FLAT [2]
int8Только Flat Search [3]Только Flat Search [3]Только Flat Search [3]Не поддерживается

Если в сноске не указано иное, «Поддерживается» означает, что сочетание работает со всеми шестью типами индексов, перечисленными выше. «Только Flat Search» означает, что к векторам можно выполнять запросы, но векторный индекс не используется.

  1. Построение индекса l2 по данным float16 с большими значениями может занять слишком много времени или завершиться ошибкой. Поддерживайте значения векторов в контролируемом диапазоне. Если создать индекс не удаётся, используйте Flat Search или, если это подходит приложению, метрику cosine.
  2. Lance рассматривает векторы uint8 как бинарные и поддерживает только метрику hamming. Product Quantization и Scalar Quantization не поддерживают uint8, поэтому доступны только IVF_FLAT и IVF_HNSW_FLAT.
  3. Векторы int8 в настоящее время не могут использовать векторный индекс и ограничены Flat Search.

Метрика запроса должна совпадать с метрикой индекса​

Параметр запроса metric должен совпадать с метрикой, использованной при построении индекса. Иначе, если запрошенная метрика поддерживается для типа столбца, Doris использует Flat Search. Результаты остаются корректными, но производительность обычно ниже, поскольку требуется прямое сканирование векторов.

Если metric не задан, Doris считает его равным l2 при выборе индекса. Поэтому явно задавайте metric при использовании индекса, построенного с cosine, dot или hamming. Например, векторный индекс uint8 требует "metric" = "hamming"; иначе запрос использует неподдерживаемую метрику l2 и завершается ошибкой.

Используйте EXPLAIN, чтобы подтвердить использование индекса. Значение lanceSearchIndexSegments больше 0 означает индексный поиск, а 0 — Flat Search.

Doris рассматривает только один векторный индекс для каждого векторного столбца. Храните не более одного векторного индекса на каждом векторном столбце, чтобы избежать неоднозначного выбора между индексами с разными метриками.

Мультивекторный столбец хранит несколько подвекторов в каждой строке таблицы, например эмбеддинги токенов документа. Матрица запроса даёт один ранжированный набор строк таблицы. Это не пакет независимых векторных поисков и не поиск по нескольким столбцам.

Подготовка данных и выполнение запроса​

Используйте приведённый выше пример Filesystem Catalog для AWS S3 с Catalog lance_fs_s3 и хранилищем s3://my-bucket/lance. Установите Lance Python SDK (pylance) и pyarrow в среде записи. Настройте в ней учётные данные S3 и регион, например через AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY и AWS_DEFAULT_REGION. Модулю записи нужен доступ на запись, а Doris — доступ на чтение к тому же расположению. Для пользовательских конечных точек или других способов аутентификации настройте параметры объектного хранилища SDK в соответствии с Catalog.

Создайте documents.lance непосредственно в каталоге хранилища, чтобы Doris обнаружил его как lance_fs_s3.default.documents. Замените бакет и в Catalog, и в примере Python. Используйте новое расположение набора данных:

import lance
import pyarrow as pa

vector_type = pa.list_(pa.float32(), 2)
schema = pa.schema([
pa.field("id", pa.int64()),
pa.field("embeddings", pa.list_(
pa.field("item", vector_type, nullable=False)
)),
])
data = pa.Table.from_pylist([
{"id": 1, "embeddings": [[1.0, 0.0], [0.0, 1.0]]},
{"id": 2, "embeddings": [[1.0, 0.0]]},
{"id": 3, "embeddings": []},
{"id": 4, "embeddings": None},
], schema=schema)
lance.write_dataset(
data,
"s3://my-bucket/lance/documents.lance",
data_storage_version="2.1",
)

Внешний список позволяет строкам содержать разное количество подвекторов. Внутренний параметр nullable=False обязателен: сам подвектор не может быть null. Выполните запрос к таблице с двумерным JSON-массивом:

SELECT id, _distance
FROM vector_search(
"table" = "lance_fs_s3.default.documents",
"column" = "embeddings",
"query_vector" = "[[1,0],[0,1]]",
"top_k" = "2",
"metric" = "l2",
"use_index" = "false"
)
ORDER BY _distance, id;

Ожидаемый результат:

id_distance
10.0
22.0

Строка 1 содержит точное совпадение для каждого подвектора запроса. Строка 2 совпадает с первым подвектором запроса с расстоянием 0, а со вторым — с квадратом расстояния L2, равным 2. Пустые внешние списки и внешние списки null не участвуют в ранжировании. Если добавить "offset" = "1" и изменить "top_k" = "1", вернётся только строка 2. Если добавить "filter" = "id > 1" и задать "prefilter" = "true", поиск кандидатов ограничится подходящими строками и для этого набора данных также вернёт только строку 2.

Оценка на уровне строки​

Для подвекторов запроса Q и сохранённых подвекторов строки V:

_distance = sum(q in Q, min(v in V, distance(q, v)))
metricРасстояние между двумя подвекторами
l2 (по умолчанию)Квадрат евклидова расстояния
cosine1 - cosine_similarity
dot или dot_product1 - dot_product; значение может быть отрицательным

Чем меньше _distance, тем лучше. Каждый подвектор запроса вносит вклад один раз, а один сохранённый подвектор может соответствовать нескольким подвекторам запроса. Оценки суммируются, а не усредняются; повторение подвектора запроса повторяет его вклад. _distance имеет тип Doris FLOAT, в том числе для входных данных Float64. top_k и offset считают строки таблицы, а не подвекторы. Для строк с одинаковой оценкой на границе Top-K не гарантируются ни включение в результат, ни порядок; внешний ORDER BY сортирует только возвращённые строки.

Типы, размерности и значения null​

ЭлементТребование
Столбец Lancelist<fixed_size_list<T, D>>, где D > 0, а T — float16, float32 или float64
Тип DorisARRAY<ARRAY<FLOAT>> для Float16/Float32; ARRAY<ARRAY<DOUBLE>> для Float64
Форма запросаНепустая JSON-матрица; каждый внутренний массив содержит ровно D элементов. Даже единственный подвектор запроса должен записываться как [[...]]. Обычные одновекторные столбцы по-прежнему требуют [...].
Значения запросаКонечные числа, представимые типом T. Пустые матрицы, несовпадающие размерности, значения null, нечисловые элементы и значения вне диапазона отклоняются.
Сохранённые значенияПодвекторы должны быть объявлены не допускающими null и содержать конечные элементы, отличные от null. При вычислении оценки фактические элементы null или неконечные элементы отклоняются при обнаружении; при этом сам флаг схемы, разрешающий null для элемента, допустим.
Пустые строки или строки nullВнешний список null или [] не содержит подходящего подвектора и исключается из результатов.
Нулевые нормы для cosineДля пары с нулевой нормой косинусное расстояние не определено, поэтому совпадения нет. Строка исключается, если хотя бы у одного подвектора запроса нет совпадения с определённым расстоянием. Поэтому подвектор запроса с нулевой нормой не даёт результатов.
Неподдерживаемые формы и типыВнешние large_list или fixed_size_list, внутренние списки переменной длины, целочисленные мультивекторные элементы, кодировки extension/dictionary и мультивекторный поиск hamming не поддерживаются.

Ограничения параметров​

Пусть M — количество подвекторов запроса, K = top_k, O = offset, а R = refine_factor (по умолчанию 1 для мультивекторного поиска).

ОграничениеПредел
Количество подвекторов запроса1 <= M <= 128
Количество результатов и смещениеK > 0, O >= 0
Бюджет кандидатов запросаM × (K + O) <= 100000
Бюджет кандидатов уточненияR × (K + O) <= 100000, при R > 0
Явно заданные nprobes, ef или refine_factorПоложительное знаковое 32-битное целое число, не более 2147483647; бюджеты кандидатов дополнительно ограничивают refine_factor

Оба бюджета кандидатов применяются независимо, в том числе при use_index=false. Например, M=128, K=1000, O=0 отклоняется из-за превышения бюджета кандидатов запроса. M=4, K=1000, O=0, R=10 удовлетворяет обоим бюджетам. Эти ограничения ограничивают работу с кандидатами, но не гарантируют определённого потребления памяти или времени выполнения запроса.

Использование индексов и уточнение​

Встроенная версия Lance поддерживает мультивекторные индексы с метрикой cosine. Чтобы использовать существующий совместимый индекс, например IVF_FLAT или IVF_PQ с метрикой cosine, задайте "metric" = "cosine" и оставьте use_index=true. Если metric не задан, выбирается l2; поиск L2 и dot использует Flat Search при отсутствии совместимого индекса. Doris не создаёт индексы через эту функцию. Используйте SHOW INDEX (только для Filesystem Catalog), чтобы просмотреть метаданные индексов, и EXPLAIN, чтобы проверить lanceSearchIndexSegments > 0.

При индексном поиске выбор кандидатов приближённый. Мультивекторные кандидаты всегда уточняются по исходным векторам, в том числе когда refine_factor не задан или равен 1, поэтому для индексированных и неиндексированных строк используется одна и та же оценка на уровне строки. Увеличение refine_factor расширяет набор кандидатов и требует дополнительного чтения векторов и вычислений; оно не гарантирует полной полноты поиска. nprobes управляет проверкой разделов IVF, а ef применяется к индексам HNSW. Настраивайте их только после измерения полноты поиска и задержки. Строки, добавленные после создания индекса, включаются через Flat Search.

Для исчерпывающей оценки выбранных строк используйте "use_index" = "false". Чтобы ограничить кандидатов до поиска, используйте TVF filter с prefilter=true; значение по умолчанию prefilter=false применяет условие после формирования кандидатов. Внешний SQL WHERE всегда вычисляется Doris по сформированным кандидатам и не пополняет результаты отдельной части сканирования. Правила фильтрации в следующем разделе применяются как к одновекторному, так и к мультивекторному поиску.

Предварительная и последующая фильтрация​

Предварительную и последующую фильтрацию можно использовать в одном запросе:

SELECT user_id, category, _distance
FROM vector_search(
"table" = "lance_catalog.default.items",
"column" = "embedding",
"query_vector" = "[0.1, 0.2, 0.3, 0.4]",
"top_k" = "10",
"prefilter" = "true",
"filter" = "category = 'book'"
)
WHERE user_id > 100
ORDER BY _distance ASC, user_id;
Тип фильтраПример условияКогда выполняетсяВлияние на результаты
Предварительный фильтр TVFПараметры TVF "filter" = "category = 'book'" и "prefilter" = "true"До формирования векторных кандидатов в LanceИщет ближайших соседей только среди строк, где category = 'book'.
Последующий фильтр TVFПараметр TVF "filter" = "category = 'book'", при этом prefilter не задан или равен falseПосле формирования кандидатов в LanceУдаляет неподходящие строки из сформированных кандидатов. Не пополняет кандидатов, поэтому результат может содержать меньше top_k строк.
Последующий фильтр сканирования DorisВнешнее условие WHERE user_id > 100После формирования кандидатов в Lance и до применения итогового TopN в DorisУдаляет неподходящие строки из сформированных кандидатов. Не пополняет кандидатов, поэтому итоговый результат может содержать меньше top_k строк.

Если user_id > 100 также должно участвовать в формировании кандидатов, включите его в TVF filter и задайте prefilter=true, например "filter" = "category = 'book' AND user_id > 100". При значении по умолчанию prefilter=false это условие применяется после формирования кандидатов. Даже если оптимизатор переместит внешний WHERE в Doris Lance Scan, он останется последующим фильтром сканирования Doris и не преобразуется в предварительный фильтр Lance.

Lance внутренне читает и вычисляет столбцы, на которые ссылается только filter. Если такой столбец не упоминается в SELECT или другом выражении Doris, возвращать его в Doris не требуется.

Кэш доступа к таблицам FE​

Для этой настройки требуется сборка Doris, содержащая изменение кэша доступа к таблицам.

Каждый FE может кэшировать определённый URI набора данных таблицы и параметры доступа в пределах Catalog, избегая повторного обнаружения через файловую систему или запросов REST describeTable при планировании запросов. Каждый клиент Catalog хранит не более 10,000 записей. Значение lance.table_access_cache_ttl_seconds по умолчанию равно 60; попадания в кэш не продлевают время жизни. Этот кэш не хранит версии наборов данных: каждое чтение по-прежнему открывает набор данных и выбирает его снимок.

Расположение таблицы с управлением версиями через Namespace определяется заново при каждом чтении, поэтому оба модуля чтения используют расположение и параметры хранилища, возвращённые Namespace для этого чтения. Ответ, содержащий предоставляемые параметры хранилища, также запрашивается заново при каждом чтении, даже если в нём указано expires_at_millis. URI с userinfo, параметрами запроса (включая предварительно подписанные учётные данные или SAS) либо фрагментами также не кэшируются. Фиксированный запас времени до истечения учётных данных не гарантирует их действительность на протяжении всего сканирования, а BE не может обновлять эти учётные данные во время сканирования. REST Catalog без этих полей ответа могут использовать кэш.

Явное обновление таблицы или базы данных очищает все записи доступа к таблицам в этом Catalog, в том числе на ведомых FE, воспроизводящих обновление, если соответствующий локальный объект базы данных или таблицы был вытеснен. Обычное вытеснение локального объекта базы данных не очищает кэш доступа. Обновление Catalog с инвалидацией кэша, удаление Namespace или изменение конфигурации Catalog также инвалидируют записи доступа. Просмотр индексов и проверка целевой таблицы задания индекса всегда определяют доступ заново.

После изменения URI удалённой таблицы или конфигурации доступа выполните явное обновление, чтобы не ждать истечения TTL. Для существующей таблицы example_lance.default.items:

REFRESH TABLE example_lance.default.items;

При следующем чтении доступ к таблице определяется заново. Это не очищает нативный кэш Lance Session; после замены набора данных по тому же URI выполните обновление Catalog с инвалидацией кэша. Такое обновление очищает Lance Session только в FE, но не кэши BE; см. Поиск в исторической версии или ветке.

Чтобы отключить кэширование доступа к таблицам для существующего Catalog:

ALTER CATALOG example_lance SET PROPERTIES (
"lance.table_access_cache_ttl_seconds" = "0"
);

Последующие чтения каждый раз определяют доступ к таблице заново. Описанные ниже кэши BE по-прежнему настраиваются независимо.

Кэш Lance​

Кэш Lance повторно использует содержимое индексов, метаданных и файлов данных, сокращая повторные чтения. Каждый BE управляет собственным кэшем, общим для запросов Lance на этом BE. Ёмкость и содержимое кэша не разделяются между BE. Обычное сканирование, векторный поиск, полнотекстовый поиск и извлечение второй фазы могут повторно использовать соответствующие кэши без дополнительных свойств CREATE CATALOG.

Типы кэшей и область действия​

КэшХранениеКэшируемое содержимое
Кэш индексовПамять BEЗагруженное содержимое индексов Lance, сокращающее повторные загрузки индексов.
Кэш метаданныхПамять BEМетаданные, используемые модулем чтения Lance, сокращающие повторное чтение и разбор.
Кэш данныхЛокальный диск BE с уровнем в памяти размером в один блок чтенияДанные из файлов с расширением .lance, расположенных непосредственно в каталоге data/ набора данных, кэшируемые блоками.

Кэш данных использует Foyer внутри модуля чтения Lance; его собственный каталог и ёмкость настраиваются параметрами BE lance_*, приведёнными ниже. Он не использует общие настройки каталога или ёмкости File Cache в Doris. Файлы индексов, манифесты и файлы удалений обходят этот дисковый кэш данных и продолжают использовать существующие пути чтения и кэширования Lance.

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

Конфигурация BE​

Все параметры ниже необязательны. Задайте их в be.conf каждого BE и перезапустите этот BE, чтобы изменения вступили в силу. Ёмкости задаются для каждого BE в байтах.

ПараметрТипПо умолчаниюОписание
lance_index_cache_size_bytesInt6410737418240 (10 GiB)Ёмкость кэша индексов в памяти. Задайте 0 для нулевой ёмкости.
lance_metadata_cache_size_bytesInt641073741824 (1 GiB)Ёмкость кэша метаданных в памяти. Задайте 0 для нулевой ёмкости.
enable_lance_data_cacheBooleantrueВключает дисковый кэш данных. При его отключении кэши индексов и метаданных сохраняются.
lance_data_cache_pathString${DORIS_HOME}/lance_data_cacheКаталог кэша данных. Процессу BE нужны права на создание каталога, чтение и запись файлов. Используйте отдельные каталоги для разных процессов BE.
lance_data_cache_disk_capacity_bytesInt64107374182400 (100 GiB)Ёмкость дискового кэша. Должна быть кратна 4096 и составлять не менее удвоенного размера блока чтения.
lance_data_cache_read_block_size_bytesInt641048576 (1 MiB)Размер блока чтения кэша данных. Должен быть положительным и кратным 4096. Также задаёт ёмкость уровня кэша данных в памяти.

Например, явно настройте ёмкости по умолчанию и разместите кэш в отдельном каталоге локального диска:

lance_index_cache_size_bytes = 10737418240
lance_metadata_cache_size_bytes = 1073741824
enable_lance_data_cache = true
lance_data_cache_path = /mnt/ssd/lance_data_cache
lance_data_cache_disk_capacity_bytes = 107374182400
lance_data_cache_read_block_size_bytes = 1048576

После перезапуска BE общий кэш инициализируется при первом открытии набора данных Lance. Если кэш данных включён, недоступный каталог, недопустимая ёмкость или размер блока приводят к сбою инициализации, а запросы Lance возвращают ошибку. Автоматического перехода к работе без кэша данных нет. Чтобы отключить дисковый кэш данных, задайте enable_lance_data_cache равным false и перезапустите BE.

Планируйте память для кэшей индексов и метаданных вместе с рабочей памятью запросов. Эти ёмкости не ограничивают общую память запросов Lance или процесса BE. Дисковый кэш также занимает место на локальном диске и использует файловые дескрипторы. Более крупные блоки чтения уменьшают количество блоков, но могут увеличить избыточный ввод-вывод при небольших чтениях. При изменении размера блока чтения или дисковой структуры используйте новый каталог кэша.

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

Проверка эффективности кэша​

Просмотрите счётчики кэша индексов в LanceReader → LanceIndex и счётчики кэша файлов данных в LanceReader → LanceDataCache в Profile завершённого запроса:

СчётчикЗначение
LanceIndexCacheHits, LanceIndexCacheMissesОбращения к кэшу, зарегистрированные инструментированными путями работы с индексами для этого выполнения. Они отличаются от метрик Session для всего BE.
LanceIndexObjectLoads, LanceIndexComponentsLoadedЗагруженные объекты индексов верхнего уровня и внутренние компоненты. Ни один из этих счётчиков не эквивалентен промахам кэша.
LanceDataCacheBytesReadFromCacheЛогическое количество запрошенных байтов, предоставленных кэшем файлов данных для этого дескриптора набора данных.
LanceDataCacheBytesReadFromRemoteЛогическое количество запрошенных байтов, предоставленных исходным хранилищем после промахов или обхода кэша. Источником также может быть локальный файл.

Оба счётчика байтов кэша данных не учитывают увеличение объёма чтения из-за блоков и ввод-вывод в обход кэша данных, например чтение индексов и манифестов. Они не являются полными счётчиками удалённого трафика, а их сумма не обязана совпадать с LanceIOReadBytes. Оба равны 0, когда кэш данных отключён, что не означает отсутствия чтений из источника. Doris фиксирует накопленный снимок статистики дескриптора набора данных перед его закрытием. Извлечение второй фазы имеет отдельную статистику чтения, которая также экспортируется в сведения Profile для Row ID Fetch/Materialization; одни счётчики сканирования не охватывают весь ввод-вывод извлечения запроса. Моменты сбора и границы метрик см. в Справочнике Profile запросов Lance.

Конечная точка BE /metrics также предоставляет метрики с префиксами doris_be_lance_session_index_cache_ и doris_be_lance_session_metadata_cache_. Суффиксы capacity_bytes, usage_bytes, entries, hits_total и misses_total обозначают настроенную ёмкость, текущее использование, количество записей, накопленные попадания и накопленные промахи. Эти метрики регистрируются после инициализации общей Lance Session и описывают кэш всего BE, а не отдельного запроса.

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

Двухфазное чтение TopN и отложенная материализация​

vector_search() и full_text_search() обычно формируют больше строк-кандидатов, чем итоговый top_k. Чтение широких выходных столбцов, таких как title или payload, во время поиска расходует ресурсы впустую, поскольку большинство кандидатов отбрасывается TopN. Двухфазное чтение откладывает эти столбцы: фаза 1 читает только столбцы, необходимые для фильтрации, сортировки и TopN; после глобального TopN фаза 2 читает остальные выходные столбцы только для сохранённых строк.

Это сокращает ввод-вывод хранилища, сетевую передачу и потребление памяти. Выигрыш обычно наибольший при небольшом top_k, большом количестве кандидатов поиска или возврате широких строковых, JSON и подобных столбцов.

ФазаЧитаемые данные
Фаза 1Столбцы, используемые поиском внутренне, _distance или _score, столбцы для последующей фильтрации или сортировки и внутреннее расположение строки Row Location.
Фаза 2Столбцы верхнего уровня, используемые только итоговой проекцией; читаются только для строк, сохранённых глобальным TopN.

Условия включения​

Doris использует двухфазное чтение только при выполнении всех следующих условий:

  1. Запрос читает Lance через vector_search() или full_text_search(), а его план содержит TopN, подходящий для отложенной материализации. Этот переключатель в настоящее время не применяется к обычному сканированию таблиц Lance или файловым TVF Lance.
  2. Логическая переменная сеанса enable_lance_lazy_materialization равна true, своему значению по умолчанию.
  3. Чтение хотя бы одного столбца верхнего уровня можно отложить. Можно отложить столбец, используемый только итоговой проекцией SELECT. Столбец, используемый последующим фильтром, ORDER BY или другим выражением до TopN, должен читаться в фазе 1. Чтение вложенных подстолбцов пока откладывать нельзя.

enable_lance_lazy_materialization не зависит от topn_lazy_materialization_threshold: Lance может использовать двухфазное чтение, даже если последний равен -1 или top_k превышает этот порог. Другие таблицы с поддержкой отложенной материализации продолжают следовать общему порогу. Включение переключателя разрешает оптимизацию; применимость по-прежнему определяется структурой плана и подходящими столбцами.

В следующем запросе category должен читаться в фазе 1 для последующего фильтра, а user_id, title и payload можно получить после TopN:

SET enable_lance_lazy_materialization = true;

SELECT user_id, title, payload, _distance
FROM vector_search(
"table" = "lance_catalog.default.items",
"column" = "embedding",
"query_vector" = "[0.1, 0.2, 0.3, 0.4]",
"top_k" = "10",
"offset" = "3"
)
WHERE category = 'book';

Столбцы, упомянутые в TVF filter, используются Lance внутренне. Само присутствие в строке filter не делает их результирующими столбцами фазы 1, возвращаемыми в Doris; prefilter меняет только момент применения условия относительно формирования кандидатов.

Проверка плана​

Добавьте перед приведённым выше запросом EXPLAIN VERBOSE. Когда двухфазное чтение активно, план включает VMaterializeNode, в котором column_descs_lists перечисляет столбцы, извлекаемые в фазе 2. Отключите переключатель и снова выполните EXPLAIN VERBOSE для того же простого поискового запроса; соответствующий узел материализации должен исчезнуть. Если он отсутствует при включённом переключателе, проверьте, не используются ли выходные столбцы в фильтрации, сортировке или других вычислениях до TopN и поддерживает ли план эту оптимизацию.

Отключение двухфазного чтения​

Чтобы отключить двухфазное чтение, используйте следующую настройку сеанса:

SET enable_lance_lazy_materialization = false;

Doris использует однофазное чтение, когда нельзя отложить ни один столбец или план не поддерживает оптимизацию. Само по себе большое top_k не отключает двухфазное чтение для Lance автоматически. Рассмотрите отключение и сравнение производительности в следующих случаях:

  • top_k велик, поэтому фаза 2 всё равно читает большинство строк-кандидатов.
  • Запрос возвращает лишь несколько узких столбцов, и двухфазное чтение почти не сокращает ввод-вывод.
  • Случайные чтения по Row-ID имеют высокую задержку в используемом хранилище, а дополнительные затраты извлечения фазы 2 превышают экономию от отложенного чтения.

Отключение этой оптимизации меняет только момент чтения выходных столбцов. Оно не отключает векторные индексы или индексы FTS и не меняет семантику предварительной фильтрации, последующей фильтрации или TopN. Сравните обе настройки на характерных запросах и в типичных условиях хранения.

full_text_search() — реляционная TVF, выполняющая поиск BM25 через существующий инвертированный индекс FTS таблицы Lance. Она поддерживает объединение проанализированных поисковых термов через OR или AND, а также сопоставление их порядка и расстояния с помощью запросов Phrase. Doris объединяет кандидатов из физических сегментов индекса в глобальный Top-K и добавляет _score к исходным столбцам. Более высокая оценка означает большую релевантность.

Перед использованием TVF создайте инвертированный индекс FTS для целевого строкового столбца с помощью Lance SDK или другого модуля записи Lance. Для запросов Phrase также необходимо включить сохранение позиций токенов при создании индекса, например задав with_position=true в параметрах индекса Lance. Doris не создаёт этот индекс и не переходит к построчному полнотекстовому сканированию, если индекс FTS отсутствует.

Синтаксис и пример​

Следующий запрос ищет lance database среди документов категории storage, а затем применяет published = true как последующий фильтр Doris:

SELECT document_id, title, _score
FROM full_text_search(
"table" = "lance_catalog.default.documents",
"column" = "body",
"query" = "lance database",
"query_type" = "match",
"operator" = "and",
"top_k" = "10",
"offset" = "0",
"prefilter" = "true",
"filter" = "category = 'storage'",
"coverage_mode" = "strict"
)
WHERE published = true
ORDER BY _score DESC, document_id;

В этом примере используется Match AND, поэтому документ должен содержать оба проанализированных терма: lance и database. При prefilter=true Lance вычисляет filter до формирования кандидатов FTS. Если prefilter не задан, его значение по умолчанию равно false, и Lance применяет filter после формирования кандидатов; результат может содержать меньше top_k строк. Внешний WHERE — последующий фильтр Doris, который не пополняет удалённых кандидатов. Если published = true должно влиять на формирование кандидатов, включите его в filter и задайте prefilter=true.

Следующий запрос Phrase допускает не более одного промежуточного токена между соседними термами запроса. Например, ему может соответствовать lance search engine:

SELECT document_id, title, _score
FROM full_text_search(
"table" = "lance_catalog.default.documents",
"column" = "body",
"query" = "lance engine",
"query_type" = "phrase",
"slop" = "1",
"top_k" = "10"
)
ORDER BY _score DESC, document_id;

Возвращаемое отношение содержит все столбцы исходной таблицы Lance и допускающий null столбец FLOAT с именем _score. Исходная таблица не должна уже содержать столбец с таким именем. TVF выбирает свой Top-K по убыванию _score, но SQL-отношение не гарантирует итоговый порядок отображения. Явно используйте ORDER BY _score DESC, когда нужен стабильный порядок, и добавляйте уникальный дополнительный критерий сортировки для одинаковых оценок.

Параметры​

ПараметрОбязательныйПо умолчаниюОписание
tableДа-Полное имя из трёх частей catalog.database.table, указывающее на таблицу в Lance Catalog. Если многоуровневый Namespace отображается в имя базы данных, содержащее ., заключите часть с базой данных в обратные кавычки. У пользователя должна быть привилегия SELECT для таблицы.
columnДа-Строковый столбец с инвертированным индексом FTS. Поддерживаются Arrow utf8 и large_utf8, которые отображаются в Doris TEXT.
queryДа-Непустой поисковый текст. Обрабатывается токенизатором, настроенным при создании индекса FTS.
query_typeНетmatchТип запроса. Допустимые значения: match и phrase.
operatorНетorТолько для запросов Match. or требует совпадения хотя бы одного терма запроса; and — всех термов. Для запросов Phrase этот параметр задавать нельзя.
slopНет0Только для запросов Phrase. Целое число от 0 до 2147483647. 0 требует точной фразы; положительное значение задаёт максимальное количество промежуточных токенов между соседними термами запроса. Для запросов Match этот параметр задавать нельзя.
top_kНет10Количество результатов, возвращаемых после пропуска offset. Должно быть положительным целым числом.
offsetНет0Количество результатов, пропускаемых в порядке релевантности. Должно быть неотрицательным целым числом; top_k + offset не должно превышать максимальное значение беззнакового 32-битного целого числа.
filterНет-Условие Lance SQL. Момент выполнения определяется параметром prefilter.
prefilterНетfalseОпределяет момент выполнения TVF filter. При true фильтрация выполняется до формирования полнотекстовых кандидатов; при false (по умолчанию) — после формирования кандидатов, поэтому результат может содержать меньше top_k строк. Этот параметр не влияет на внешний WHERE в Doris.
coverage_modeНетstrictПоведение, когда индекс FTS охватывает не все Fragment текущего снимка. Допустимые значения: strict и index_only.
version, timestamp, tag, branchНет-Поиск в исторической версии, версии на определённый момент времени, теге или ветке вместо последней версии main. См. Поиск в исторической версии или ветке.

Типы запросов​

Тип запросаПравила сопоставленияСвязанные параметрыТребование к индексу
Match ORСовпадает хотя бы один проанализированный терм запроса. Используется по умолчанию.query_type=match, operator=orСтандартный инвертированный индекс FTS.
Match ANDСовпадают все проанализированные термы запроса.query_type=match, operator=andСтандартный инвертированный индекс FTS.
PhraseСохраняет порядок проанализированных термов запроса и применяет расстояние, заданное slop.query_type=phrase, slop=NИндекс FTS должен хранить позиции токенов.

Запросы FTS Boolean, Boost, Prefix, Wildcard, Regex и составные запросы по нескольким полям в настоящее время не поддерживаются.

Режимы покрытия индекса​

РежимПоведениеКогда использовать
strictТребует, чтобы выбранный индекс FTS охватывал все Fragment зафиксированного снимка запроса. Запрос завершается ошибкой, если хотя бы один Fragment не охвачен.Используется по умолчанию и рекомендуется, когда результаты не должны пропускать данные. После добавления данных обновите или перестройте индекс FTS перед выполнением запроса.
index_onlyВыполняет поиск только по Fragment, охваченным индексом FTS, и игнорирует неохваченные данные.Используйте только тогда, когда допустимы неполные результаты, а поиск должен оставаться доступным в ожидании обновления индекса.

Оба режима требуют зафиксированного индекса FTS с полными метаданными покрытия для целевого столбца. Запрос завершается ошибкой, если пригодного индекса нет. Храните только один логический индекс FTS на столбце; если подходят несколько индексов, Doris не может однозначно выбрать один и отклоняет запрос.

Используйте EXPLAIN для проверки выполнения: lanceFtsQueryType показывает Match или Phrase, lanceFtsMatchOperator — оператор Match OR/AND, а lanceFtsPhraseSlop — расстояние Phrase. lanceFtsCoverageMode показывает режим покрытия, lanceSearchIndexSegments — количество используемых физических сегментов индекса FTS, а lanceSearchUnindexedFragments — сколько Fragment текущего снимка не охвачены индексом.

Поиск в исторической версии или ветке​

По умолчанию vector_search() и full_text_search() выполняют поиск в последней версии main. Четыре необязательных параметра выбирают другой снимок и имеют тот же смысл, что и синтаксис таблицы Time Travel:

ПараметрЗначениеЭквивалентный синтаксис таблицыМожно сочетать с
versionПоложительный целочисленный номер версииFOR VERSION AS OF Nbranch
timestampyyyy-MM-dd HH:mm:ss или yyyy-MM-dd HH:mm:ss.SSS в часовом поясе сеансаFOR TIME AS OF '...'branch
tagИмя тега@tag(name)Ничем
branchИмя ветки; main — основная цепочка@branch(name)version или timestamp
-- Search version 3.
SELECT id, _distance
FROM vector_search(
"table" = "lance_catalog.db.documents",
"column" = "embedding",
"query_vector" = "[0.1, 0.2, 0.3]",
"version" = "3")
ORDER BY _distance;

-- Search the branch "dev" as it was at a point in time.
SELECT id, _score
FROM full_text_search(
"table" = "lance_catalog.db.documents",
"column" = "content",
"query" = "database",
"branch" = "dev",
"timestamp" = "2026-09-20 12:00:00")
ORDER BY _score DESC;
  • Снимок выбирается по тем же правилам, что и в синтаксисе таблицы, включая теги, указывающие на ветку, и таблицы, версиями которых управляет REST Namespace. version принимает только номер версии. В отличие от FOR VERSION AS OF, он не принимает имя тега, поскольку tag однозначно задаёт тег: тег с именем 123 выбирается через "tag" = "123".
  • version и timestamp нельзя сочетать, а tag нельзя сочетать ни с одним из остальных трёх параметров. Параметр с пустым значением вызывает ошибку, а не игнорируется.
  • Выходная схема, поисковый столбец, метаданные векторного индекса и индекса FTS, а также Fragment берутся из выбранного снимка. Формирование кандидатов, фильтрация, Top-K и двухфазное чтение используют этот снимок. Историческая версия использует индексы, зафиксированные в ней: версия до создания векторного индекса использует Flat Search, версия до создания индекса FTS недоступна для поиска через full_text_search(), а coverage_mode проверяет Fragment этой версии.
  • Снимок, который невозможно выбрать или файлы индексов которого отсутствуют, вызывает ошибку. Поиск никогда не переходит к последней версии. Версия, удалённая очисткой Lance, считается ненайденной. Версия, сохранённая тегом, сохраняет свои файлы индексов при очистке; если файлы индексов удалены другим способом, выполнение завершается ошибкой с указанием версии набора данных.
  • Снимок выбирается заново при каждом выполнении, включая каждый EXECUTE подготовленного оператора, поэтому последняя версия, время в будущем, ветка и перемещённый тег могут каждый раз выбирать другую версию. Все четыре параметра должны быть константами: в подготовленном операторе vector_search() принимает заполнители (?) только для query_vector, top_k, offset и filter, а full_text_search() не принимает их вовсе.
  • Каждое поисковое отношение выбирает собственный снимок. Два отношения к одной таблице в одном операторе, например vector_search(), соединённая с самой таблицей, могут читать разные версии, если между ними зафиксирована новая версия. Чтобы читать одну и ту же, зафиксируйте обе: version для поисковой функции и FOR VERSION AS OF для таблицы.
  • CREATE TABLE AS SELECT планирует запрос дважды: сначала для схемы новой таблицы, затем для её строк, поэтому подвижный селектор может каждый раз выбрать другую версию. Чтобы точно скопировать одну версию, задайте version.
  • EXPLAIN показывает выбранную версию в lanceVersion, ветку в lanceBranch, а управление версиями через REST Namespace — в lanceManagedVersioning. Поскольку EXPLAIN заново выбирает снимок, Profile запроса фиксирует, что действительно прочитало выполнение: LanceDatasetVersion и LanceDatasetUri, отображаемые вместе с другими сведениями Scanner Lance, например LanceSearchType. URI включает каталог ветки и не содержит строки запроса и пароля.
  • Ветка, созданная из другой ветки или поверхностного клона, может не найти унаследованные файлы индексов, поскольку модули записи Lance до версии 13 регистрируют их под промежуточной веткой. Тогда поиск завершается ошибкой; собственные индексы ветки это не затрагивает.
  • После удаления и повторного создания ветки под тем же именем или замены набора данных по тому же URI новые данные повторно используют старые номера версий. BE может продолжать использовать закэшированные данные для этих номеров версий, пока записи не будут вытеснены или BE не перезапустится. В наборе данных со стабильными идентификаторами строк векторный и полнотекстовый поиск могут фильтровать по актуальным строкам старых данных, поэтому результаты могут включать строки, удалённые в новых данных, или пропускать существующие строки. Если манифест использует старую схему именования Lance (_versions/<version>.manifest) или объектное хранилище не возвращает ETag, BE и FE также могут использовать старые метаданные индексов; FE прекращает это после обновления Catalog. Дайте повторно созданной ветке новое имя или перезапустите BE. Обновление Catalog не очищает эти кэши BE.
  • Индекс FTS, построенный без позиций токенов, не может выполнять запросы Phrase. Индексы, построенные старыми модулями записи и всё ещё используемые исторической версией, часто не содержат позиций.

Текущая модель выполнения​

Порядок выполнения vector_search() и full_text_search():

Dataset snapshot selected by version / timestamp / tag / branch, or the latest version of main
-> FE search Split planning
-> Vector search: physical vector Index Segments + Flat Search for uncovered Fragments
-> Full-text search: physical FTS Index Segments (coverage checked by coverage_mode)
-> Per Split: vector/full-text search; the TVF `filter` runs before candidate generation when `prefilter=true`, and after candidate generation when `prefilter` is omitted or `false`
-> at most K+n candidates
-> Doris Scan Post-filter
-> Doris local TopN
-> Exchange
-> Doris global TopN (applies offset=n and limit=K)
-> Optional lazy-materialization Fetch

Profile запроса​

Сбор и просмотр Profile описаны в Руководстве по анализу Profile запросов. Группы, счётчики, границы измерения времени и статистика извлечения второй фазы, специфичные для Lance, описаны в Справочнике Profile запросов Lance.

Текущие ограничения и рекомендации​

  • Lance Catalog и TVF Lance доступны только для чтения. CREATE TABLE, INSERT, UPDATE, DELETE, TRUNCATE TABLE и запись данных обратно в Lance не поддерживаются.
  • Для таблиц с неподдерживаемыми типами столбцов явно перечисляйте читаемые столбцы вместо включения неподдерживаемых столбцов через SELECT *.
  • Для обычного сканирования проверяйте lancePushdownPredicate в EXPLAIN, чтобы узнать, какие условия были протолкнуты.
  • Перед индексным векторным поиском создайте в Lance векторный индекс, соответствующий предполагаемому запросу. Для небольших наборов данных или проверки задайте "use_index" = "false", чтобы выполнить Flat Search.
  • Перед полнотекстовым поиском создайте в Lance инвертированный индекс FTS. Режим strict по умолчанию отклоняет запрос, если индекс охватывает не все Fragment текущего снимка; используйте index_only только тогда, когда пропуск данных допустим.
  • Для запросов Phrase индекс FTS должен хранить позиции токенов. Если нужны запросы Phrase, задайте with_position=true при создании индекса.
  • Используйте SHOW INDEX для таблицы Filesystem Catalog, чтобы просмотреть её логические индексы Lance, например проверить имя, тип и индексируемые поля перед векторным или полнотекстовым поиском. Для REST Catalog оператор SHOW INDEX не поддерживается.
  • Для детерминированного порядка результатов векторного поиска явно используйте ORDER BY _distance ASC и добавляйте уникальный дополнительный критерий сортировки.
  • Для детерминированного порядка результатов полнотекстового поиска явно используйте ORDER BY _score DESC и добавляйте уникальный дополнительный критерий сортировки.
  • Чтобы фильтровать до формирования векторных или полнотекстовых кандидатов, задайте TVF filter и prefilter=true; при prefilter=false по умолчанию TVF filter применяется после формирования кандидатов. Внешний WHERE вычисляется Doris по кандидатам, сформированным каждой поисковой частью Split, до глобального TopN, поэтому итоговый результат может содержать меньше top_k строк.
  • Используйте EXPLAIN для просмотра lanceSearchFragments и lanceSearchIndexSegments. Первое значение — количество видимых Fragment в зафиксированном снимке; второе — количество физических частей Index Segment, выбранных FE. Векторный поиск также может включать резервные части Fragment для Flat Search; полнотекстовый поиск никогда не переходит к Flat Search.
На этой странице