
分片能力天花板!Elastic-Job搞定1000万级数据批处理
引言
当业务数据量从百万级跃升到千万级甚至亿级时,传统的单机定时任务和基于中心化调度的框架(如XXL-JOB)开始显得力不从心。面对海量数据批处理场景,如用户画像计算、日志清洗、积分兑换等,我们需要一个能够将任务弹性分片、去中心化执行的调度框架。Elastic-Job正是为解决这类问题而生。
本文将深入解析Elastic-Job的核心优势、架构原理,并通过完整的实操教程和实战案例,带您掌握如何利用Elastic-Job高效处理千万级数据。最后,我们将对比XXL-JOB,帮助您在不同场景下做出正确选型。
1. Elastic-Job核心优势:去中心化架构+弹性分片
Elastic-Job是由当当网开源的一款分布式调度解决方案,它有两个主要特性:去中心化和弹性分片。
1.1 去中心化架构
与XXL-JOB这类中心化调度框架不同,Elastic-Job没有独立的调度中心节点。每个执行器节点都是对等的,通过注册中心(ZooKeeper或ETCD)协调任务分片和状态同步。这种架构的好处是:
- 无单点故障:任何一个节点宕机,其他节点自动接管其分片。
- 水平扩展性极佳:增加节点只需启动新实例,注册中心自动感知并重新分片。
1.2 弹性分片
Elastic-Job的核心能力是分片。它将一个任务逻辑上划分为多个“分片项”(sharding items),每个分片项代表一部分数据或工作。执行器集群中的每个节点领取一定数量的分片项,并行执行。当节点数量变化时,Elastic-Job自动重新分配分片项,实现弹性伸缩。
分片 vs 并行执行:普通并行执行只是将任务简单复制到多个节点,每个节点执行相同逻辑,可能导致重复处理。而分片是将数据/任务切分成互斥的片段,每个节点只处理属于自己的分片,保证数据不重复不遗漏。
2. 架构原理:ZooKeeper/ETCD在分片协调中的作用
Elastic-Job依赖于ZooKeeper(或ETCD)实现分布式协调。下面我们以ZooKeeper为例,说明其核心数据结构和工作流程。
2.1 ZooKeeper数据节点结构
Elastic-Job在ZooKeeper中为每个任务创建一个命名空间,结构如下:
/your-job-namespace
├── job1
│ ├── instances
│ │ ├── instance-ip1:port
│ │ ├── instance-ip2:port
│ ├── sharding
│ │ ├── 0
│ │ │ ├── instance
│ │ │ └── status
│ │ ├── 1
│ │ │ ├── instance
│ │ │ └── status
│ │ └── …
│ ├── leader
│ │ ├── election
│ │ └── failover
│ └── config
└── job2
└── …
- instances:记录当前所有运行中的执行器实例节点(临时节点)。
- sharding:记录每个分片项(0,1,2…)被哪个实例持有,以及运行状态。
- leader:用于主节点选举,处理任务分片、故障转移等控制逻辑。
- config:存储任务的配置信息,如分片总数、cron表达式等。
2.2 工作流程
Mermaid流程图:
Leader(Executor1)ZooKeeperExecutor2Executor1Leader(Executor1)ZooKeeperExecutor2Executor1#mermaid-svg-Za5CS685IUOZekt8{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Za5CS685IUOZekt8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Za5CS685IUOZekt8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Za5CS685IUOZekt8 .error-icon{fill:#552222;}#mermaid-svg-Za5CS685IUOZekt8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Za5CS685IUOZekt8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Za5CS685IUOZekt8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Za5CS685IUOZekt8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Za5CS685IUOZekt8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Za5CS685IUOZekt8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Za5CS685IUOZekt8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Za5CS685IUOZekt8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Za5CS685IUOZekt8 .marker.cross{stroke:#333333;}#mermaid-svg-Za5CS685IUOZekt8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Za5CS685IUOZekt8 p{margin:0;}#mermaid-svg-Za5CS685IUOZekt8 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Za5CS685IUOZekt8 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Za5CS685IUOZekt8 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Za5CS685IUOZekt8 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Za5CS685IUOZekt8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Za5CS685IUOZekt8 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Za5CS685IUOZekt8 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Za5CS685IUOZekt8 .sequenceNumber{fill:white;}#mermaid-svg-Za5CS685IUOZekt8 #sequencenumber{fill:#333;}#mermaid-svg-Za5CS685IUOZekt8 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Za5CS685IUOZekt8 .messageText{fill:#333;stroke:none;}#mermaid-svg-Za5CS685IUOZekt8 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Za5CS685IUOZekt8 .labelText,#mermaid-svg-Za5CS685IUOZekt8 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Za5CS685IUOZekt8 .loopText,#mermaid-svg-Za5CS685IUOZekt8 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Za5CS685IUOZekt8 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Za5CS685IUOZekt8 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Za5CS685IUOZekt8 .noteText,#mermaid-svg-Za5CS685IUOZekt8 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Za5CS685IUOZekt8 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Za5CS685IUOZekt8 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Za5CS685IUOZekt8 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Za5CS685IUOZekt8 .actorPopupMenu{position:absolute;}#mermaid-svg-Za5CS685IUOZekt8 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Za5CS685IUOZekt8 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Za5CS685IUOZekt8 .actor-man circle,#mermaid-svg-Za5CS685IUOZekt8 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Za5CS685IUOZekt8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}当Executor2宕机创建临时节点 /instances/executor1创建临时节点 /instances/executor2通知instances变化通知instances变化尝试创建/leader/election节点,成为leader读取所有instances,计算分片分配写入分片分配信息到/sharding/*监听到分片分配,获取自己的分片项监听到分片分配,获取自己的分片项执行分片任务执行分片任务临时节点/instances/executor2消失重新分片,更新/sharding/*监听到分片变化,获取新的分片项执行新增的分片项
3. 实操教程:从零搭建Elastic-Job环境
3.1 环境准备
- JDK 1.8+
- Maven 3.5+
- ZooKeeper 3.4.6+(集群或单机)
- IDE(如IntelliJ IDEA)
3.2 部署ZooKeeper集群(可选单机)
单机模式(测试用):
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz
tar -zxvf zookeeper-3.4.14.tar.gz
cd zookeeper-3.4.14
cp conf/zoo_sample.cfg conf/zoo.cfg
# 修改dataDir为自定义路径
bin/zkServer.sh start
集群模式(生产推荐):
至少三台机器,每台修改zoo.cfg:
server.1=192.168.1.10:2888:3888
server.2=192.168.1.11:2888:3888
server.3=192.168.1.12:2888:3888
并在dataDir目录下创建myid文件,内容为对应序号。
3.3 创建Spring Boot项目整合Elastic-Job
我们将使用Elastic-Job-Lite,它是轻量级无中心化版本。
Maven依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>elasticjob-lite-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
</dependencies>
application.yml配置:
elasticjob:
reg-center:
server-lists: localhost:2181 # ZooKeeper地址
namespace: elasticjob–example # ZooKeeper命名空间
jobs:
dataCleanJob:
elasticJobClass: com.example.job.DataCleanJob
cron: 0 0 2 * * ? # 每天凌晨2点执行
sharding-total-count: 5 # 分片总数
sharding-item-parameters: 0=beijing,1=shanghai,2=guangzhou,3=shenzhen,4=others # 分片参数
overwrite: true
3.4 编写第一个分片任务
创建任务类,实现SimpleJob接口:
package com.example.job;
import org.apache.shardingsphere.elasticjob.api.ShardingContext;
import org.apache.shardingsphere.elasticjob.simple.job.SimpleJob;
import org.springframework.stereotype.Component;
@Component
public class DataCleanJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
int shardingItem = shardingContext.getShardingItem();
String shardingParameter = shardingContext.getShardingParameter();
System.out.println("分片项:" + shardingItem + ",参数:" + shardingParameter + " 开始处理数据…");
// 模拟根据分片参数处理不同地域的数据
switch (shardingParameter) {
case "beijing":
// 处理北京的数据
cleanDataByRegion("beijing");
break;
case "shanghai":
cleanDataByRegion("shanghai");
break;
// … 其他地域
default:
cleanDataByRegion("others");
}
}
private void cleanDataByRegion(String region) {
// 实际业务:从数据库查询该地域的数据并清洗
System.out.println("正在清洗 " + region + " 的数据…");
// 模拟耗时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.5 分片策略配置
Elastic-Job内置了三种分片策略:
- 平均分配策略(默认):AverageAllocationJobShardingStrategy,将分片项尽可能平均分配给所有实例。
- 轮询分配策略:RoundRobinAllocationJobShardingStrategy,按顺序轮流分配。
- 根据作业名称哈希分配:OdevitySortByNameJobShardingStrategy,根据作业名称的哈希值奇偶决定。
我们可以在配置中指定策略:
elasticjob:
jobs:
dataCleanJob:
jobShardingStrategyType: AVG_ALLOCATION # 或 ROUND_ROBIN
3.6 启动验证
分别启动两个或多个应用实例(通过不同端口或命令行参数),观察控制台输出。每个实例会打印自己负责的分片项及参数,并且当停止一个实例后,剩余实例会自动接管其分片。
弹性伸缩验证:
通过日志可清晰看到分片变化。
4. 优缺点分析
4.1 优点
- 分片能力极强:支持灵活的分片策略,适合海量数据并行处理。
- 弹性伸缩:执行器节点动态增减,任务自动重新分片,无需人工干预。
- 去中心化:无调度中心,避免单点故障,系统更健壮。
- 与Spring生态集成良好:提供Spring Boot Starter,配置简单。
- 支持分布式事务(通过补偿):虽然不直接提供事务,但可通过幂等和重试保证最终一致性。
4.2 缺点
- 需要维护ZooKeeper:引入额外的中间件,增加运维复杂度。
- 学习曲线稍陡:对分片概念、ZooKeeper原理需要一定理解。
- 不支持复杂DAG工作流:Elastic-Job主要面向单任务分片,如果需要复杂依赖编排,需结合其他框架(如Apache DolphinScheduler)或自行实现。
- 无内置监控界面:虽然可通过ZooKeeper查看状态,但缺乏友好的可视化控制台(社区有第三方扩展,但非官方)。
5. 实战案例:1000万条数据清洗分片执行
假设我们有一个用户行为日志表,记录了近千万条数据,需要每天凌晨进行清洗(过滤无效数据、格式转换、脱敏等),并存入数仓。使用Elastic-Job分片处理,可以大幅提升效率。
5.1 数据分片策略
按用户ID取模分片是最常见的方式。例如,分片总数为10,每个分片处理用户ID % 10 == 固定值的数据。这样能保证同一用户的所有数据进入同一分片,便于后续聚合计算。
配置分片参数:
elasticjob:
jobs:
logCleanJob:
sharding-total-count: 10
# 分片参数可传递每个分片对应的用户ID模值,但通常业务中直接使用分片项序号取模
5.2 任务实现
@Component
public class LogCleanJob implements SimpleJob {
@Autowired
private LogDao logDao;
@Override
public void execute(ShardingContext context) {
int shard = context.getShardingItem();
int totalShards = context.getShardingTotalCount();
// 计算当前分片需要处理的用户ID范围(按ID取模)
// 假设用户ID为数字,清洗最近一周的数据
Date startTime = DateUtils.addDays(new Date(), –7);
Date endTime = new Date();
List<Long> userIds = logDao.findUserIdsByMod(shard, totalShards, startTime, endTime);
// 分批处理,防止一次加载过多
for (Long userId : userIds) {
List<LogEntity> logs = logDao.findLogsByUserId(userId, startTime, endTime);
for (LogEntity log : logs) {
// 清洗逻辑:过滤、脱敏、转换
cleanLog(log);
// 写入清洗后的表或发送到MQ
logDao.insertCleanedLog(log);
}
// 每处理完一个用户,更新进度(可选)
}
}
private void cleanLog(LogEntity log) {
// 模拟清洗:脱敏IP、过滤敏感词等
log.setIp(maskIp(log.getIp()));
// … 其他处理
}
}
5.3 性能对比
假设单机处理1000万条数据需要10小时,采用10个分片(10台机器并行)理论上可将时间缩短到1小时左右。实际中受数据库瓶颈、网络等影响,但提升依然显著。
6. 对比XXL-JOB:什么时候选Elastic-Job而非XXL-JOB?
XXL-JOB是目前国内使用最广泛的分布式任务调度框架,它采用中心化架构(调度中心+执行器)。两者对比如下:
| 架构 | 去中心化,依赖ZooKeeper协调 | 中心化,调度中心负责触发和分配 |
| 分片能力 | 原生支持弹性分片,动态分配 | 支持分片(广播模式),但分片逻辑需自行实现 |
| 可扩展性 | 节点增减自动重新分片 | 执行器增减需调度中心感知,但任务分片需手动配置 |
| 运维复杂度 | 需要维护ZooKeeper集群 | 只需维护调度中心数据库(MySQL),更简单 |
| 监控界面 | 无官方UI,需通过ZooKeeper查看 | 自带功能丰富的Web控制台 |
| 任务依赖 | 不支持DAG工作流 | 支持简单依赖(通过子任务) |
| 多语言支持 | Java为主 | Java为主,但有HTTP任务可支持其他语言 |
| 适用场景 | 海量数据批处理,需要弹性分片 | 常规定时任务,中小团队,简单分片需求 |
何时选择Elastic-Job?
- 数据量巨大,需要将数据切分到多台机器并行处理,且节点数量会动态变化。
- 追求高可用,希望避免调度中心单点(虽然XXL-JOB调度中心可集群,但依赖数据库锁)。
- 团队有ZooKeeper运维能力,愿意承担额外中间件成本。
何时选择XXL-JOB?
- 任务数量不多,对分片要求不高。
- 需要可视化管理和快速部署。
- 团队规模小,希望降低运维复杂度。
7. 适用场景:海量数据批处理
Elastic-Job特别适合以下场景:
- 用户画像计算:每天根据用户行为日志计算标签,数据量千万级,需要按用户ID分片并行计算。
- 数据清洗/ETL:日志清洗、数据标准化,可按日期或地域分片。
- 批处理作业:如积分兑换、优惠券发放,需扫描全量用户,分片处理避免数据库压力。
- 定时报表生成:按区域、渠道分片生成报表,再合并结果。
- 索引/缓存重建:大规模数据重建Elasticsearch索引或Redis缓存,可分片并行。
总结
Elastic-Job凭借其去中心化架构和强大的弹性分片能力,成为海量数据批处理场景的首选方案。虽然需要维护ZooKeeper增加了一定复杂度,但换来的高可用性和伸缩性在数据量爆炸的今天极具价值。
本文从原理到实战,带您完整体验了Elastic-Job的魅力。下篇我们将探讨PowerJob——新一代支持工作流和多语言的调度框架,敬请期待!
您的业务是否遇到过千万级数据处理的挑战?欢迎留言分享!
附录:完整Mermaid图汇总
1. Elastic-Job架构图
#mermaid-svg-DTXqSi3SVObr1f8J{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DTXqSi3SVObr1f8J .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DTXqSi3SVObr1f8J .error-icon{fill:#552222;}#mermaid-svg-DTXqSi3SVObr1f8J .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DTXqSi3SVObr1f8J .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DTXqSi3SVObr1f8J .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DTXqSi3SVObr1f8J .marker.cross{stroke:#333333;}#mermaid-svg-DTXqSi3SVObr1f8J svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DTXqSi3SVObr1f8J p{margin:0;}#mermaid-svg-DTXqSi3SVObr1f8J .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-DTXqSi3SVObr1f8J .cluster-label text{fill:#333;}#mermaid-svg-DTXqSi3SVObr1f8J .cluster-label span{color:#333;}#mermaid-svg-DTXqSi3SVObr1f8J .cluster-label span p{background-color:transparent;}#mermaid-svg-DTXqSi3SVObr1f8J .label text,#mermaid-svg-DTXqSi3SVObr1f8J span{fill:#333;color:#333;}#mermaid-svg-DTXqSi3SVObr1f8J .node rect,#mermaid-svg-DTXqSi3SVObr1f8J .node circle,#mermaid-svg-DTXqSi3SVObr1f8J .node ellipse,#mermaid-svg-DTXqSi3SVObr1f8J .node polygon,#mermaid-svg-DTXqSi3SVObr1f8J .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DTXqSi3SVObr1f8J .rough-node .label text,#mermaid-svg-DTXqSi3SVObr1f8J .node .label text,#mermaid-svg-DTXqSi3SVObr1f8J .image-shape .label,#mermaid-svg-DTXqSi3SVObr1f8J .icon-shape .label{text-anchor:middle;}#mermaid-svg-DTXqSi3SVObr1f8J .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DTXqSi3SVObr1f8J .rough-node .label,#mermaid-svg-DTXqSi3SVObr1f8J .node .label,#mermaid-svg-DTXqSi3SVObr1f8J .image-shape .label,#mermaid-svg-DTXqSi3SVObr1f8J .icon-shape .label{text-align:center;}#mermaid-svg-DTXqSi3SVObr1f8J .node.clickable{cursor:pointer;}#mermaid-svg-DTXqSi3SVObr1f8J .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DTXqSi3SVObr1f8J .arrowheadPath{fill:#333333;}#mermaid-svg-DTXqSi3SVObr1f8J .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DTXqSi3SVObr1f8J .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DTXqSi3SVObr1f8J .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DTXqSi3SVObr1f8J .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DTXqSi3SVObr1f8J .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DTXqSi3SVObr1f8J .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DTXqSi3SVObr1f8J .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DTXqSi3SVObr1f8J .cluster text{fill:#333;}#mermaid-svg-DTXqSi3SVObr1f8J .cluster span{color:#333;}#mermaid-svg-DTXqSi3SVObr1f8J div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DTXqSi3SVObr1f8J .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DTXqSi3SVObr1f8J rect.text{fill:none;stroke-width:0;}#mermaid-svg-DTXqSi3SVObr1f8J .icon-shape,#mermaid-svg-DTXqSi3SVObr1f8J .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DTXqSi3SVObr1f8J .icon-shape p,#mermaid-svg-DTXqSi3SVObr1f8J .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DTXqSi3SVObr1f8J .icon-shape rect,#mermaid-svg-DTXqSi3SVObr1f8J .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DTXqSi3SVObr1f8J .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DTXqSi3SVObr1f8J .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DTXqSi3SVObr1f8J :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
执行器集群
ZooKeeper集群
ZK节点1
ZK节点2
ZK节点3
执行器1
执行器2
执行器3
数据库
2. 分片重新分配流程图
#mermaid-svg-RW0tekOusDyvtCtz{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RW0tekOusDyvtCtz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RW0tekOusDyvtCtz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RW0tekOusDyvtCtz .error-icon{fill:#552222;}#mermaid-svg-RW0tekOusDyvtCtz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RW0tekOusDyvtCtz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RW0tekOusDyvtCtz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RW0tekOusDyvtCtz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RW0tekOusDyvtCtz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RW0tekOusDyvtCtz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RW0tekOusDyvtCtz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RW0tekOusDyvtCtz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RW0tekOusDyvtCtz .marker.cross{stroke:#333333;}#mermaid-svg-RW0tekOusDyvtCtz svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RW0tekOusDyvtCtz p{margin:0;}#mermaid-svg-RW0tekOusDyvtCtz .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-RW0tekOusDyvtCtz .cluster-label text{fill:#333;}#mermaid-svg-RW0tekOusDyvtCtz .cluster-label span{color:#333;}#mermaid-svg-RW0tekOusDyvtCtz .cluster-label span p{background-color:transparent;}#mermaid-svg-RW0tekOusDyvtCtz .label text,#mermaid-svg-RW0tekOusDyvtCtz span{fill:#333;color:#333;}#mermaid-svg-RW0tekOusDyvtCtz .node rect,#mermaid-svg-RW0tekOusDyvtCtz .node circle,#mermaid-svg-RW0tekOusDyvtCtz .node ellipse,#mermaid-svg-RW0tekOusDyvtCtz .node polygon,#mermaid-svg-RW0tekOusDyvtCtz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RW0tekOusDyvtCtz .rough-node .label text,#mermaid-svg-RW0tekOusDyvtCtz .node .label text,#mermaid-svg-RW0tekOusDyvtCtz .image-shape .label,#mermaid-svg-RW0tekOusDyvtCtz .icon-shape .label{text-anchor:middle;}#mermaid-svg-RW0tekOusDyvtCtz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RW0tekOusDyvtCtz .rough-node .label,#mermaid-svg-RW0tekOusDyvtCtz .node .label,#mermaid-svg-RW0tekOusDyvtCtz .image-shape .label,#mermaid-svg-RW0tekOusDyvtCtz .icon-shape .label{text-align:center;}#mermaid-svg-RW0tekOusDyvtCtz .node.clickable{cursor:pointer;}#mermaid-svg-RW0tekOusDyvtCtz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RW0tekOusDyvtCtz .arrowheadPath{fill:#333333;}#mermaid-svg-RW0tekOusDyvtCtz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RW0tekOusDyvtCtz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RW0tekOusDyvtCtz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RW0tekOusDyvtCtz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RW0tekOusDyvtCtz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RW0tekOusDyvtCtz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RW0tekOusDyvtCtz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RW0tekOusDyvtCtz .cluster text{fill:#333;}#mermaid-svg-RW0tekOusDyvtCtz .cluster span{color:#333;}#mermaid-svg-RW0tekOusDyvtCtz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RW0tekOusDyvtCtz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RW0tekOusDyvtCtz rect.text{fill:none;stroke-width:0;}#mermaid-svg-RW0tekOusDyvtCtz .icon-shape,#mermaid-svg-RW0tekOusDyvtCtz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RW0tekOusDyvtCtz .icon-shape p,#mermaid-svg-RW0tekOusDyvtCtz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RW0tekOusDyvtCtz .icon-shape rect,#mermaid-svg-RW0tekOusDyvtCtz .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RW0tekOusDyvtCtz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RW0tekOusDyvtCtz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RW0tekOusDyvtCtz :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
实例启动/停止
ZooKeeper触发
Leader选举
重新计算分片分配
写入ZooKeeper
各实例监听到变化
执行新分片任务

