这条链上的三个报错,我当时是当三个问题分别登记的。事后回看,它们只有一个起点:进程根本没活着。

01现象:SHOW BACKENDS 里 Alive: false

FE 是好的,能连、能建库,但后端列表里 Alive: false,附一句 ErrMsg: Connection refused。

这句 Connection refused 是关键证据:它不是“超时”,是端口上没人监听。FE 去探 BE 的 RPC 端口,连上了协议栈又立刻被拒——BE 进程不在。所以这一步不需要怀疑网络、防火墙或心跳参数,只需要去看 BE 自己的启动日志。

02BE 的启动自检对代理变量是“存在即拒绝”

启动日志里给的原因很直白:

text
env 'http_proxy' = 'http://<local-proxy-port>', need unset it

start_be.sh 在真正拉起进程之前会检查代理类环境变量,只要存在就拒绝启动。这个设计看着霸道,理由我认同:BE 对外的地址和端口是集群拓扑的一部分,一旦有 HTTP 代理插在中间,BE 上报给 FE 的地址、以及它自己发起的内部通信都可能被改写到一个错误的出口。宁可起不来,也不要起来一个“地址是错的”后端。

处置就是登录 shell 里显式清掉再启动:

bash
unset http_proxy https_proxy
<DORIS_BE_HOME>/bin/start_be.sh --daemon

后来我把这条固化成集群启动前置(和 swapoff -a 并列写进交付文档),因为它属于“每次重启机器都会再犯一次”的那类问题——代理是桌面环境给的,不是我为集群配的。

03下游第一个受害者:replication_num

BE 假死期间我去调副本数,报的是:

text
replication num should be less than the number of available backends

这句话把我误导了整整一轮——字面读起来像“我写的副本数太大,配置错了”。但它的判据是当前 Alive 的 BE 数,不是集群里安装了多少个 BE。BE 没活着时这个数是 0,于是任何 ALTER TABLE SET ("replication_num" = ...) 都会被拒,不管填几。

这类报错的通用形状是:校验语句把“运行时状态”写进了措辞里,而人把它读成了“参数写错了”。正确的读法是先问“这个上界此刻是多少”,再决定改不改参数。

我们这个环境还有一条约束:FE 和 BE 同机部署,可用后端就一个,所以所有建表语句里 replication_num 只能硬编成 1。这是环境约束不是设计选择,我在 DDL 里把它写成显式的,而不是靠默认值——默认值是 3,在单 BE 集群上会直接建表失败。

04我当时把它登记成了三个问题

翻自己写的问题汇总会发现一件挺好笑的事:BE 起不来记在“Doris 问题”下,replication_num 失败被记在“SQL/配置问题”下,还有一条“BE 内存不足”记在“资源/性能问题”下。同一条因果链被按报错出现的界面切成了三类,于是每类都单独找了一遍解法。

这条经验我写进了项目的排查规范:先看进程状态,再看资源状态,最后才怀疑参数。Connection refused、Alive: false、require object 这类词出现在哪一层,决定了后面所有推断值不值得信。

05另一件没成的事:物化视图换成 BITMAP 索引

想给高频过滤列做加速时,创建物化视图直接抛了 FE 侧的空指针:

text
java.lang.NullPointerException: require distribution object

这是那个 Doris 小版本上物化视图实现不完善的表现——FE 在建异步视图时拿不到目标表的分桶描述对象。我的处置不是绕过去,而是整条降级:放弃物化视图,改用 BITMAP 索引承担低基数列(用户标签、聚类 id、名次)的过滤加速。

这个降级里我犯了一个更隐蔽的错:索引脚本建在遗留 Python 链路的那套 ADS 表上,而最终交付的是一套新的服务表。两套表名相近、字段相近,脚本重跑时全都“成功”了,但优化其实落空了。现在我的判断标准是:任何“性能优化”动作,必须能指名它作用在哪张会被查询的表上——否则它只是让优化脚本变得更长。

顺带一条小的:CREATE INDEX 不幂等,没有 IF NOT EXISTS,重跑就报 index idx_user_tag already exist。而我这个优化脚本天生就是要反复重跑的形态,所以它比别处的 DDL 更需要幂等,这一点我当时没意识到——同一份 SQL 文件里,建表用的是 DROP TABLE IF EXISTS + 重建,建索引却是裸语句,两种幂等风格混着。

06还没解决的

  • 代理变量的处置是“记住要先 unset”,不是机制化的。 服务单元文件里写 Environment= 显式清空才算根治,我没做。
  • replication_num 的硬编没有随环境抽象出来。 单 BE 是我们当前的约束,将来加后端时所有 DDL 都要人工过一遍。
  • 降级成 BITMAP 之后没有做前后对比实验。 计划里的查询性能对比(响应时间分布、优化前后差值)一个都没跑,所以“用 BITMAP 替代物化视图”这件事目前只有设计理由,没有效果证据。

07相关文章