XXL-JOB 有哪些路由策略?分片广播是怎么回事?
简化版
当一个任务的执行器有多个实例时,调度中心需要一个路由策略决定「把这次调度发给哪个实例」。XXL-JOB 常见路由策略:第一个、最后一个、轮询、随机、一致性哈希、最不经常使用、最近最久未使用、故障转移(选一个健康的)、忙碌转移、分片广播。其中最特别、最重要的是分片广播(Sharding Broadcast)——它同时调度所有执行器实例,并给每个实例分配一个分片编号(shardIndex)和总分片数(shardTotal),每个实例根据自己的分片号只处理属于自己那一部分的数据,实现一个大任务拆分到多台机器并行处理。这是 XXL-JOB 处理大数据量任务的杀手锏。
详细版
常见路由策略:
| 策略 | 说明 |
|---|---|
| 第一个 / 最后一个 | 固定选列表第一个/最后一个实例 |
| 轮询 / 随机 | 依次轮流 / 随机选一个实例 |
| 一致性哈希 | 按任务哈希固定到某实例(粘性) |
| 最不经常使用 LFU / 最近最久未使用 LRU | 按使用频率/时间选 |
| 故障转移(Failover) | 逐个心跳检测,选第一个健康的实例 |
| 忙碌转移(Busyover) | 逐个检测,选第一个空闲的实例 |
| 分片广播(Sharding Broadcast) | 同时调度所有实例,每个处理一个分片 |
分片广播的核心:
@XxlJob("shardingJob")
public void shardingJob() {
int shardIndex = XxlJobHelper.getShardIndex(); // 当前实例的分片号 (0,1,2...)
int shardTotal = XxlJobHelper.getShardTotal(); // 总分片数 (=实例数)
// 每个实例只处理 id % shardTotal == shardIndex 的数据
List<Data> myData = queryByShard(shardIndex, shardTotal);
process(myData);
}
完整版教学
一、路由策略要解决什么
XXL-JOB 里,一个任务对应的执行器通常部署了多个实例(业务应用集群)。当任务到了触发时间,调度中心要发起调度——发给哪个实例执行? 这就是路由策略要决定的。不同策略适合不同需求:有的固定选一个、有的轮流、有的挑健康的、有的全都调度(分片广播)。选对路由策略,能实现负载均衡、故障转移、或并行分片处理。
二、普通路由策略(选一个实例执行)
大部分路由策略是从多个实例里选一个来执行任务:
- 第一个 / 最后一个:固定选执行器列表里的第一个 / 最后一个实例。简单固定。
- 轮询(Round Robin):依次轮流选实例,任务均匀分散到各实例。
- 随机(Random):随机选一个实例。
- 一致性哈希:按任务 ID 哈希,同一个任务固定路由到同一个实例(粘性),适合有状态/缓存亲和。
- LFU(最不经常使用)/ LRU(最近最久未使用):按实例被使用的频率/时间来选,做负载均衡。
- 故障转移(Failover):逐个对实例做心跳检测,选第一个心跳正常(健康)的实例执行。保证任务发给活着的实例,实现高可用。
- 忙碌转移(Busyover):逐个检测实例是否空闲,选第一个空闲的(当前没在跑任务的)实例执行,避免发给正忙的实例。
这些策略的共同点是:一次调度只有一个实例执行任务(其他实例不参与这次任务)。
三、分片广播:一个任务拆到多机并行(重点)
分片广播(Sharding Broadcast) 是最特殊、最重要的路由策略,它和上面的完全不同——它会同时调度该任务的所有执行器实例,让它们一起参与、各自处理一部分数据,实现并行分片处理。
核心机制:调度中心广播任务给所有实例时,给每个实例分配两个参数:
shardIndex(分片序号):当前实例是第几个分片(从 0 开始:0, 1, 2, …)。shardTotal(分片总数):总共有几个分片(= 执行器实例数)。
每个实例在 JobHandler 里拿到自己的 shardIndex 和 shardTotal,然后只处理「属于自己分片」的那部分数据。最常见的分片方式是按数据 ID 取模:
- 3 个实例,
shardTotal=3,各自shardIndex是 0、1、2。 - 实例 0 处理
id % 3 == 0的数据,实例 1 处理id % 3 == 1,实例 2 处理id % 3 == 2。 - 这样 1000 万条数据被均匀拆成 3 份,3 台机器并行处理,速度提升约 3 倍,且没有重复(每条数据只被一个分片处理)。
四、分片广播的价值:处理海量数据
分片广播解决了「单机处理不了的大数据量任务」——比如「给 1000 万用户发账单」「清理上亿条历史数据」。
- 单机跑:一台机器处理 1000 万条,又慢又可能撑不住。
- 分片广播:任务拆分到集群 N 台机器并行处理,每台只处理 1/N,速度提升 N 倍,充分利用集群算力。
而且分片是动态的——如果执行器实例数变了(扩容/缩容),shardTotal 会自动变化,分片自动重新分配,弹性伸缩。这是 XXL-JOB 相比 Quartz 集群(不支持单任务分片)的重要优势。
五、使用分片广播的注意点
- 数据分片要均匀:按 ID 取模是常用且均匀的方式;要保证数据能被
shardTotal均匀切分,避免某个分片数据特别多导致倾斜。 - 分片逻辑在业务代码里:XXL-JOB 只负责把
shardIndex/shardTotal传给每个实例,具体怎么根据分片号筛选数据、是你的 JobHandler 代码要实现的(如 SQL 里加where id % shardTotal = shardIndex)。 - 无状态、可并行:分片的各部分数据处理要相互独立、可并行,不能有跨分片的依赖。
- 失败处理:某个分片实例执行失败,可配合失败重试;要考虑重试时的幂等。
六、常见误区与追问
这道题面试时最容易丢分的地方,是把「XXL-JOB 路由与分片」答成一段泛泛的组件介绍。更稳的答法是先给结论,再沿着 分布式任务调度链路 拆清楚流程,最后补上异常场景、数字边界和选型取舍。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心定义 | 路由决定任务发给哪个执行器,分片广播让多个执行器各自处理不同数据范围 | 不要停在名词解释 |
| 流程机制 | 调度中心获取执行器列表 -> 按路由策略选择节点 -> 分片广播时通知所有节点 -> 每个节点拿到 shardIndex/shardTotal -> 按分片处理数据 -> 分别回报结果 | 说明谁触发、谁存储、谁通知、谁兜底 |
| 工程取舍 | 3 个执行器分片时 shardTotal=3,shardIndex 分别为 0、1、2,可按 userId % 3 分配数据 | 调度系统解决统一触发和治理,业务侧仍要保证幂等,因为网络抖动和重试不可避免 |
XXL-JOB 路由与分片 面试拆解:
1. 调度中心获取执行器列表
2. 按路由策略选择节点
3. 分片广播时通知所有节点
4. 每个节点拿到 shardIndex/shardTotal
5. 按分片处理数据
6. 分别回报结果
记忆钩子:先区分调度中心、执行器、触发、路由、分片、重试和告警,再说明如何避免重复执行;回答时一定要落到题目中的「XXL-JOB 路由与分片」,不要把相邻中间件的能力混着讲。
- 误区:路由和分片是一回事。 路由是选择执行器,分片是把一个大任务拆给多个执行器并行处理。
- 误区:分片广播会自动拆数据。 框架传 shardIndex 和 shardTotal,具体数据范围要业务代码自己计算。
- 误区:分片越多越快。 分片过多会增加调度、连接和下游压力,瓶颈可能转移到数据库。
- 追问:常见路由策略有哪些? 第一个、最后一个、轮询、随机、一致性哈希、最不经常使用、分片广播等。
- 追问:如何保证分片不重复不遗漏? 使用稳定的取模、范围切分或游标规则,并记录处理进度。
- 追问:某个分片失败怎么办? 只重试失败分片或补偿对应数据范围,业务侧要保证幂等。
七、加强记忆
XXL-JOB 路由策略决定「任务的多个执行器实例中,这次调度发给谁」。普通策略(选一个实例):第一个/最后一个、轮询、随机、一致性哈希(粘性)、LFU/LRU、故障转移(选第一个健康实例)、忙碌转移(选第一个空闲实例)。最重要的是分片广播(Sharding Broadcast)——同时调度所有实例,给每个实例分配 shardIndex(分片号 0,1,2…)+ shardTotal(总分片数=实例数),每个实例只处理属于自己分片的数据(常按 id % shardTotal == shardIndex 取模),实现一个大任务拆到多机并行处理(1000 万数据拆 N 份、N 台并行、提速 N 倍、无重复)。价值:处理单机搞不定的海量数据 + 弹性伸缩(实例变则分片自动重分)。注意:分片均匀、分片筛选逻辑在业务代码里写、各分片独立可并行。口诀:普通策略选一个执行、分片广播全体并行各处理一片(shardIndex/shardTotal 取模分数据)。