搜索运营刚发现某个关键词流量异常,第一反应不是看昨天报表,而是立刻按实验组、渠道和 SKU 下钻。此时系统同时面对四个条件:数据刚产生、查询维度临时变化、访问并发很高、结果还要直接出现在产品页面。
只满足其中一个并不难。难的是四个条件同时成立。
MPP 只解决一条复杂 SQL 怎样并行执行。Doris 真正有差异的地方,是让刚写入的明细不经过固定指标加工,就能继续承接高并发分析查询。
同一个搜索分析需求,会把四套常见方案逼到不同边界
先固定同一组条件再比较:搜索事件持续写入,数据需要秒级可见,用户可能临时增加分析维度,外部页面存在高并发访问,而且不能返回交易库中的半成品状态。
| 方案 | 数据新鲜度 | 临时增加维度 | 高并发复杂聚合 | 真正要付出的代价 |
|---|---|---|---|---|
| MySQL 只读副本 | 高 | SQL 可以改 | 宽表扫描、聚合和并发会与行存路径冲突 | 分库分表、缓存与汇总表不断增加 |
| Hive/Spark 离线数仓 | 低到中 | 灵活 | 适合大批量计算,不适合直接承接在线请求 | 用户看到的是上一批数据 |
| Flink 预聚合 + Redis | 高 | 低 | 已定义指标的查询很快 | 新维度意味着新状态、新 Job 和历史回灌 |
| Elasticsearch | 高 | 中 | 检索与过滤强,复杂 Join 和统一指标治理成本较高 | 数据副本、映射和聚合口径需要额外维护 |
| Doris | 高 | 高 | 明细写入后继续使用 SQL 聚合、Join 和下钻 | 写入建模、分区分桶、资源隔离必须设计正确 |
这张表没有绝对冠军。固定指标、极端低延迟读可以继续用预计算和缓存;全文相关性是核心时专用搜索系统仍然重要;交易写入仍应留在 MySQL。Doris 优秀的是条件交集:既不想把所有指标提前算死,又必须让新数据迅速进入查询服务。
京东案例最值钱的不是跑分,而是删除了一层固定计算
Apache Doris 客户侧分析官方案例 给出的京东搜索框场景包括整体搜索流量、在线 A/B 实验和热词榜,并要求下钻到 SKU 粒度。官方页面给出的原始结果包括日处理百亿行、最高万级 QPS、最低 150ms 查询延迟和每秒百万行实时导入。
这些数字不能直接换算成其他集群的 SLA。官方页面没有完整披露机器配置、SQL 复杂度、缓存命中率、数据倾斜和延迟分位数,拿最低 150ms 承诺 P99 没有意义。
案例真正值得学的是官方总结的架构变化:用 Doris 替代 Flink 窗口计算后,维度变化更容易适配,开发和计算资源成本下降。
旧路径 事件 → Flink 固定窗口与维度 → 结果表或缓存 → 页面 ↑ 新增维度就要改 Job、重算历史 Doris 路径 事件 → Doris 明细或状态表 → SQL 按当前问题聚合 → 页面 ↑ 查询口径可以继续变化这不是 Doris 比 Flink 更强。Flink 擅长有状态流处理、CEP 和复杂事件计算;问题在于把每个尚未稳定的分析问题都提前固化成流任务。Doris 接管的是可由 SQL 在查询时决定的那部分计算。
MPP 解决计算规模,写入后可查还需要另外三件事
Doris 官方概览 把产品定位为实时分析数据库,并强调同一系统支持高并发点查与高吞吐复杂分析。MPP 只是其中的执行骨架。
一条新事件能否立刻用于复杂查询,还取决于三层约束:
写入语义:事务何时提交,数据何时可见 数据语义:明细全部保留,还是同主键只保留当前状态 服务语义:并发上升后,导入与查询是否仍能获得稳定资源缺少任何一层,MPP 都救不了结果。
- 事务没有发布,BE 再多也查不到新数据;
- 订单状态表选错 Key 模型,查询越快,错误结果返回得越快;
- 大查询与在线接口没有隔离,高峰期的 P99 仍然会失控。
所以 Doris 的技术选型不能写成 MPP、列存、向量化三个词。真正的判断是:业务是否需要把持续写入、可变分析和查询服务放在同一个数据副本上。
一个有用的 POC,要故意改变问题而不是重复跑同一条 SQL
准备一张搜索事件表,保留查询词、实验组、渠道、SKU 和事件时间。POC 第一轮只查询热词,第二轮临时增加实验组,第三轮继续下钻到 SKU。
CREATETABLEsearch_event(event_timeDATETIME,query_textVARCHAR(200),experiment_idVARCHAR(32),channelVARCHAR(32),sku_idBIGINT,user_idBIGINT)DUPLICATEKEY(event_time,query_text)DISTRIBUTEDBYHASH(user_id)BUCKETS AUTO PROPERTIES("replication_num"="1");上面为了便于单副本 POC 使用replication_num = 1,生产环境不能照搬。分区需要按照目标集群的动态分区或自动分区规范补齐。
第一轮问题:最近五分钟哪些搜索词突然升高。
SELECTquery_text,COUNT(*)ASsearchesFROMsearch_eventWHEREevent_time>=NOW()-INTERVAL5MINUTEGROUPBYquery_textORDERBYsearchesDESCLIMIT20;第二轮不改写入链路,只改变分析问题:异常是否只发生在某个实验组和渠道。
SELECTexperiment_id,channel,query_text,COUNT(*)ASsearches,COUNT(DISTINCTuser_id)ASusersFROMsearch_eventWHEREevent_time>=NOW()-INTERVAL5MINUTEGROUPBYexperiment_id,channel,query_textORDERBYsearchesDESCLIMIT50;这两条 SQL 的意义不是证明 Doris 一定快,而是验证数据是否保留了继续提问的能力。Flink 预聚合方案如果只保存了query_text + window,第二个问题需要新增状态并回灌;Doris 明细表只需要改变 SQL,但要承担更多存储、扫描和并发治理成本。
验收时只看平均耗时,会把最关键的失败藏起来
POC 必须同时记录四条结果:
| 证据 | 测量方法 | 淘汰条件 |
|---|---|---|
| 新鲜度 | 写入带唯一 ID 和源端时间的哨兵记录,轮询到首次可见 | 故障恢复后仍无法满足业务时限 |
| 正确性 | 对账主键集合、行数、最大业务时间和核心聚合 | 任务成功但业务结果不一致 |
| 查询尾延迟 | 保存 P50、P95、P99,并逐步增加并发 | 只有单查询快,并发后长尾失控 |
| 可变分析成本 | 临时增加维度、过滤和 Join,记录是否需要改写链路 | 每次需求变化仍要新建流任务和结果表 |
查询侧至少保存EXPLAIN与 Query Profile。Query Profile 官方文档 说明 Profile 会记录算子耗时、行数和内存;总耗时相同的两次查询,可能分别卡在 Scan、Exchange 或某个长尾实例,处理方式完全不同。
Doris 真正替掉的是不必要的数据搬运,不是所有系统
| 业务职责 | 应该留下的系统 |
|---|---|
| 订单扣款、库存锁定、强事务写入 | MySQL、PostgreSQL 等 OLTP 数据库 |
| 消息缓冲、解耦和重放 | Kafka |
| CEP、复杂窗口、长时间有状态计算 | Flink |
| 新鲜明细上的聚合、Join、下钻和查询服务 | Doris |
如果业务只有每天一次的固定报表,离线数仓可能更便宜;如果只有不可变日志和极致扫描吞吐,ClickHouse 也可能更合适;如果所有指标已经稳定且读延迟要求极低,预聚合加缓存依然有效。
Doris 值得进入架构的信号只有一个:业务不断对刚发生的数据提出新问题,而现有链路每增加一个问题就必须复制数据、增加任务或创建新的服务层。
Doris 的优势不是把一条已知 SQL 跑得更炫,而是让下一条尚未出现的 SQL 不必先改造整条实时链路。
官方资料
- Apache Doris 4.0.8 Release Notes
- Apache Doris Overview
- Customer-Facing Analytics
- Query Profile