跳到主要内容
最后 更新

Lance Catalog

Lance 是面向分析和 AI 场景的列式数据格式。Doris 可以通过 Lance Catalog 发现 Lance Namespace 中的数据库和表,并直接查询存储在本地文件系统或 S3 兼容对象存储中的 Lance 数据集。

当前 Doris 对 Lance 提供只读能力,不支持创建、写入、更新或删除 Lance 表。

功能概览

功能支持情况
Filesystem Catalog支持本地文件系统、file://s3:// Warehouse
REST Catalog支持 Lance REST Namespace,以及无认证、Bearer Token、API Key 和自定义 HTTP Header
元数据访问支持 SHOW DATABASESSHOW TABLESDESC
数据查询支持列裁剪、并行扫描 Lance Fragment 和当前版本的快照一致性读取
谓词下推支持将部分标量谓词下推到 Lance 执行
文件 TVF支持通过 s3()local() 直接查询 Lance 数据集
向量检索支持通过 vector_search() 查询 Lance 向量索引或执行 Flat Search
写入 Lance暂不支持
Time Travel暂不支持
Full-Text Search / Hybrid Search暂不支持

配置 Catalog

语法

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

通用属性

属性是否必需默认值说明
type-固定为 lance
lance.catalog.typefilesystemCatalog 类型,可选值为 filesystemrest
lance.namespace.parent仅访问指定 Lance Namespace 及其子 Namespace。例如默认分隔符下,production$analytics 表示两级 Namespace。
lance.namespace.delimiter$用于解析 lance.namespace.parent,同时会传递给 REST Namespace 客户端。该配置不改变 Doris 中多级 Namespace 的展示方式。
lance.namespace.root_databasedefaultLance 根 Namespace 在 Doris 中映射的数据库名。

Filesystem Catalog

Filesystem Catalog 直接从 Warehouse 目录发现 Lance Namespace 和表。

属性是否必需说明
warehouseLance Warehouse 根路径。支持本地绝对路径、file:// URI 和 s3:// URI。

使用 S3 兼容对象存储

下面以 MinIO 为例创建 Catalog:

CREATE CATALOG lance_catalog PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "filesystem",
"warehouse" = "s3://my-bucket/lance",
"s3.endpoint" = "http://127.0.0.1:9000",
"s3.access_key" = "admin",
"s3.secret_key" = "password",
"s3.region" = "us-east-1",
"use_path_style" = "true"
);

访问 AWS S3 时,可以省略 s3.endpoint,并按实际环境配置访问密钥、Region 和 Path Style。

使用本地文件系统

CREATE CATALOG lance_local PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "filesystem",
"warehouse" = "/data/lance"
);

使用本地文件系统时,warehouse 必须是绝对路径。FE 需要通过该路径读取 Namespace 和表元数据,执行查询的 BE 也需要能够通过相同路径访问数据。因此在多节点环境中,应将相同的共享目录挂载到所有相关 FE 和 BE 节点。

REST Catalog

REST Catalog 通过 Lance REST Namespace 获取 Namespace、表地址和存储访问参数。REST Catalog 不需要、也不允许设置 warehouse

属性是否必需默认值说明
lance.rest.uri-REST 服务地址,必须使用 http://https://
lance.rest.security.typenone认证方式,可选值为 nonebearerapi_key
lance.rest.bearer-token使用 Bearer 认证时是-Bearer Token。
lance.rest.api-key使用 API Key 认证时是-API Key,通过 x-api-key Header 发送。
lance.rest.header.<header-name>-发送给 REST 服务的自定义 HTTP Header。认证 Header 应使用上面的专用认证属性配置。

使用 Bearer Token 创建 REST Catalog:

CREATE CATALOG lance_rest PROPERTIES (
"type" = "lance",
"lance.catalog.type" = "rest",
"lance.rest.uri" = "https://lance.example.com",
"lance.rest.security.type" = "bearer",
"lance.rest.bearer-token" = "your-token"
);

使用 API Key 时,将认证配置替换为:

"lance.rest.security.type" = "api_key",
"lance.rest.api-key" = "your-api-key"

如果 REST 服务返回临时存储凭证,Doris 会使用这些凭证访问对应的 Lance 表。也可以在 Catalog 中配置 s3.endpoints3.access_keys3.secret_keys3.regionuse_path_style,作为默认的对象存储访问参数。

警告

当前 BE Reader 不支持由 REST Namespace 管理版本的 Lance 表(Managed Versioning)。

Namespace 映射

Lance 支持多级 Namespace,而 Doris Catalog 使用数据库名承载 Namespace:

Lance NamespaceDoris 数据库名
根 Namespacedefault,可通过 lance.namespace.root_database 修改
dorisdoris
doris.analyticsdoris.analytics

多级 Namespace 在 Doris 中使用 . 连接为一个数据库名。引用包含 . 的数据库名时,需要使用反引号:

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",
"s3.region" = "us-east-1"
);

此时 Doris 只展示 production.analytics 之下的表和子 Namespace。

查询 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 查询会在规划阶段固定一个 Lance 数据集版本,并按 Fragment 生成扫描任务。因此,同一条查询读取一致的快照,同时可以由多个 Scanner 并行扫描不同 Fragment,不会让每个 Scanner 重复扫描整个数据集。

类型映射

Lance / Arrow 类型Doris 类型说明
boolBOOLEAN
int8TINYINT
uint8SMALLINT无符号整数无损提升
int16SMALLINT
uint16INT无符号整数无损提升
int32INT
uint32BIGINT无符号整数无损提升
int64BIGINT
uint64LARGEINT无符号整数无损提升
float16FLOAT提升为 32 位浮点数
float32FLOAT
float64DOUBLE
decimal128(P,S)DECIMAL(P,S)最大精度为 38
decimal256(P,S)DECIMAL(P,S)最大精度为 76
utf8large_utf8TEXT
binarylarge_binaryVARBINARY(2147483647)
fixed_size_binary(N)VARBINARY(N)保留固定字节宽度
date32(day)date64(ms)DATEdate64 应表示完整自然日
time32(s)TIME(0)
time32(ms)TIME(3)
time64(us)time64(ns)TIME(6)纳秒精度截断为微秒
无时区 timestamp(s)DATETIME不随 Session Time Zone 转换
无时区 timestamp(ms)DATETIME(3)不随 Session Time Zone 转换
无时区 timestamp(us)timestamp(ns)DATETIME(6)纳秒精度截断为微秒
带时区 timestampTIMESTAMPTZ(0-6)保存时间点,按 Doris Session Time Zone 展示
structSTRUCT子字段递归映射
listlarge_listfixed_size_listARRAY元素类型递归映射
mapMAPKey 和 Value 类型递归映射

当前不支持以下类型:

  • Arrow nullduration
  • 带有 ARROW:extension:name 元数据的 Arrow/Lance Extension 类型,例如 Lance Blob v2、Arrow JSON Extension 和 Lance BFloat16 Extension。
  • 无法递归映射其子类型的复杂类型。
  • 保留了 Dictionary 标记的 Arrow Dictionary 类型。

对于不支持的顶层列,Catalog 表的 DESC 和 Lance 文件 TVF 的 DESC FUNCTION 都会保留该列,并显示 unknown type: UNSUPPORTED_TYPE。如果复杂类型的任一子字段无法映射,则整个顶层复杂列会标记为不支持。查询只投影支持的列仍可正常执行;当 SQL 投影不支持的列时,Doris 会在分析阶段报错。例如:

SELECT * EXCEPT(blob_col, json_col)
FROM lance_catalog.default.all_types;
备注

部分 Lance Java SDK 版本可能在读取 Schema 时丢失 Dictionary 标记,并将 Dictionary 列暴露为物理索引类型。该结果不代表 Doris 已支持 Dictionary 的逻辑值,请勿依赖这一行为。

谓词下推

Doris 会将语义兼容的谓词转换为 Substrait 表达式,并交给 Lance 在读取阶段执行。对于已完整下推的条件,Doris BE 不再重复计算该条件;不能安全下推的条件仍由 Doris 执行。

支持下推的数据类型

Lance / Arrow 类型下推范围
bool等值、空值和逻辑运算,不包含大小比较
int8/16/32/64支持
uint8/16/32/64支持
float32/64支持
decimal128精度 1-38,Scale 范围为 0 到 Precision
utf8large_utf8支持
date32(day)支持
无时区 timestamp(s/ms/us)支持

其他可读取类型,例如 float16decimal256、Binary、date64、Time、纳秒 Timestamp、带时区 Timestamp 和复杂类型,当前保留为 Doris 侧谓词。

支持下推的操作符

SQL 谓词下推条件
=!=<><<=>>=直接的列与常量比较;支持将常量写在左侧
<=>直接的列与常量 Null-safe 等值比较;在 NOTANDOR 中仍保持非 NULL 的二值逻辑结果
INNOT IN非空常量列表,列表中不能包含 NULL
IS NULLIS NOT NULL直接引用列
AND顶层 Conjunct 可以分别下推,不能下推的部分保留在 Doris
OR两个分支都能完整转换时下推
NOT操作数能完整转换时下推

以下形式通常不会下推:

  • 列上包含函数或算术表达式。
  • IN 列表为空或包含 NULL
  • ORNOT 中只有部分表达式可转换。
  • 数据类型或常量值无法无损转换到 Lance。

可以通过 EXPLAIN 中的 lancePushdownPredicate 查看实际下推的条件:

EXPLAIN
SELECT user_id
FROM lance_catalog.default.user_profiles
WHERE age >= 18 AND country IN ('CN', 'US');

使用文件 TVF 查询 Lance

如果只需要读取一个已知路径的 Lance 数据集,可以不创建 Catalog,直接使用 s3()local() TVF。urifile_path 必须指向 Lance 数据集的根目录,而不是其内部的数据文件。

S3 TVF

SELECT user_id, name
FROM s3(
"uri" = "s3://my-bucket/lance/user_profiles.lance",
"s3.endpoint" = "http://127.0.0.1:9000",
"s3.access_key" = "admin",
"s3.secret_key" = "password",
"s3.region" = "us-east-1",
"use_path_style" = "true",
"format" = "lance"
)
WHERE user_id > 100;

S3 TVF 在 FE 获取 Schema、当前版本和 Fragment 列表,并固定该版本后按 Fragment 并行扫描。

Local TVF

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

file_path 会按用户填写的值直接传给目标 BE 上的 Lance Reader。Doris 不会自动拼接 user_files_secure_path,也不会对该路径执行 Glob 展开,因此该参数必须直接指向目标 BE 可访问的单个 Lance 数据集根目录;建议使用绝对路径。

Local TVF 的 Schema 发现和执行扫描会分别打开数据集的最新版本,当前不会将 Schema 发现时解析出的版本固定到后续扫描。如果数据集在查询分析与执行之间发生更新,Schema 和实际扫描的快照可能不一致。因此,应避免在 Local TVF 查询的分析和执行期间修改数据集。当前 Local Lance TVF 使用单个 Scanner。

Lance 文件 TVF 还有以下限制:

  • 仅支持 s3()local(),暂不支持 HDFS、HTTP 等其他文件 TVF。
  • 不支持 path_partition_keys
  • 一个 TVF 路径只能表示一个 Lance 数据集。
  • DESC FUNCTION 可以展示包含不支持类型的 Schema,但 SQL 不能投影不支持的列。

向量检索

vector_search() 是一个关系型 TVF,用于对 Lance 表的向量列执行 Top-K 检索。它既可以使用 Lance 中已建立的向量索引,也可以执行 Flat Search。

语法和示例

SELECT row_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",
"metric" = "l2",
"nprobes" = "20",
"refine_factor" = "10",
"use_index" = "true"
)
ORDER BY _distance ASC, row_id;

结果包含 Lance 源表的所有列,以及 Lance Scanner 为最近邻查询自动投影的 _distance 列。Doris 会反序列化该 Arrow 列,并以 FLOAT 类型提供给用户。_distance 表示距离,而不是通用的相似度分数;值越小表示两个向量越接近。源表不能已经包含名为 _distance 的列。SQL 关系本身不保证输出顺序,因此需要稳定的最近邻顺序时,应显式使用 ORDER BY _distance ASC,并建议增加唯一列作为距离相同情况下的 Tie-breaker。

table 必须解析为恰好三部分的 catalog.database.table 名称。多级 Lance Namespace 在 Doris 中映射为包含 . 的单个数据库名,因此必须使用反引号将数据库部分括起来。例如,表 items 位于 doris.analytics Namespace 时,应写为:

"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>
query_vector-JSON 数字数组。维度必须与向量列一致,元素值必须能由向量元素类型表示。
top_k10跳过 offset 后返回的结果数,必须为正整数。
offset0在向量检索内部跳过的最近邻数量,必须为非负整数。top_k + offset 不能超过无符号 32 位整数上限。
metric匹配索引的 Metric;无索引时 uint8hamming,其他支持类型为 l2距离类型:l2cosinedothammingdot_productdot 的别名。uint8 向量仅支持 hamming;其他当前支持的向量元素类型支持 l2cosinedot
filter-Lance SQL 条件,在生成候选向量之前执行,即 Prefilter。
nprobes最少 1,不限制最大值IVF 索引探测的分区数量,必须为正整数。不设置时从 1 个分区开始;使用 Prefilter 且候选不足时,Lance 可以继续探测更多分区。显式设置为 N 时,最少和最多探测数都会固定为 N
refine_factor不启用精排候选集精排倍数,必须为正整数。不设置时不基于原始向量重新计算距离,量化索引返回的 _distance 可能是近似距离;设置为 N 后,Lance 先获取 (top_k + offset) × N 个候选,再用原始向量计算真实距离并重新排序。即使设置为 1 也会执行精排,因此与不设置不同。
effloor(1.5 × (top_k + offset))HNSW 图索引搜索时保留的候选宽度,必须为正整数。如果同时设置了 refine_factor,默认值为 floor(1.5 × (top_k + offset) × refine_factor)。对非 HNSW 索引无效。
use_indextruetrue 表示存在兼容索引时优先使用索引,否则自动执行 Flat Search;false 强制执行 Flat Search。

以上默认值对应 Doris 当前集成的 Lance Scanner 行为。metric 未指定时,如果向量列存在兼容索引,查询使用该索引创建时配置的 Metric;不存在兼容索引或 "use_index" = "false" 时,uint8 向量使用 hamming,其他当前支持的向量元素类型使用 l2

Prefilter 和 Post-filter

TVF 的 filter 参数由 Lance 在 Top-K 候选选择前执行:

SELECT row_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",
"filter" = "category = 'book'"
)
ORDER BY _distance ASC, row_id;

外层 WHERE 由 Doris 在 Lance 返回 Top-K 后执行:

SELECT row_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"
)
WHERE category = 'book'
ORDER BY _distance ASC, row_id;

因此,外层 WHERE 可能使最终结果少于 top_k。如果过滤条件应该参与最近邻候选选择,应使用 TVF 的 filter 参数。

当前执行方式

vector_search() 会固定一个 Lance 数据集版本,并由一个 Scanner 搜索该版本中的全部 Fragment,以保证得到全局 Top-K。当前尚未将向量检索拆分为多个 Scanner,也没有在 Doris 中执行多路候选集的全局 Top-K 合并。

当前限制和建议

  • Lance Catalog 和 Lance TVF 当前仅支持读取,不支持 CREATE TABLEINSERTUPDATEDELETETRUNCATE TABLE 或写回 Lance。
  • 查询总是读取规划时选择的当前版本,不支持通过 SQL 指定 Version 或执行 Time Travel。
  • 使用不支持的列类型时,建议显式列出需要读取的列,避免 SELECT * 投影到不支持的列。
  • 对普通扫描使用 EXPLAIN 检查 lancePushdownPredicate,确认目标条件是否已下推。
  • 向量检索前应在 Lance 中创建与查询方式匹配的索引;小数据集或验证场景可以设置 "use_index" = "false" 使用 Flat Search。
  • 向量查询需要稳定顺序时,显式使用 ORDER BY _distance ASC 并增加唯一 Tie-breaker。
  • 需要在向量候选选择前过滤时使用 vector_search()filter;只有明确需要 Top-K 之后过滤时才使用外层 WHERE