3. 调度平台数据模型分析:核心表结构与索引设计要点
调度平台作为 CI/CD 集成中的核心调度引擎,其数据模型直接决定了任务分发效率、实例状态追踪能力以及系统整体的吞吐上限。本章将深入分析调度平台的四大核心表——任务表、实例表、日志表、依赖表——并给出经过生产验证的索引设计策略。
3.1 任务表(Task Table)结构设计
任务表是调度平台的元数据核心,每一条记录对应一个可被调度的作业定义。其字段设计需兼顾静态属性与动态调度策略。
| 字段名 | 类型 | 说明 | 索引策略 |
|---|---|---|---|
| task_id | BIGINT | 主键,自增 | 聚簇索引 |
| task_name | VARCHAR(128) | 任务名称,业务唯一 | 唯一索引 |
| task_type | TINYINT | 任务类型:1-Shell, 2-SQL, 3-HTTP | 非唯一索引 |
| cron_expression | VARCHAR(64) | 调度周期表达式 | — |
| timeout_sec | INT | 超时阈值,单位秒 | — |
| retry_count | TINYINT | 失败重试次数 | — |
| status | TINYINT | 0-禁用, 1-启用, 2-暂停 | 复合索引(status, task_type) |
| create_time | DATETIME | 创建时间 | 索引(用于分页排序) |
| update_time | DATETIME | 最后修改时间 | — |
索引设计要点:
- task_name 唯一索引:防止同名任务重复注册,同时支持按名称精确查询。
- 复合索引 (status, task_type):调度器轮询时高频查询“启用状态 + 特定类型”的任务列表,该索引可避免全表扫描。
- create_time 索引:任务管理后台的分页列表查询,避免文件排序。
3.2 实例表(Instance Table)结构设计
实例表记录每次任务调度的执行记录,是调度平台中数据量最大、写入最频繁的表。每触发一次调度,生成一条实例记录。
| 字段名 | 类型 | 说明 | 索引策略 |
|---|---|---|---|
| instance_id | BIGINT | 主键,自增 | 聚簇索引 |
| task_id | BIGINT | 关联任务表 | 非唯一索引 |
| scheduled_time | DATETIME | 计划执行时间 | 复合索引(task_id, scheduled_time) |
| actual_start_time | DATETIME | 实际开始时间 | — |
| actual_end_time | DATETIME | 实际结束时间 | — |
| status | TINYINT | 0-等待, 1-运行中, 2-成功, 3-失败, 4-超时 | 复合索引(status, scheduled_time) |
| executor_ip | VARCHAR(32) | 执行节点 IP | 索引(用于节点负载查询) |
| retry_times | TINYINT | 已重试次数 | — |
性能陷阱:实例表是调度平台中写入 TPS 最高的表。避免在实例表上建立过多索引,尤其是联合索引的字段顺序必须遵循“等值条件在前,范围条件在后”的原则。例如复合索引 (task_id, scheduled_time) 中,task_id 是等值查询,scheduled_time 是范围查询,顺序不可颠倒。
3.3 日志表(Log Table)结构设计
日志表记录实例执行过程中的详细输出,包括标准输出、错误输出、执行上下文等。日志表是典型的“写多读少”场景,且数据量增长极快。
| 字段名 | 类型 | 说明 | 索引策略 |
|---|---|---|---|
| log_id | BIGINT | 主键,自增 | 聚簇索引 |
| instance_id | BIGINT | 关联实例表 | 非唯一索引 |
| log_level | TINYINT | 0-INFO, 1-WARN, 2-ERROR | — |
| log_content | TEXT | 日志内容 | — |
| log_time | DATETIME | 日志产生时间 | 复合索引(instance_id, log_time) |
优化建议:日志表建议按时间分区(如按月分区),并设置 TTL 自动清理过期数据。索引只需保留 (instance_id, log_time) 复合索引,用于按实例查询日志并按时间排序。避免在 log_content 上建立全文索引,会严重拖慢写入性能。
3.4 依赖表(Dependency Table)结构设计
依赖表用于定义任务之间的上下游依赖关系,是调度平台实现 DAG(有向无环图)调度的核心支撑。
| 字段名 | 类型 | 说明 | 索引策略 |
|---|---|---|---|
| dep_id | BIGINT | 主键,自增 | 聚簇索引 |
| upstream_task_id | BIGINT | 上游任务 ID | 复合索引(upstream_task_id, downstream_task_id) |
| downstream_task_id | BIGINT | 下游任务 ID | 复合索引(downstream_task_id, upstream_task_id) |
| dep_type | TINYINT | 0-强依赖, 1-弱依赖 | — |
| create_time | DATETIME | 创建时间 | — |
索引设计要点:
- 双向复合索引:依赖表需要同时支持“查询某个任务的所有下游”和“查询某个任务的所有上游”两种场景。因此建立两个复合索引:(upstream_task_id, downstream_task_id) 和 (downstream_task_id, upstream_task_id)。
- 唯一约束:建议在 (upstream_task_id, downstream_task_id) 上建立唯一约束,防止重复依赖定义导致 DAG 环检测异常。
3.5 核心表关系与索引覆盖全景图
下图展示了调度平台四张核心表之间的关联关系以及关键索引的覆盖路径,帮助理解数据流走向。
3.6 索引设计避坑清单
常见索引设计错误:
- 过度索引:实例表和日志表写入频繁,每增加一个索引都会降低写入性能。只保留查询必需的索引。
- 索引字段顺序错误:复合索引中,等值条件字段必须放在范围条件字段之前。例如 (task_id, scheduled_time) 正确,(scheduled_time, task_id) 会导致索引失效。
- 忽略覆盖索引:对于高频查询(如按 task_id 查询实例状态),将 status 字段包含在索引中,避免回表查询。
- 主键选择不当:实例表和日志表使用自增 BIGINT 主键,保证写入时的页分裂最小化。避免使用 UUID 或业务主键。
3.7 索引维护与监控
索引设计不是一次性工作,需要持续监控和优化。以下是生产环境中的索引维护建议:
| 监控指标 | 阈值 | 处理措施 |
|---|---|---|
| 索引碎片率 | > 30% | 执行索引重建(REBUILD)或重组(REORGANIZE) |
| 缺失索引 | 扫描次数 > 1000/天 | 分析查询计划,补充缺失索引 |
| 未使用索引 | 使用次数 = 0 且超过 7 天 | 评估后删除,减少写入开销 |
| 索引查找 vs 扫描比例 | 扫描占比 > 60% | 检查索引选择性,考虑调整索引字段 |
生产建议:在低峰期(如凌晨 2:00-4:00)执行索引维护任务。对于 MySQL 数据库,使用
ALTER TABLE ... ENGINE=InnoDB 重建表空间;对于 PostgreSQL,使用 REINDEX 命令。同时开启慢查询日志,定期分析 Top N 慢 SQL,针对性优化索引。