07_Doris AI 能力企业级实践:字节跳动 DataMind 案例深度剖析

张开发
2026/4/9 10:29:25 15 分钟阅读

分享文章

07_Doris AI 能力企业级实践:字节跳动 DataMind 案例深度剖析
07_Doris AI 能力企业级实践字节跳动 DataMind 案例深度剖析关键字Apache Doris、字节跳动DataMind、企业级AI、Hybrid Search实战、IVF_PQ、Faiss IDSelector、Python UDF、GraphRAG、BM25全局统计、大规模向量检索标签Apache Doris字节跳动DataMind企业级AI向量检索实战混合搜索GraphRAG前言技术选型的第一个问题往往是这个技术在大规模生产环境里真正跑过吗对于 Doris 的 AI 能力来说字节跳动 DataMind 就是这个问题的回答。DataMind 是字节跳动内部的一站式 AI 知识检索引擎目前服务于抖音、今日头条、飞书等多条业务线。更重要的是DataMind 团队将他们在字节内部验证过的核心能力——包括基于 Faiss 的 HNSW/IVF_PQ 双算法支持、Tablet-level BM25 全局统计、Bitmap 与 IDSelector 集成等——贡献给了 Doris 开源社区成为了 Doris 4.0 AI 能力的重要技术基础。本文深度剖析 DataMind 的技术架构和关键实现细节这些内容对于理解 Doris 4.0 为什么这样设计有直接帮助。一、DataMind 的选型背景1.1 字节的痛点2023 年底字节跳动的知识管理需求快速膨胀飞书的 Wiki、Lark Docs、内部知识库系统累积了海量非结构化数据需要支持语义检索。原有的技术栈是 Elasticsearch全文检索 自研向量服务ANN 检索双系统维护成本高更严重的问题是两路检索结果在应用层融合时数据一致性难以保证。团队调研了当时主流的方案DataMind 选型评估矩阵 Milvus Weaviate Qdrant pgvector Doris ────────────────────────────────────────────────────────────── 向量检索性能 ◎ ◎ ◎ ○ ◎ 全文检索能力 ✗ ○ ✗ ✗ ◎ 结构化分析能力 ✗ ✗ ✗ ○ ◎ 统一存储/免同步 ✗ ✗ ✗ ○ ◎ 水平扩展能力 ◎ ◎ ○ ○ ◎ 运维成本 中 中 低 低 已有 社区开放度 好 好 好 一般 好 ────────────────────────────────────────────────────────────── ◎极好 ○一般 ✗不支持Doris 胜出的关键点是统一存储和团队内部已有深厚积累字节是 Doris 最大的工业用户之一。1.2 DataMind 的定位DataMind 定位为字节内部的一站式 AI 数据智能引擎核心目标是DataMind 能力全景 ┌─────────────────────────────────────────────────────────────┐ │ DataMind 一站式引擎 │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Hybrid Search│ │ AI Function │ │ GraphRAG │ │ │ │ 混合语义检索 │ │ SQL内大模型 │ │ 知识图谱推理 │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ──────────── Apache Doris 4.0 统一存储与计算 ──────────── │ └─────────────────────────────────────────────────────────────┘二、Hybrid Search 技术实现2.1 双算法 ANN 支持DataMind 在 Doris 中实现了两种 ANN 算法的并行支持这也是 Doris 4.0 官方版本中向量索引支持双算法的技术来源HNSW在线高性能查询HNSW 在 DataMind 的部署参数实测调优结果 数据规模5000 万条文档768 维向量 ────────────────────────────────────────────────────────── 参数 生产值 说明 max_degree 48 比默认 32 高提升召回率内存可接受 ef_construction 100 索引质量高构建耗时约 6 小时 ef_search 60 P99 延迟 20ms召回率 98% quantizer sq8 内存从 150GB 压缩到 50GB ──────────────────────────────────────────────────────────IVF_PQ大规模内存受限场景对于超过 1 亿条向量、内存严重受限的场景DataMind 团队贡献了 IVFInverted File Index算法的实现IVF_PQ 索引原理 训练阶段 1. 用 K-means 将全量向量聚类为 N 个质心nlist 个 cluster 2. 每个向量记录它所属的 cluster ID 查询阶段 1. 计算查询向量与所有质心的距离 2. 选取最近的 nprobe 个 clusternprobe 越大召回率越高 3. 在这 nprobe 个 cluster 内精确搜索 关键参数 nlist 4096 # cluster 数量通常设为 sqrt(N) 左右 nprobe 64 # 查询时检索的 cluster 数量越大越慢越准 m 8 # PQ 子向量数768/8 96 个子向量 nbits 8 # 每个子向量的量化位数 内存特性 IVF_PQ 只需加载倒排列表不需要全量数据驻留内存 1 亿条 768 维向量FLAT 需要 ~300GBIVF_PQ 只需 ~8GB2.2 Tablet-level BM25 全局 IDF 统计这是 DataMind 最重要的技术贡献之一解决了长期困扰分布式全文检索的 IDF 不准确问题。问题背景Doris 数据按 Tablet 分片存储每个 Tablet 包含若干 Segment。在标准 BM25 中IDF逆文档频率需要基于全局文档数计算但在分布式环境里每个 Tablet 只知道自己的局部文档数导致分数不可比。DataMind 的解决方案Tablet-level BM25 全局 IDF 统计方案 写入时 每个 Tablet 的 Compaction 完成后统计本 Tablet 内的 - 总文档数N_local - 每个 Term 的文档频率df_local 并将统计信息上报到 FE 的全局元数据存储 查询时 FE 聚合所有 Tablet 的统计信息 N_global Σ N_local各 Tablet df_global Σ df_local各 Tablet 计算全局 IDF IDF(t) log((N_global - df_global 0.5) / (df_global 0.5) 1) 将全局 IDF 下推给各 BE 节点BE 用全局 IDF 计算本地文档的 BM25 分数这个方案使得 Doris 的 BM25 分数达到了与 Elasticsearch 单节点相当的准确性是混合搜索中 BM25 路可以和向量路做可靠融合的前提条件。2.3 Bitmap 与 Faiss IDSelector 集成DataMind 实现的预过滤机制是 Doris 混合搜索架构的核心基础设施# IDSelector 集成的伪代码内核实现简化版classDorisANNWithFilter:defsearch(self,query_vector,where_conditions,top_k):# Step 1: 执行结构化倒排过滤生成 Bitmapbitmapself.inverted_index.evaluate(where_conditions)# bitmap {23, 45, 89, 156, ...} 满足条件的 doc_id 集合# Step 2: 将 Bitmap 转换为 Faiss IDSelectorid_selectorfaiss.IDSelectorBatch(list(bitmap))# Step 3: HNSW 搜索仅在 IDSelector 范围内search_paramsfaiss.SearchParametersHNSW(efSearch60,selid_selector# 关键传入过滤掩码)distances,indicesself.hnsw_index.search(query_vector,ktop_k,paramssearch_params)returndistances,indicesdef_should_brute_force(self,bitmap_size,total_docs):当候选集极少时暴力计算比 HNSW 更高效ratiobitmap_size/total_docsreturnratio0.01# 候选集不足 1% 时降级为暴力计算何时降级为暴力计算DataMind 的经验是当过滤后候选集占总文档数的比例小于 1-5% 时暴力遍历候选集的成本反而低于 HNSW 的图遍历成本此时自动降级。这个阈值已被内置到 Doris 4.0 的实现中。三、AI Function 生产实践3.1 TEXT_EMBEDDING 在 DataMind 的使用DataMind 面临的 Embedding 规模是每天新增 50 万 文档需要实时生成并写入向量。他们的方案-- DataMind 的 Embedding 写入管道简化版-- 通过 Doris 的物化视图当 raw_documents 有新数据时自动触发向量生成CREATEMATERIALIZEDVIEWmv_doc_embeddings BUILD IMMEDIATE REFRESHONDEMANDASSELECTdoc_id,chunk_id,content,TEXT_EMBEDDING(bytedance_embed_model,content)ASembedding,NOW()ASembed_timeFROMraw_doc_chunksWHEREembedded0;-- 定时刷新每 15 分钟一次-- 字节内部的 Embedding 服务通过 openai.endpoint 适配器暴露3.2 Python UDF 多进程架构DataMind 还贡献了 Doris 的 Python UDF 功能解决了在 Doris 内部运行 Python 机器学习模型的工程问题。核心挑战Python 的 GIL全局解释器锁使得多线程无法利用多核大模型推理本质上是 CPU/GPU 密集型任务。解决方案多进程架构 Marshal 序列化Python UDF 多进程架构 Doris BE 主进程 │ │ 任务分发分片数据 ▼ ┌─────────────────────────────────────────────┐ │ Python UDF 管理进程 │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Worker-1 │ │ Worker-2 │ │ Worker-N │ │ │ │独立进程│ │独立进程│ │独立进程│ │ │ │ 各自持有 │ │ 各自持有 │ │ 各自持有 │ │ │ │ 模型副本 │ │ 模型副本 │ │ 模型副本 │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ 数据传输Marshal 序列化比 pickle 快 3x │ └─────────────────────────────────────────────┘使用示例在 Doris 中注册 Python UDF 进行自定义推理# 注册到 Doris 的 Python UDF 脚本importtorchfromtransformersimportAutoModel,AutoTokenizer# 模型在进程启动时加载一次避免每次调用重载MODEL_NAMEBAAI/bge-large-zh-v1.5tokenizerAutoTokenizer.from_pretrained(MODEL_NAME)modelAutoModel.from_pretrained(MODEL_NAME)defprocess_batch(texts):批量处理提高 GPU 利用率inputstokenizer(texts,return_tensorspt,paddingTrue,truncationTrue)withtorch.no_grad():outputsmodel(**inputs)# 取 [CLS] token 的向量embeddingsoutputs.last_hidden_state[:,0,:].numpy().tolist()returnembeddings-- 在 Doris SQL 中调用 Python UDFSELECTdoc_id,content,custom_embedding(content)ASembedding-- 调用 Python UDFFROMraw_documentsLIMIT1000;四、GraphRAG 的探索DataMind 在 2025 年开始探索 GraphRAG 与 Doris 的结合将知识图谱的多跳推理能力嫁接到 RAG 系统上。4.1 为什么需要 GraphRAG标准 RAG 的弱点在于多步推理标准 RAG vs GraphRAG 处理复杂问题 问题飞书的研发负责人的前任老板现在在哪家公司任职 标准 RAG单次检索 搜索 飞书研发负责人 → 找到人名 A 搜索 A 的经历 → 可能找不到 结果无法回答 GraphRAG多跳推理 Step1: 实体识别 → 飞书、研发负责人 Step2: 图查询 → 飞书 -[研发负责人]→ 人名 A Step3: 图查询 → 人名 A -[前任上级]→ 人名 B Step4: 图查询 → 人名 B -[当前任职]→ 公司 C 结果给出完整推理链4.2 DataMind 的 GraphRAG 实现DataMind 的 GraphRAG 采用 Doris 图数据库的混合架构DataMind GraphRAG 架构 文档/实体 │ ▼ 实体抽取NER AI_EXTRACT 实体关系三元组主体-关系-客体 │ ├──▶ Doris存储实体属性和向量语义检索 └──▶ 图数据库存储实体关系图图遍历查询 查询时 │ ├── 语义检索Doris 混合搜索→ 候选文档 └── 图推理图数据库路径查询→ 关系链 │ ▼ 结果融合 LLM 生成综合回答这套架构的核心优势是用 Doris 处理文档级别的语义检索速度快、召回准用图数据库处理实体间的多跳关系查询专门优化图遍历。两者各取所长通过应用层融合。五、规模化落地经验5.1 性能数据DataMind 在字节内部的实测数据2025 年 Q4 生产数据DataMind 生产环境性能指标 数据规模2 亿条文档768 维向量 集群规模20 台 BE每台 64 核 256GB 内存 ────────────────────────────────────────────────────────── 场景 P50 P99 QPS 纯向量检索 8ms 22ms 5000 BM25 全文检索 5ms 15ms 8000 混合检索RRF 15ms 45ms 3000 带预过滤的向量检索 12ms 35ms 4000 AI_SENTIMENT 批量 - - 2000条/分钟 ──────────────────────────────────────────────────────────5.2 成本优化策略向量内存成本2 亿条 768 维向量FLAT 模式需要约 600GB 内存采用 SQ8 量化后降至约 200GB单机 256GB 内存可以承载配合 IVF_PQ超热数据用 HNSW冷数据用 IVF_PQ整体内存降至 80GBLLM 调用成本AI 函数不放在实时查询路径批量任务如每日新文档的情感分析、分类统一在低谷期凌晨 2-6 点执行优先使用字节内部自研的轻量模型仅在复杂任务时调用大模型5.3 坑和教训坑1HNSW 内存估算不准导致 OOMDataMind 早期曾因低估 HNSW 图结构的内存开销而出现 BE OOM。图结构的开销不只是向量本身还包括每个节点的邻居列表约max_degree × 4 bytes × N。坑2Compaction 期间 BM25 IDF 统计不准确在 Compaction 进行时部分 Segment 的统计信息可能暂时失效导致 BM25 分数出现抖动。解决方案是在 Compaction 完成后强制触发 IDF 全局刷新。坑3Python UDF 进程泄漏早期 Python UDF 的多进程管理存在进程泄漏问题长时间运行后系统出现 CPU 异常高。后来通过进程池 健康检查 定期轮替机制解决。六、对社区的贡献DataMind 团队向 Doris 社区贡献的关键功能已合入 Doris 4.0 主线贡献内容影响HNSW 向量索引基于 FaissANN 检索核心能力IVF_PQ 向量索引大规模内存受限场景支持Tablet-level BM25 IDF 统计分布式全文检索分数准确性Bitmap IDSelector 集成预过滤性能大幅提升Python UDF 多进程架构自定义模型集成能力TEXT_EMBEDDING 函数数据库内 Embedding 生成小结DataMind 案例对于我们理解 Doris 4.0 AI 能力有几个重要启示这些能力已在生产级大规模场景验证2 亿文档、3000 QPS 混合检索不是 paper 数据双算法策略很实用在线场景用 HNSW超大规模用 IVF_PQ不同场景用不同工具BM25 全局 IDF 是混合搜索的基础没有这个保证BM25 和向量分数根本无法融合AI 函数做批量向量做实时混合使用才是最优策略下一篇讲全文搜索的进阶细节BM25 算法原理 SEARCH 函数 DSL 自定义分词器Doris 全文搜索能力全貌。

更多文章