FeatureDB 存储引擎
本文档介绍 FeatureDB 的产品定位、部署配置、数据导入、Python SDK 使用、fdb-cli 运维管理以及当前版本的功能与限制。
FeatureDB 是运行于 DolphinDB 中的特征存储与低延时读取引擎,适用于 AI/ML、量化研究和在线特征服务等场景,尤其适合批量导入、频繁读取的工作负载。
FeatureDB 专注于在线特征数据的存储与访问,不提供通用 SQL 查询与分析能力。本手册将围绕当前版本支持的主要功能展开介绍,包括表管理、Parquet 数据导入、低延时读取等。
提示:FeatureDB 当前处于早期版本阶段,FeatureDB 当前处于早期版本阶段,
FeatureDB 运行于 DolphinDB 环境,并通过独立的 RPC 服务提供数据访问能力。其核心目标是为 AI/ML、量化、在线特征服务等场景提供低延时的数据访问能力,尤其适合批量导入、频繁读取的工作负载。
可以通过以下两种方式使用 FeatureDB:
-
使用 fdb-cli 创建和管理表,并批量导入数据;
-
使用 Python SDK(fdbpy)读取数据并开发业务应用。
后续章节将分别介绍 FeatureDB 的部署配置、Parquet 数据导入、fdb-cli 运维管理和 Python SDK 使用方法。
本章按照从部署到导入数据的顺序,介绍完成一次 FeatureDB 试用所需的最小流程。你将完成以下操作:
-
部署 FeatureDB 服务端并安装客户端工具;
-
配置服务端和客户端连接参数;
-
使用 fdb-cli 创建表;
-
导入 Parquet 数据并检查导入任务状态。
fdb-cli 是随 Python wheel 一起安装的命令行工具。它本质上是 FeatureDB 服务端的文本命令客户端,适合运维管理、建表、导入和任务监控。
Python SDK 提供统一的 FeatureDB 访问能力,支持连接管理、通过文本命令进行表管理和数据导入,以及发布包提供的低延时读取接口。
当前版本能力边界
当前版本支持以下功能:
-
服务端仅支持内存模式(持久化模式将在后续版本提供);
-
通过标准 Parquet 文件批量导入数据;
-
使用 Python SDK 进行低延时数据读取;
-
IPC、Socket 和 RDMA 三种通信后端。
当前版本不支持通用 SQL 查询与数据写入操作(如 SELECT、INSERT、UPDATE 和 DELETE)。
如需完整的 SQL 查询与分析能力,请使用 DolphinDB 标准数据库引擎。
适用场景
FeatureDB 适用于以下典型场景:
-
在线推理服务读取特征矩阵;
-
按时间点或时间范围读取时序特征;
-
对指定特征列进行连续窗口读取;
-
离线生成 Parquet 文件后批量导入,再供在线服务低延时读取。
部署与安装
FeatureDB 运行于 DolphinDB 服务端内置的功能模块,随安装包一同提供。使用前,需要在服务端配置文件中启用 FeatureDB RPC 服务,并在客户端安装随 Python wheel 提供的 Python SDK(fdbpy)和命令行工具(fdb-cli)。
目前 FeatureDB 支持部署于 Linux 系统的单机节点上。典型部署方式如下:
-
DolphinDB 部署于服务端主机,并动态加载 FeatureDB 模块;
-
客户端既可以部署于服务端所在主机,也可以部署于远端主机;
-
客户端与服务端位于同一主机时,可通过 IPC 建立连接;位于不同主机时,可通过 Socket 或 RDMA 建立连接。
服务端采用免安装设计,解压即可运行,无需单独安装,因此本节仅介绍客户端安装。
当前发布的 Python wheel 仅支持 Python 3.13,请确保客户端已安装对应版本的 Python 环境。
创建 Python 虚拟环境并安装 Python wheel:
python3.13 -m venv ~/fdbpy-3.13
~/fdbpy-3.13/bin/python -m pip install --upgrade pip
~/fdbpy-3.13/bin/python -m pip install \
--force-reinstall \
/path/to/fdbpy-0.1.0-cp313-cp313-linux_x86_64.whl
安装完成后,可使用以下工具:
-
~/fdbpy-3.13/bin/fdb-cli:命令行管理工具,用于建表、导入数据和查看任务状态;
-
fdbpy:Python SDK,用于业务程序读取 FeatureDB 数据。
配置
配置分为服务端配置和客户端连接配置。服务端配置用于启动 FeatureDB 服务;客户端连接配置主要指 fdb-cli 使用的 TOML 配置文件。Python SDK 不读取该 TOML 文件,连接参数需在代码中通过 Endpoint 对象指定,详见第 5.1 节。
服务端配置
启用 FeatureDB 服务前,必须配置 RPC endpoint。以 Socket 为例,在 DolphinDB 运行目录下的 dolphindb.cfg 中增加以下配置:
fdbBackend=socket
fdbRpcEndpoint=192.168.100.250:19090
fdbNumServerWorkers=1
fdbImportRoot=/data/featuredb-import
各配置项说明如下:
-
fdbBackend:客户端连接方式。fdbBackend 不同配置对应的 fdbRpcEndpoint 配置格式参考下文的提示。
-
fdbRpcEndpoint:FeatureDB 服务监听地址。需要根据实际环境进行配置。如果未配置 fdbRpcEndpoint,DolphinDB 将正常启动,但不会启动 FeatureDB 服务,因此无法通过 Python SDK 或其他客户端访问 FeatureDB。
-
fdbNumServerWorkers:服务线程数,正整数。建议与可用 CPU 核数匹配。
-
fdbImportRoot:可选配置,FeatureDB 读取 Parquet 文件的根目录。
提示:
-
首次试用建议优先使用 Socket 方式,配置更简单;确认功能可用后,再根据性能要求切换到 RDMA。
-
客户端连接服务端的方式与 Endpoint 配置格式:
| fdbBackend | fdbRpcEndpoint 格式 | 说明 |
|---|---|---|
| ipc | <base-path> |
进程间通信(Inter-Process Communication)。适用于同机部署、测试环境 |
| socket |
port 必须不同于 DolphinDB server 的 port |
基于 TCP 的网络通信。适用于跨机器部署、通用生产环境 |
| rdma | <host>:<port>:<device> |
基于 RDMA 的高性能通信。适用于低延迟、高吞吐场景(如高性能计算/金融场景) |
完成以上配置后,运行如下命令启动 DolphinDB 和 FeatureDB:
./dolphindb
运行如下命令检查监听状态(需根据实际情况调整端口号):
-
RDMA 监听状态
rdma resource show cm_id | grep 19091 -
Socket 的 TCP 监听状态
ss -ltnp | grep 19090
fdb-cli 连接配置
Socket 连接配置
fdb-cli 使用 TOML 文件保存连接参数。Python SDK 不使用该 TOML 配置文件,而是在代码中通过 IpcEndpoint、SocketEndpoint 或 RdmaEndpoint 指定连接参数。以下是 Socket 连接示例:
backend = "socket"
endpoint = "192.168.100.250:19090"
connect_timeout_ms = 1000
io_timeout_ms = 5000
tcp_no_delay = true
保存为 fdb-socket.toml 后,可以通过以下命令连接服务端:
~/fdbpy-3.13/bin/fdb-cli --config ./fdb-socket.toml
也可以通过环境变量指定默认配置文件:
export FDB_CLI_CONFIG=/path/to/fdb-socket.toml
如果未指定 --config 且未设置 FDB_CLI_CONFIG,fdb-cli 会依次读取 $XDG_CONFIG_HOME/fdb/client.toml、~/.config/fdb/client.toml;如果默认配置文件也不存在,则默认连接本机 IPC:backend = "ipc"、endpoint = "/var/tmp/fdb.ipc"。
IPC 连接配置
backend = "ipc"
endpoint = "/tmp/fdb.ipc"
RDMA 连接配置
如果使用 RDMA,除服务端地址和端口外,还需要确认客户端所在机器的 RDMA 设备信息,例如设备名、端口号和 GID 索引。RDMA 适合对访问延时要求较高的生产环境;如果只是功能验证,建议先使用 Socket。
RDMA 连接配置示例:
backend = "rdma"
endpoint = "181.181.1.240:19091:mlx5_0"
ib_port = 1
gid_index = 0
queue_depth = 256
max_inline_bytes = 256
上例中 181.181.1.240:19091:mlx5_0 的含义为:
-
181.181.1.240:FeatureDB 所在机器的 RDMA 网络 IP。
-
19091:FeatureDB 端口。
-
mlx5_0:客户端机器上的本地 RDMA 设备名。
常用检查命令包括 ibv_devices、ibv_devinfo -d <device>、ibdev2netdev 和 show_gids。确认 RDMA 环境可用后,再切换到 RDMA 连接。
创建表
连接 fdb-cli 后,可以使用 create table 创建 FeatureDB 表。下面的示例创建一张特征表;后续快速开始和
fdb-cli 示例均沿用 user_features 表,以保持建表、导入和读取流程一致。其中 user_id 是实体标识(Entity
ID),用于唯一标识特征所属的实体;dt 是时间列,用于记录特征对应的时间;f0 到 f249 是特征列。
create table user_features (
user_id INT64,
dt DATEMINUTE,
f<0..249> FLOAT16,
valid BOOL
)
partitioned by dt values (<28800000..28800002>)
建表后,可使用以下命令查看表是否创建成功:
show tables
describe user_features
建表时需要注意:
-
FeatureDB 的表结构在建表时确定,后续导入的数据需与表结构保持一致。
-
当存在大量连续命名的特征列时,可使用 fp<1..250> 这样的范围写法简化建表语句。
导入数据
FeatureDB 使用标准 Parquet 文件进行批量导入。可以使用 PyArrow、Spark、DuckDB 等工具生成 Parquet 文件,只要文件 Schema 与目标表一致即可导入。
在 fdb-cli 中执行以下命令,将与 user_features 表结构一致的 Parquet 文件导入该表:
load data infile './user_features.parquet' into table user_features
导入任务采用异步执行方式。提交命令后,返回结果中会包含任务 ID,可使用以下命令查看任务状态:
describe task-123456-1 alive
除 fdb-cli 外,也可以在 Python SDK 中通过 client.execute_command(...) 发送同样的文本命令来导入数据。该方式适合在业务程序或自动化脚本中完成建表、导入和任务状态检查。
import fdbpy as fdb
endpoint = fdb.SocketEndpoint(host="192.168.100.250", port=19090)
client = fdb.connect(endpoint)
print(client.execute_command(
"create table user_features ("
"user_id INT64, dt DATEMINUTE, f<0..249> FLOAT16, valid BOOL) "
"partitioned by dt values (<28800000..28800002>)"
))
result = client.execute_command(
"load data infile './user_features.parquet' into table user_features"
)
print(result)
# 根据 load data 返回的 task_id 查询导入状态
print(client.execute_command("describe task-123456-1 alive"))
如果使用 RDMA 连接,可将 endpoint 替换为 fdb.RdmaEndpoint:
endpoint = fdb.RdmaEndpoint(
control_host="181.181.1.240",
control_port=19091,
device_name="mlx5_0",
)
client = fdb.connect(endpoint)
需要注意,load data infile 中的文件路径仍由 FeatureDB 服务端解析;远程连接时,路径必须对服务端可见,而不是仅存在于 Python 客户端所在机器。
快速开始中仅列出最容易导致导入失败的检查项;完整 Parquet/Arrow 类型映射和文件生成要求请参考第 3 章。
导入机制概述
FeatureDB 使用标准 Apache Parquet 作为批量导入格式,不引入专有文件格式。可使用 PyArrow、Spark、DuckDB 等工具生成 Parquet 文件,只要文件 Schema 与目标表一致,即可直接导入。导入过程中,FeatureDB 会自动解析 Parquet 文件并加载到内存存储引擎,无需进行额外的数据转换。
导入要求
-
目标表已存在,或可通过
client.load_table()自动建表。 -
导入文件的列名、列顺序及列类型必须与目标表定义严格一致。
-
只支持定宽基础类型,不支持嵌套类型。
-
DATETIME 和 DATEMINUTE 需要通过字段元数据标识逻辑类型。
支持的类型
FeatureDB 在 Parquet 导入过程中使用 Apache Arrow 作为中间表示,因此需要将 Parquet/Arrow 类型映射为 FeatureDB 内部类型。下表同时列出 create table 可声明的 FeatureDB 类型及其对应的 Parquet/Arrow 导入类型;其中 Parquet/Arrow 类型或备注留空/标注不支持的,表示当前不支持通过 Parquet 导入。
| FeatureDB 类型 | 语义 | Parquet / Arrow 类型 | 备注 |
|---|---|---|---|
| BOOL | 布尔值 | BOOLEAN | 支持建表和 Parquet 导入 |
| INT8 | 8 位有符号整数 | INT8 | 支持建表和 Parquet 导入 |
| UINT8 | 8 位无符号整数 | UINT8 | 支持建表和 Parquet 导入 |
| INT32 | 32 位有符号整数 | INT32 | 支持建表和 Parquet 导入 |
| INT64 | 64 位有符号整数 | INT64 | 支持建表和 Parquet 导入 |
| FLOAT16 | 半精度浮点数 | HALF_FLOAT | 支持建表和 Parquet 导入 |
| BFLOAT16 | bfloat16 浮点数 | 支持建表;当前不支持 Parquet 导入 | |
| FLOAT32 | 单精度浮点数 | FLOAT | 支持建表和 Parquet 导入 |
| FLOAT64 | 双精度浮点数 | DOUBLE | 支持建表和 Parquet 导入 |
| FLOAT8_E4M3 | 8 位浮点数,E4M3 格式 | 支持建表;当前不支持 Parquet 导入 | |
| FLOAT8_E5M2 | 8 位浮点数,E5M2 格式 | 支持建表;当前不支持 Parquet 导入 | |
| TIMESTAMP | 纳秒时间戳 | timestamp[ns] | 支持建表和 Parquet 导入 |
| DATETIME | epoch 秒,底层为 INT32 | INT32 | 支持建表和 Parquet 导入;需要设置字段元数据 fdb.logical_type=DATETIME |
| DATEMINUTE | epoch 分钟,底层为 INT32 | INT32 | 支持建表和 Parquet 导入;需要设置字段元数据 fdb.logical_type=DATEMINUTE |
| STRING | 字符串 | 支持建表;当前不支持 Parquet 导入 |
上表中,对于 DATETIME 和 DATEMINUTE,需要在 Arrow 字段元数据中增加:
Key: fdb.logical_type
Value: DATETIME | DATEMINUTE
如果缺少这一元数据,INT32 列会被当作普通整型导入,而不是时间类型。下例展示了如何使用 PyArrow 生成 FeatureDB 能正确识别时间类型的 Parquet 文件。
import pyarrow as pa
import pyarrow.parquet as pq
dt_field = pa.field(
"dt",
pa.int32(),
nullable=False,
metadata={b"fdb.logical_type": b"DATETIME"},
)
dm_field = pa.field(
"dm",
pa.int32(),
nullable=False,
metadata={b"fdb.logical_type": b"DATEMINUTE"},
)
ts_field = pa.field("ts", pa.timestamp("ns"), nullable=False)
schema = pa.schema([dt_field, dm_field, ts_field])
table = pa.table(
{
"dt": [1717110880, 1717111000],
"dm": [28618514, 28618516],
"ts": [1717110880123456789, 1717111000987654321],
},
schema=schema,
)
pq.write_table(table, "temporal.parquet", row_group_size=64 * 1024)
注意:Parquet row group 大小建议不超过 64,000 行,这与当前导入批次大小一致。
连接配置
CLI 连接参数使用 TOML 文件配置,完整格式、默认查找路径和 Socket/IPC/RDMA 示例见 2.2.2 客户端连接配置。本节只补充 CLI 特有的配置入口。
使用方式与示例
本节介绍交互式 REPL、一次性执行、批处理文件以及端到端建表导入流程。以下示例默认已准备好连接配置文件 fdb-rdma.toml。
交互式 REPL
fdb-cli --config ./fdb-rdma.toml
连接成功后进入提示符,可直接输入命令:
fdb> help
fdb> show tables
fdb> describe ticks
fdb> exit
一次性执行命令
一次性执行命令时,可以直接在 CLI 参数后追加 FeatureDB 命令;推荐使用 -- 分隔 CLI 参数和 FeatureDB 命令。
fdb-cli --config ./fdb-rdma.toml -- show tables
fdb-cli --config ./fdb-rdma.toml describe ticks
fdb-cli --config ./fdb-rdma.toml --monitor-interval-ms 1000 describe task-123456-1 alive
fdb-cli --print-config-example
批处理文件示例
批处理文件可用于初始化表、导入数据和检查结果。命令文件每行一条命令;空行和以 # 开头的行会被忽略;行尾使用未转义的反斜杠 \ 可以续行;遇到第一条失败命令时停止执行。文件中出现 exit 或 quit 时正常结束;执行成功的 stopServer 也会结束批处理。
# setup.fdb
create table ticks (id INT32, ts TIMESTAMP, price FLOAT32) \
partitioned by id values (1,2,3)
load data infile 'ticks.parquet' into table ticks
describe ticks
fdb-cli --config ./fdb-rdma.toml --file setup.fdb
导入路径包含空格的文件
load data infile 的文件路径必须使用单引号包围;如果路径中包含空格,也应写在同一个单引号字符串中。
load data infile 'demo parquet.parquet' into table ticks
完整端到端流程
下面的示例展示了从建表、导入、监控任务到查看表信息的完整流程。实际使用时需将 task-123456-1 替换为 load data 返回的任务 ID。
create table user_features (
user_id INT64,
dt DATEMINUTE,
f<0..3> FLOAT32,
valid BOOL
) partitioned by dt values (<28800000..28800002>)
load data infile 'user_features.parquet' into table user_features
describe task-123456-1 alive
show tables
describe user_features
常用命令
fdb-cli 提供一组常用命令,用于表管理、数据导入及任务监控。以下为常用操作:
| 命令 | 作用 | 示例 |
|---|---|---|
help |
显示服务端当前支持的命令摘要 | help |
exit / quit |
断开客户端连接并退出 REPL | exit |
show tables |
列出表及摘要信息 | show tables |
describe <table> |
查看表元数据、列列表和分区分布 | describe user_features |
create table |
创建表,并以内联列定义创建或复用同名 schema | create table ticks (id INT32, ts TIMESTAMP, price FLOAT32) |
drop table <table> |
删除表 | drop table ticks |
load data infile |
异步导入 Parquet 文件 | load data infile '/data/ticks.parquet' into table ticks |
describe <task-id> 或 describe <task-id> alive |
查看或持续监控导入任务状态 | describe task-123456-1 alive |
listBdev |
列出已注册的 bdev | listBdev |
addBdev <config-path> |
从配置文件添加 bdev。路径按普通 token 解析,不能包含空白 | addBdev /etc/fdb/bdev.json |
removeBdev <bdev-name> |
移除指定 bdev | removeBdev nvme0 |
stopServer |
请求正在连接的 FeatureDB 服务端优雅退出 | stopServer |
test |
运行启动阶段存储预检;仅适用于 SPDK 初始化前的启动检查 | test |
需要注意,load data infile 中的文件路径由 FeatureDB 服务端解析;远程连接时,该路径必须对服务端可见。
此外,FeatureDB 在标准 CREATE TABLE 语法基础上,支持部分扩展语法,用于简化大规模特征表定义。
具体包括:
-
支持使用范围表达式批量生成连续列名:例如 feat<1..4>,等价于 feat1、feat2、feat3、feat4。该写法适用于大规模特征列定义场景。
-
支持在
partitioned by values中使用范围表达式:例如 values (0,<2..4>,9),等价于 0,2,3,4,9。 -
关键字大小写不敏感(如
CREATE TABLE与create table等价)。 -
标识符(表名、列名)暂不支持使用引号(如 "trade"、"price")。
create table user_features (
user_id INT64,
dt DATEMINUTE,
f<0..3> FLOAT32,
valid BOOL
) partitioned by dt values (<28800000..28800002>)
以上文本命令也可以通过 Python SDK 的 client.execute_command(...) 执行;Python 侧的集中示例见第 5 章。
Temporal Reader CLI 命令
Temporal Reader 用于按时间列定位当前时间点的行集合,并在该集合内查询或读取范围。
| 命令 | 作用 | 示例 |
|---|---|---|
temporalReaderCreate <reader-id> <temporal-column> <temporal-value> [table <table-name>] [id <id-value>] |
创建或覆盖一个 reader,并定位到指定时间值。table 可限制到指定表,id 可在初始化时按 id 过滤;两者顺序可以互换。 | temporalReaderCreate r1 dt 28800000 table user_features id 42 |
temporalReaderMove <reader-id> <delta-minutes> |
按分钟移动 reader 的当前时间点,delta-minutes 可以为负数。 |
|
temporalReaderSeek <reader-id> <temporal-value> |
将 reader 定位到指定时间值。 | temporalReaderSeek r1 28800001 |
temporalReaderLookup <reader-id> <id-value> |
在 reader 当前 scope 内按 id 查找一行。如果当前 scope 中有多行匹配该 id,服务端会随机选择其中一行读取。 | temporalReaderLookup r1 1001 |
temporalReaderReadScope <reader-id> [begin] [count] |
读取当前 scope 的行引用列表。不带参数时读取整个 scope;只带 begin 时从该位置读到末尾;同时提供 begin 和 count 时读取半开区间 [begin, begin + count)。begin 和 count 必须是非负整数。 |
|
temporalReaderClose <reader-id> |
关闭并移除 reader。 | temporalReaderClose r1 |
Temporal Reader 命令的输出通常包含 reader_id、found、scope_size、previous_temporal、current_temporal,或 lookup/scope 行引用信息。输出面向终端阅读,不建议作为稳定机器解析协议。
temporalReaderCreate r1 dt 28800000 table user_features
temporalReaderLookup r1 1001
temporalReaderMove r1 1
temporalReaderReadScope r1 0 20
temporalReaderClose r1
常见命令输出为面向终端阅读的文本,不是稳定的机器解析协议。如需程序化读取数据,应优先使用 Python SDK 或 native transport API。常见输出包括:
-
create table:返回Created table: <table-name>,分区表会追加分区信息。 -
drop table:返回Dropped table: <table-name>。 -
show tables:每张表一行,包含 id、schema、blob、pages、rows、estimated_bytes 和列摘要。 -
describe <table-name>:返回表元数据、分区值/分区分布以及列列表。 -
load data infile ...:返回Submitted load task: <task-id> table=<table-name> file='<file-path>'。 -
describe <task-id>:返回导入任务状态,例如 RUNNING、COMPLETED 或 FAILED,并可能包含文件路径和错误信息。
连接与管理
Python SDK 支持三种连接方式:
-
IpcEndpoint:本地进程通信
-
SocketEndpoint:基于 TCP 的远程连接
-
RdmaEndpoint:基于 RDMA 的高性能连接
import fdbpy as fdb
endpoint = fdb.SocketEndpoint(host="192.168.100.250", port=19090)
client = fdb.connect(endpoint)
Python SDK 支持执行与 CLI 等价的文本命令,用于表管理与元数据查询。
print(client.execute_command("show tables"))
print(client.execute_command("describe user_features"))
自动建表并导入
Python SDK 支持直接从 Parquet 文件导入数据。
client.load_table("user_features", "/data/user_features.parquet")
该接口会读取 Parquet schema 并自动创建匹配表,然后导入数据。注意路径是在 FeatureDB 服务器所在主机上解析的,因此远端部署时必须保证该路径对服务端可见。
数据读取
当前文档仅保留已验证或需要用户重点关注的读取入口。
f0_batch = reader.read_column_window_view(column="f0", begin=1000, length=128)
f0 = np.from_dlpack(f0_batch.features).reshape(-1)
或直接按表读取单列窗口:
time_batch = client.read_column_window(
table="user_features", column="time", begin=1000, length=128
)
times = np.from_dlpack(time_batch.features).reshape(-1)
常见问题
为什么导入失败并提示 schema 不匹配?
通常是因为 Parquet 文件与目标表的列名、列顺序或类型不完全一致。需要逐列核对,特别是时间类型元数据和是否存在 null。
为什么 Python 可以连接但 load_table() 失败?
常见原因是 Parquet 路径是在服务端主机上解析的。如果服务端与客户端不在同一台机器上,客户端本地路径不一定对服务端可见。
什么时候用 CLI,什么时候用 Python SDK?
CLI 适合建表、导入、任务监控和人工排查;Python SDK 适合正式业务读取、自动化接入和低延时访问。
FeatureDB 是否支持普通 SQL 查询?
当前不支持通用
SELECT、INSERT、UPDATE、DELETE。文本命令仅支持有限的操作子集,读取能力主要通过
Python SDK 提供。
