
Trino 从 4xx 版本起提供catalog.managementdynamic:catalog 不再写死在配置文件里,可以用 SQL DDLCREATE CATALOG/DROP CATALOG运行时增删。对数据平台这是刚需——用户注册一个新的湖 catalog,应该立刻能查,而不是等运维改 configmap、重启 Trino。但这个特性官方仍标注 experimental,并且有个极易被忽略的属性:默认的 catalog store 是 memory 的,不落盘。「我的数据空间」(datastudiohappy.cn)在生产里被它上了一课,这篇讲事故本身,和事后收敛出的「三路对账」自愈设计。一、事故:OOMKilled 之后,catalog 无声蒸发平台后端在启动时会把注册表里的 catalog 全量创建进 Trino,之后靠事件增量维护——注册就 CREATE,删除就 DROP。看起来闭环了,直到某天 Trino 容器因内存超限被 K8sOOMKilled自动重启:memory catalog store 清零,所有动态 catalog 消失;但后端没有重启,启动时的全量创建不会再来一次;事件侧也安静——没有任何 catalog 被注册或删除;于是缺口一开就是几个小时:所有经 Trino 的查询报Catalog xxx does not exist,直到下一次后端重启才恢复。根因不是哪行代码写错,是架构上少了一路:状态的权威在平台数据库,副本在 Trino 内存里,而「副本丢了」这件事,启动时机 事件驱动两路都感知不到。二、修法:三路触发,一个对账函数把「创建 catalog」的逻辑收敛成一个幂等的全量对账:拿权威(平台注册表)与现实(SHOW CATALOGS)做差集,缺的建、多的摘。三个触发时机共用它:触发路兜住什么catalog 注册/编辑/删除事件(经 MQ,事务性 outbox 投递)正常增量,秒级生效后端应用就绪时全量对账后端自己重启、以及消息漏投周期全量对账(默认 60s)Trino 自身重启丢 memory store——事故缺的就是这路对账的三条纪律,比触发时机更重要:幂等且不打扰:已在目标集合里的 catalog 不重建——重建会打断正在跑的查询。空转一轮的成本只是一次SHOW CATALOGS,60 秒一次可以忽略。单点失败不拖垮整轮:某个 catalog 创建失败(属性错、多实例 CREATE 撞车)只记 warn 跳过,其余照常对齐,下一轮自然重试。对账循环里一个 throw 中断全场,等于把一个 catalog 的问题放大成全部 catalog 的问题。日志安静:周期空转不刷 info。60 秒一条「对账完成,无变化」,一天 1440 条噪音,真出事时反而没人看日志了。实测:手动删掉 Trino pod,80 秒内 5 个动态 catalog 全部自动补回,期间无人工介入。三、顺带治本:OOMKilled 本身也要修自愈解决「重启后恢复」,但 ~7 小时一次的 OOMKilled 循环本身是个内存配置问题:-Xmx几乎贴着容器 memory limit,JVM 的堆外开销(metaspace、线程栈、direct buffer、JIT)没有余量,堆越满、native 越挤,最终被 cgroup 杀掉。把 Xmx 从 limit 的 2/3 档再往下压一档,给 native 留足空间,循环消失。JVM 容器化的老规矩:limit 减去 Xmx 的差值才是你真正给 native 的预算。对用户而言,这一切的意义是:新注册的 catalog 即刻可查,跨 catalog 联邦查询始终可用:四、三句话总结凡是「运行时注入到别的组件里」的状态,都要回答三个问题:权威在哪、副本丢了怎么发现、重建会不会打扰在跑的活——事件驱动只回答第一问;启动对账 事件增量只兜「自己重启」,对方重启只有周期对账兜得住,这一路最容易被省略;对账要幂等、单点失败降级、空转安静——以及别忘了把触发对账的那个 OOM 本身修掉。这套多引擎查询(Spark/Trino/Doris 自动选路)是「我的数据空间」的一部分——一套可私有化部署的数据平台,支持 OEM 合作。产品介绍:https://datastudiohappy.cn/。