调度器多副本,不引 ZooKeeper:DB CAS + slot 分片就够了

发布时间:2026/10/6 9:41:15

调度器多副本,不引 ZooKeeper:DB CAS + slot 分片就够了 调度器一挂,全平台定时作业停摆,所以单副本变多副本是绕不过去的一步。而说到「调度器 HA」,多数人的第一反应是条件反射式的:上 ZooKeeper(或 etcd)选个主,leader 干活,follower 待命。「我的数据空间」(datastudiohappy.cn)的作业调度器(cron 触发 DAG 工作流编排)走了另一条路:一行 ZK 代码都没有,整个多副本方案只用两样东西——DB 乐观锁 CAS 保正确性,slot 分片提吞吐。这篇讲清楚它是怎么闭环的。一、多副本到底要保证什么先把「HA」拆成四条可验证的性质:同一次触发,全局只发生一次——双跑轻则浪费资源,重则数据写重(「删旧分区再写入」的 ETL 并发跑两遍);任一副本挂掉,任务不丢不僵——在跑的被别人接管收尾,宕机漏掉的触发按策略补;扩缩容自动再均衡——加副本活儿自动摊过去,减副本活儿自动被捡走,不靠人肉改分片;成员变化的过渡期,以上三条不破——「现在有几个副本」的视图短暂不一致时,系统不出错。第 1 条和第 2 条天然有张力:要「不丢」就得允许接管,要「不双跑」就得防接管和原执行者撞车。怎么解这个张力,是所有方案的分水岭。二、四条路线,一张表说完路线典型代表主要代价ZK/etcd 选主大量自研调度器新增重量级组件且它自己也要 HA;follower 纯热备不出力;脑裂仍要 fencing 补分布式锁Redis/ZK 锁租期两难:短了一次 GC 停顿就双跑,长了故障后干等;多一个故障域DB 悲观行锁Quartz 集群模式所有节点排队过同一把FOR UPDATE,触发频率一高锁竞争即瓶颈DB 乐观锁 CASAirflow 多 scheduler无等待、无租期难题、依赖零新增;需要自己把每个动作设计成条件更新先例值得注意:Airflow 的多 scheduler 完全靠元数据库协调,官方明确不引 Raft/Paxos;DolphinScheduler 的多 master 靠命令表去重 slot 取模分片,注册中心甚至提供纯 JDBC 实现。真正在大规模生产里跑的调度器,协调层早有「往 DB 收敛」的传统,ZK 从来不是标配。选 CAS 的理由说穿了很朴素:调度器的一切状态——作业定义、实例、任务——本来就在 MySQL 里,「谁在跑」的权威在 DB,「谁来跑」的裁决权放 DB 就是最短路径。依赖清单每多一项,可用性是乘法不是加法。并且选主保证的只是「谁干活」,CAS 直接保证「每件事只被干一次」——后者才是真正要的性质,粒度更细,还顺便让 N 个副本能同时干 N 件不同的事。三、四个核心机制触发去重:对「下次触发时间」做 CAS。cron 不放内存定时器(多副本天然 N 份、重启即丢),而是一张触发表 近端扫描:每个副本每几秒扫「即将到期」的行(走索引,代价与作业总量脱钩),抢占是一条带条件的更新:UPDATE触发表SET下次触发时间:按cron算的下一次WHEREid:idAND下次触发时间:本次看到的值;影响行数为 1 即抢到本次触发,为 0 说明别的副本已把时间推进,直接跳过——不加锁、不等待,MySQL 的行级原子性就是裁判。宕机漏掉的触发顺便解决:扫描发现触发时刻已超过宽限期,按 misfire 策略补跑(默认只补最近一次),基线就是表里持久化的触发时间本身。执行去重:状态机 CAS outbox。触发之后任务经消息队列派发,而 MQ 是 at-least-once 的,消息可能重复——去重不指望 MQ,在消费端用同一招:UPDATE任务表SET状态执行中WHEREid:idAND状态已派发;消息重复来十次,十个消费者做同一条 CAS,只有一个翻转成功,其余静默丢弃。「认领」与「发消息」放在同一个 DB 事务里(transactional outbox 模式),杜绝「已认领但消息漏发」的孤儿任务。两层 CAS 各管一段:触发一次靠前者,投递至少一次但执行恰好一次靠后者。僵尸接管:心跳租约 reaper。每个在跑任务记「属主进程 租约到期时间」,属主活着就周期续租(25 秒一续、租约 90 秒,连续错过三次心跳才判死,长作业不会因「跑得久」被误杀);进程死了续租自然停止,任一存活副本上的常驻 reaper 扫「非终态 租约过期」的任务做收尾。两条纪律直接融在这条路径里:收尾的副作用(判失败、杀残留执行体、释放额度、告警)一律挂在 CAS 认领之后,多个副本同时扫到同一具僵尸,只有 CAS 赢家真正执行,否则告警发两遍就是新的双跑;kill 以外部权威收敛——执行体都在 K8s 里,按标签删 pod,任何副本都能杀,不依赖某个副本内存里的执行句柄。slot 分片:从「不出错」到「不白干」。到此正确性已闭环,但 N 个副本扫同一批行、N-1 个在 CAS 上扑空,吞吐不随副本数涨。解法是 DolphinScheduler 式取模分片:成员发现就一张 MySQL 心跳表,每副本周期性 upsert 自己一行,活成员按节点 ID 排序定出自己的 slot,扫描时只捞「id 取模等于自己 slot」的那片。稳态下零重复扫描、零 CAS 争抢,吞吐近似线性;扩缩容是「无状态重算」,十秒级自动再均衡,没有任何迁移动作。成员视图短暂不一致怎么办?不怎么办,CAS 兜底:分片重叠,最坏多一次扑空;分片有缝,视图偏小的副本覆盖面更宽,漏不掉。这是整套架构最关键的性质——分片只是吞吐优化,永远不是正确性边界。心跳表哪怕整个被清空、滞后、彻底不一致,系统也只退化成「全扫 CAS」:慢一点,绝不出错。唯一的悲观锁留给了资源配额准入(共享计数器的「读-算-写」,CAS 不适合),配套一条容易踩空的纪律:锁定读必须是事务里的第一条读,否则 REPEATABLE READ 的快照会让锁内读到过期用量,并发下配额直接超卖。多副本之下,DAG 工作流照常编排、运维侧照常可观测,这些能力在「我的数据空间」(产品介绍)里长这样:四、适用边界需要毫秒级成员感知(在线服务路由级的故障切换)、需要真 fencing token(被抢占者持有的资源无法由 K8s 这类外部权威收敛)、协调事件高频到关系库扛不住,或跨区域部署——这四种场景该老实上 ZK/etcd。反之,已有一个大家都写的关系库、协调决策是秒级低频、执行体可由外部权威收敛,就不需要为 HA 引入任何新组件。五、三句话总结选主解决「谁干活」,CAS 直接解决「每件事只干一次」——后者才是目标性质;正确性与吞吐拆成两层:CAS 保正确,分片只管加速,任何一层糙一点系统都不出错;依赖清单每多一项,可用性是乘法——正确性只押在系统里本来就必须活着的那个组件上。这套调度器是「我的数据空间」的一部分——一套可私有化部署的数据平台,支持 OEM 合作。产品介绍:https://datastudiohappy.cn/。
延伸阅读

更多相关文章

2026/10/4 17:44:44

新能源汽车补贴新政深度解读:技术门槛提升与产业链应对策略

1. 新规落地:从“普惠”到“择优”的必然转向最近,新能源汽车圈子里讨论最热的话题,莫过于新一轮补贴政策调整的靴子终于落地。简单来说,核心就八个字:“提标准,降补贴”。这可不是简单的“钱变少了”&…

2026/10/5 9:58:07

keil默认的文本编辑器字体太丑

问题: keil默认的文本编辑器字体太丑。 解决办法: 1)从网上下载“MicrosoftYaHeiMono”或者“YaHei.Consolas.1.11b”的字体安装包; 2)选择字体安装包文件,右键选择“为所有用户安装”; 3) 重启KEIL&#x…

2026/10/6 9:38:49

OpenShell:GPU加速的现代终端模拟器,轻量高效替代iTerm2

上个月我把主力终端从系统自带的 Terminal 换成了 OpenShell,一句话来概括这段体验——这是我今年换过的所有开发者工具里,性价比最高的一次迁移。先交代一下背景:我日常工作几乎泡在命令行里,跑测试、改配置、连远程服务器、盯日…

2026/10/6 9:38:49

Python虚拟环境实战:从venv到conda迁移与避坑指南

在Linux上折腾Python的人,迟早会在一个深夜被依赖冲突逼疯:新项目要Python 3.11,老服务还锁在3.8,系统自带的包管理工具又认死理,你一升级,cron里跑了几年的脚本第二天全挂。我就是在一次手贱升级requests之…

2026/10/6 9:38:49

RabbitMQ高并发实战:从异步解耦到削峰填谷的完整指南

接手过一个电商中台项目,第一次让我失眠的就是大促期间下单接口的耗时。用户点一下支付,后端要依次同步调用库存、优惠券、积分、短信四个服务,接口动不动飙到800毫秒,数据库连接池直接被打满,再往下就是雪崩。架构师甩…

2026/10/6 9:38:49

程序员深夜思考:从代码世界到人生世界的五个映射框架

凌晨一点三十七分,我在IDE前面坐了二十分钟,一行代码没写。光标在闪烁,脑海里想的却是"代码职业生涯的版本号到底是谁定的"这种不着边际的问题。白天完全不会想这些——白天有需求deadline压着,有测试用例等着&#xff…

2026/10/6 9:38:49

Context-Mode实战:把项目上下文喂给AI,告别答非所问

第一次意识到 context-mode 这个问题的价值,是在某个周三下午改一个用了三年的老项目。AI 辅助工具已经装好了,提示词写得很清楚,“帮我看看这个函数为什么偶发异常”,结果模型答非所问,给了我一篇关于异常处理的最佳实…

2026/10/6 9:33:48

OpenShell实战:AI智能体如何重塑命令行运维与自动化工作流

OpenShell这名字乍一听像某个终端模拟器,或者某个操作系统的新玩具,但如果你关注AI工具圈,可能会知道它其实是一套面向命令行场景的AI智能体框架——准确说,是在OpenAI Codex CLI停更之后,由原团队核心成员开源出来的那…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/5 17:38:27

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/6 0:03:23

MR25H40CDF+STM32F031C6工业级高可靠数据存储方案

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的 PLC 控制柜里、在风电变流器的散热片背面、在矿井监测终端的金属外壳下,你经常能看到一块指甲盖大小的黑色芯片——它既不是 Flash,也不是…

2026/10/6 0:03:23

MRAM+STM32工业断电数据保全实战指南

1. 项目概述:为什么在工业现场非得用 MR25H40CDF 配 STM32F031C6 做数据存储?在工厂产线的PLC柜里、在野外无人值守的环境监测终端里、在高速运转的包装机控制板上,你经常能看到一块指甲盖大小的黑色芯片,旁边贴着“MR25H40CDF”丝…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑