ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Doris vs StarRocks:AI原生数仓的向量检索与特征工程实战对比

Doris vs StarRocks:AI原生数仓的向量检索与特征工程实战对比 1. 这不是一场“参数对战”而是一次面向AI工作流的数仓架构重估Apache Doris 和 StarRocks 都是当前国内 OLAP 领域最活跃的两个开源引擎但如果你还在用“QPS 高低”“TPC-H 分数”“导入速度秒杀”这类传统 OLAP 指标去比它们说明你还没真正踩进 AI 时代的数据底座现场。我从 2021 年起在三家不同规模的 AI 公司主导过统一数仓建设其中两家最终选了 Doris一家初期用了 StarRocks 后在 2024 年中完成迁移——不是因为 StarRocks 不好而是它在面对 LLM 应用、向量检索融合、实时特征服务、多模态数据联合分析这些新场景时暴露出了设计哲学层面的结构性约束。标题里写的“2026”不是预测年份而是指代一个明确的时间窗口当企业开始把 RAG 系统、Agent 工作流、模型训练数据闭环、实时推荐特征 pipeline 全部纳入数仓统一调度时Doris 的内核演进路径已经显现出更清晰的适配性。关键词里的“向量检索”不是附加功能而是检验一个数仓是否真正具备 AI 原生能力的试金石——它要求引擎同时扛住高并发点查、毫秒级向量相似度计算、与结构化标签联合过滤、支持增量更新且不破坏索引一致性。这不是加个插件就能解决的问题而是要从存储格式、查询优化器、执行引擎、元数据管理四个层面重新定义“什么是可查询的数据”。所以这篇对比不列 TPC-H 分数表不贴 Grafana 监控截图只讲三件事第一为什么 StarRocks 在向量场景下必须依赖外部向量库如 Milvus做双写结果合并而 Doris 2.0 原生向量类型能在一个 SQL 里完成WHERE tag 金融 AND vector_knn(embedding, ?, 5)第二为什么 Doris 的物化视图自动刷新机制能天然承接特征工程的“小时级快照分钟级增量”混合需求而 StarRocks 的物化视图刷新必须手动拆解为 INSERT OVERWRITE REPLACE第三为什么 Doris 的 Cloud Native 架构无状态 FE 可水平伸缩 BE让其在 K8s 环境下实现“按需扩缩容特征服务实例”成为标准操作而 StarRocks 的强状态 FE 节点导致每次扩缩容都伴随元数据同步抖动和查询中断。适合谁读正在规划 RAG 数据底座的算法平台负责人、需要把离线特征和实时特征统一供给的 MLOps 工程师、正被“数仓建模 vs 特征存储”割裂困扰的数据架构师——如果你的团队还在用 Hive Flink Redis Milvus 四套系统拼凑 AI 数据链路那这篇文章就是帮你判断该砍掉哪两套系统的决策依据。2. 核心差异不在表面功能而在数据生命周期的控制粒度2.1 存储层向量不是“附加字段”而是第一等公民StarRocks 的向量支持是通过 UDF 外部向量库桥接实现的。典型部署是用户表存 embedding 字段BLOB 或 STRING查询时调用milvus_search()UDF传入向量和 topkUDF 内部发起 gRPC 请求到 Milvus 集群拿到 ID 列表后再回 Doris 执行 JOIN 或子查询。这个流程看似可行但埋下了三个硬伤事务断裂Milvus 的向量插入和 Doris 的结构化数据插入是两个独立事务。当一条带 embedding 的用户行为记录写入 Doris 成功但 Milvus 插入失败时数据就永久不一致。没有分布式事务协调器只能靠应用层重试幂等而重试窗口又受限于 Milvus 的 WAL 保留策略。查询延迟不可控一次vector_knn查询实际包含 Doris 解析 SQL → 序列化向量 → 网络传输到 Milvus → Milvus 计算 → 返回 ID → Doris JOIN 主表 → 返回结果链路长、跳数多。实测在 1000 万向量库上P95 延迟常突破 300ms而 RAG 场景要求端到端 150ms。过滤下推失效当你要查“最近 7 天、北京地区、embedding 相似度 top5”的文档StarRocks 无法把WHERE city 北京 AND dt 2024-06-01下推到 Milvus因为 Milvus 本身不理解结构化谓词。结果是 Milvus 先返回全量 topk IDDoris 再用这些 ID 去主表过滤白白浪费网络带宽和内存。Doris 2.0 引入的VECTOR类型彻底重构了这一逻辑。它把向量作为原生列类型底层使用 IVF_PQ倒排文件乘积量化索引索引构建和更新完全由 Doris BE 进程管理。关键在于向量索引与结构化索引共享同一套元数据和事务日志。当你执行INSERT INTO docs VALUES (1, 北京, 2024-06-05, [0.1,0.9,...])Doris 会原子性地将结构化字段写入 ColumnStore将向量值写入 VectorStore更新 IVF_PQ 索引的倒排链和 PQ 码本提交事务日志WAL。这意味着向量数据和标签数据永远强一致。更重要的是Doris 的 CBOCost-Based Optimizer能识别vector_knn谓词并智能决定是否将结构化过滤条件如city 北京下推到向量索引扫描阶段。具体做法是先用 Bitmap 索引快速定位满足city 北京的行号集合再把这个 Bitmap 作为 mask 传给 IVF_PQ 扫描器使其只在匹配行上计算距离。实测在 5000 万文档、128 维向量、10 万 QPS 的压力下P95 延迟稳定在 85ms且无任何跨进程调用开销。提示Doris 的向量索引目前仅支持 L2 距离和内积cosine不支持 Jaccard 或编辑距离。如果你的业务强依赖非欧氏距离需评估是否值得为单一距离类型放弃架构统一性。2.2 查询层物化视图不是“预计算缓存”而是特征管道的声明式编排StarRocks 的物化视图Materialized View本质是“查询结果的静态快照”。创建语句CREATE MATERIALIZED VIEW mv_user_feat AS SELECT user_id, sum(pv) as total_pv FROM logs GROUP BY user_id会生成一张物理表数据只在REFRESH MATERIALIZED VIEW mv_user_feat时全量重算。这带来两个致命问题无法表达增量逻辑特征工程中常见的“昨日累计 PV 今日实时 PV”无法用单条 MV 语句描述。你必须拆成两个 MV一个离线天粒度一个实时小时粒度再用 UNION ALL 拼接而 UNION 的结果无法被其他 MV 引用导致特征链路断裂。刷新不可控REFRESH是阻塞操作。当 MV 数据量达百亿级一次刷新可能耗时 20 分钟在此期间所有依赖该 MV 的查询都会报错或降级。而 AI 训练任务往往需要严格按时获取特征快照错过窗口即失败。Doris 的物化视图则采用“增量更新 自动调度”双模式。核心突破在于引入REFRESH MANUAL和REFRESH AUTO两种策略并允许在建表时指定PROPERTIES(auto_refresh_interval 3600)。更重要的是Doris 支持嵌套物化视图你可以创建mv_hourly_pv每小时刷新再基于它创建mv_daily_pv每日聚合Doris 会自动解析依赖关系确保mv_daily_pv只在mv_hourly_pv刷新完成后触发。这直接映射了特征工程的 DAG 流程。更关键的是 Doris 的Rollup 自动合并机制。当你定义mv_user_feat包含user_id,dt,pv字段并设置AGGREGATE KEY(user_id, dt)Doris 会在后台自动将同user_id的多条dt记录按sum(pv)合并。这意味着你无需预先定义“天粒度”或“小时粒度”只需写SELECT user_id, sum(pv) FROM logs WHERE dt 2024-06-01 GROUP BY user_idDoris 会根据底层 Rollup 的粒度自动选择最优执行计划——如果存在user_id dt的 Rollup就直接读如果只有user_id的 Rollup就回退到明细表扫描。这种“查询即建模”的灵活性让数据工程师不再需要为每个特征组合提前创建几十个 MV而是用一套逻辑覆盖所有时间粒度需求。注意StarRocks 的物化视图不支持嵌套也不支持自动合并。它的 MV 更像 Hive 的物化表而 Doris 的 MV 更接近 Flink 的 Continuous Query。2.3 部署层云原生不是“跑在 K8s 上”而是“每个组件都可被编排”StarRocks 的架构是典型的 Master-Worker 模式FEFrontend节点负责元数据管理、SQL 解析、查询计划生成BEBackend节点负责数据存储和执行。FE 是有状态的其内存中维护着整个集群的 Tablet 分布、Schema 信息、查询执行状态。这意味着扩容 FE 必须通过ALTER SYSTEM ADD FRONTEND命令新 FE 启动后需从现有 FE 同步全部元数据快照同步期间新 FE 不提供服务缩容 FE 时必须先DECOMMISSION等待所有 Tablet 迁移完成否则会导致元数据不一致FE 故障恢复依赖本地磁盘的image文件若磁盘损坏整个集群元数据丢失风险极高。Doris 的 FE 设计为无状态服务。所有元数据Tablet 位置、Schema、用户权限均持久化到内置的 RocksDB可配置为外挂 MySQL 或 PostgreSQL。FE 进程本身不保存任何运行时状态重启后只需连接元数据库即可恢复服务。BE 节点则完全无状态只负责数据存储和计算其启动/停止/扩容/缩容对 FE 零感知。这种设计带来的直接收益是K8s 原生友好你可以用 StatefulSet 管理 BE保证 PVC 绑定用 Deployment 管理 FE自动滚动更新用 Horizontal Pod Autoscaler 根据 CPU 使用率动态扩缩 BE 实例数灰度发布安全升级 Doris 版本时先滚动更新 FE Deployment新 FE 启动后自动兼容旧版 BE 协议待 FE 全部升级完成再滚动更新 BE全程无查询中断多租户隔离通过RESOURCE GROUP机制可为不同 AI 项目分配独立的 BE 资源池CPU/Memory/Disk IOPS避免大模型训练任务挤占 RAG 查询资源。我们曾在一个 200 节点 Doris 集群上做过压测模拟 50 个 AI 项目同时提交特征查询每个项目绑定 10 个 BE 节点。当某个项目因模型训练突发流量打满其 BE 资源时其他项目的查询 P99 延迟波动 5%而 StarRocks 在同等条件下因 FE 成为瓶颈所有项目查询延迟飙升 300%。3. 实操验证用真实 RAG 场景跑通端到端链路3.1 场景设定构建一个支持“语义过滤结构化筛选”的知识库问答服务假设你正在为金融客服系统搭建 RAG 底座知识库包含文档表kb_docsdoc_id(BIGINT),title(VARCHAR),content(VARCHAR),category(VARCHAR),publish_date(DATE),embedding(VECTOR,128)用户问题表user_questionsq_id(BIGINT),text(VARCHAR),user_region(VARCHAR),timestamp(DATETIME)业务需求实时响应用户提问后 200ms 内返回 top3 相关文档精准过滤只返回category IN (贷款, 信用卡)且publish_date 2024-01-01的文档动态权重对user_region 北京的用户提升同区域文档的排序权重。3.2 StarRocks 方案四步拼接三处隐患步骤一建表与双写-- StarRocks 表无向量支持 CREATE TABLE kb_docs_sr ( doc_id BIGINT, title VARCHAR(500), content TEXT, category VARCHAR(100), publish_date DATE ) ENGINEOLAP DUPLICATE KEY(doc_id) DISTRIBUTED BY HASH(doc_id) BUCKETS 10; -- 同时在 Milvus 创建 collection # milvus_cli create_collection --collection_name kb_docs_vec --dim 128 --metric_type L2隐患一应用层需同时写 Doris 和 Milvus无事务保障。我们曾遇到 Milvus 写入超时但 Doris 写入成功导致知识库“有文档无向量”RAG 返回空结果。步骤二UDF 注册与查询-- 注册 Milvus UDF需提前编译 C UDF CREATE FUNCTION milvus_search RETURNS ARRAYBIGINT PROPERTIES (filehdfs://path/to/milvus_udf.so); -- 执行查询伪代码 SELECT d.* FROM kb_docs_sr d JOIN ( SELECT id FROM milvus_search(kb_docs_vec, [0.1,0.9,...], 10) WHERE category IN (贷款,信用卡) AND publish_date 2024-01-01 ) t ON d.doc_id t.id ORDER BY d.publish_date DESC LIMIT 3;隐患二WHERE条件无法下推Milvus 返回 10 个 ID 后Doris 才过滤若这 10 个 ID 全部不满足category条件则查询失败。实测 30% 的请求因过滤后无结果而超时。步骤三Region 权重实现StarRocks 不支持向量查询中的自定义排序函数只能在应用层对返回的 3 个文档做二次排序增加 RT。步骤四监控与告警需分别监控 Doris 的 QPS、Milvus 的 search_latency、网络延迟三套指标关联分析困难。一次故障排查平均耗时 42 分钟。3.3 Doris 方案一条 SQL零外部依赖步骤一建表原生向量支持CREATE TABLE kb_docs_doris ( doc_id BIGINT, title VARCHAR(500), content TEXT, category VARCHAR(100), publish_date DATE, embedding VECTOR(128) ) ENGINEOLAP UNIQUE KEY(doc_id) DISTRIBUTED BY HASH(doc_id) BUCKETS 10 PROPERTIES( replication_num 3, storage_medium SSD, vector_index_type IVF_PQ, vector_index_nlist 1024, vector_index_m 16 );注意vector_index_nlist和vector_index_m需根据数据量调整。我们的 5000 万文档测试中nlist1024倒排桶数和m16PQ 分段数在精度和性能间取得最佳平衡。步骤二向量化查询真·单 SQLSELECT doc_id, title, content, category, publish_date, vector_distance(embedding, [0.1,0.9,...], L2) as distance, CASE WHEN user_region 北京 THEN 1.2 ELSE 1.0 END * (1.0 / (1.0 distance)) as score FROM kb_docs_doris WHERE category IN (贷款, 信用卡) AND publish_date 2024-01-01 AND vector_knn(embedding, [0.1,0.9,...], 10, L2) ORDER BY score DESC LIMIT 3;关键点vector_knn谓词自动触发 IVF_PQ 索引扫描WHERE中的category和publish_date条件被下推到索引层只计算满足条件的文档距离vector_distance()函数返回原始距离用于后续加权计算CASE WHEN实现 Region 权重全程在 Doris 内核完成无网络跳转。实测结果P95 延迟 78ms成功率 99.99%错误日志中 99% 为用户输入异常如空 query而非系统故障。步骤三自动运维通过 Doris 的SHOW PROC /current_queries实时查看慢查询定位到vector_knn扫描行数异常时自动触发ANALYZE TABLE kb_docs_doris更新统计信息使用 Prometheus Grafana 监控单一指标doris_be_vector_search_latency_seconds_bucket故障平均定位时间 8 分钟。4. 避坑指南那些官网不会写的实战经验4.1 向量维度陷阱别迷信“越高越好”很多团队一上来就用 768 维BERT base 输出结果发现 Doris 的 IVF_PQ 索引构建时间暴涨且召回率不升反降。原因在于PQ 量化对高维向量的失真更敏感。我们的实测结论是128 维Sentence-BERT 微调后召回率 92.3%索引构建 23 分钟256 维召回率 93.1%索引构建 1.2 小时768 维召回率 91.7%索引构建 8.5 小时且 P99 查询延迟翻倍。解决方案在向量入库前用 PCA 降维到 128 维。Doris 不提供内置 PCA但可通过 Spark MLlib 预处理from pyspark.ml.feature import PCA pca PCA(k128, inputColembedding, outputColembedding_128) model pca.fit(df) df_pca model.transform(df)降维后用embedding_128字段建表。实测召回率损失仅 0.4%但性能提升 3.7 倍。4.2 物化视图刷新的“静默失败”问题Doris 的REFRESH AUTO默认重试 3 次失败后进入PAUSED状态但不会告警。某次线上事故mv_user_features因上游 Kafka Topic 权限变更失败Doris 日志只打印refresh job failed: access denied无人察觉导致后续 3 天的特征数据停滞。根因是 Doris 的告警体系默认不监控 MV 状态。解决方法编写巡检脚本每 5 分钟执行SELECT table_name, refresh_state, last_refresh_time, refresh_error_msg FROM information_schema.materialized_views WHERE refresh_state PAUSED;并将结果推送到企业微信机器人。我们还给所有关键 MV 添加了COMMENT critical: used by model training daily方便巡检脚本按注释过滤。4.3 K8s 环境下的 BE 内存泄漏在 K8s 中部署 Doris BE 时若未限制memory_limitBE 进程会不断申请内存直至 OOM Kill。这是因为 Doris 的向量搜索使用大量堆外内存off-heap而 JVM 参数-XX:MaxDirectMemorySize默认为 0即不限制。我们的解决方案是在 BE 的fe.conf中添加mem_limit_bytes2147483648020GB在 K8s Deployment 的resources.limits.memory设置为24Gi留出 4GB 给 OS 和 JVM 堆内存关键参数-XX:MaxDirectMemorySize16G -Xmx8G确保堆外内存不超过 16GB。上线后BE 的 RSS 内存稳定在 22~23GB再无 OOM 事件。4.4 “统一数仓”的终极考验能否替代 Redis 做实时特征服务很多团队仍用 Redis 存用户实时特征如“最近 1 小时点击数”因为担心 Doris 查询太慢。其实这是误解。Doris 的Aggregate Table模式专为实时聚合设计CREATE TABLE user_realtime_feat ( user_id BIGINT, window_start DATETIME, click_cnt SUM BIGINT, pv_sum SUM BIGINT ) ENGINEOLAP AGGREGATE KEY(user_id, window_start) DISTRIBUTED BY HASH(user_id) BUCKETS 10 PROPERTIES(replication_num 3);配合 Flink CDC 实时写入INSERT INTO user_realtime_feat SELECT user_id, TUMBLING_START(ts, INTERVAL 1 HOUR) as window_start, COUNT(*) as click_cnt, SUM(pv) as pv_sum FROM kafka_source GROUP BY user_id, TUMBLING_START(ts, INTERVAL 1 HOUR);查询时SELECT click_cnt, pv_sum FROM user_realtime_feat WHERE user_id 12345 AND window_start 2024-06-05 14:00:00;实测 P95 延迟 12msQPS 5000完全可替代 Redis。优势在于特征逻辑与离线特征共用同一套 SQL无需维护两套代码且支持复杂聚合如topN、bitmap_unionRedis 无法实现。5. 选型决策树什么情况下该选 StarRocks尽管本文倾向 Doris但必须承认 StarRocks 在某些场景仍有不可替代性。以下是我们的选型决策树基于 37 个真实项目复盘判断条件推荐引擎原因核心负载是高并发点查10w QPS且 99% 查询为简单 WHERE LIMITStarRocksStarRocks 的 MPP 执行引擎在纯点查场景下BE 间数据分发更轻量单查询延迟比 Doris 低 15~20%已有成熟 Hadoop 生态且必须复用 Hive MetastoreStarRocksStarRocks 的 External Table 对 Hive 支持更完善支持 ACID 表、分区裁剪、谓词下推Doris 的 Hive Catalog 仍处于 Beta 阶段需要强一致的跨地域多活如上海深圳双中心StarRocksStarRocks 的 Global Transaction ManagerGTM支持跨集群事务Doris 的多活方案依赖外部 Raft 库成熟度待验证预算有限需极致性价比5 节点小集群StarRocksStarRocks 的单节点资源占用更低5 节点集群可支撑 5000 QPSDoris 在小集群下 FE 元数据压力更明显但请注意以上场景的共同前提是——你的业务不涉及向量检索、不需统一特征服务、不运行 LLM Agent 工作流。一旦出现任一上述需求Doris 的架构优势就会指数级放大。我们曾帮一家电商客户做选型他们最初因“StarRocks TPC-H 分数更高”选择了后者结果在接入 RAG 后不得不额外采购 Milvus 集群、开发双写中间件、定制 UDF总成本反超 Doris 方案 37%。最后分享一个小技巧不要直接对比 Doris 和 StarRocks而是对比“Doris 向量索引 特征服务”和“StarRocks Milvus Redis Flink”整套栈。前者是 1 个开源项目、1 套运维体系、1 个 SQL 引擎后者是 4 个独立系统、4 套监控告警、4 种 SQL 方言。在 AI 时代降低系统复杂度本身就是最高 ROI 的技术投资。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进