前面已经将 Airflow 2.3.4 的元数据库从默认 SQLite 修改为了 MySQL,并将执行器修改为了 LocalExecutor。
元数据库配置完成之后,Airflow 的很多运行状态都会写入 MySQL 中。
例如:
- DAG 是否暂停
- DAG 最近一次解析时间
- DAG Run 的运行状态
- Task Instance 的运行状态
- 任务失败记录
- 任务重试记录
- XCom 数据
- Connection 配置
- Variable 配置
- Web UI 用户、角色和权限
所以,想要真正排查 Airflow 调度问题,除了会看 Web UI,还需要理解元数据库中几张核心表的作用。
本文基于 Apache Airflow 2.3.4 初始化后的 MySQL 元数据库 airflow_db 进行整理。
一、Airflow 元数据库整体结构
下面这张图是通过 DBeaver 查看 airflow_db 得到的元数据库 ER 图。

从图中可以看到,Airflow 元数据库中的表并不是完全围绕一条主线展开的,而是可以大致分成几类:
| 分类 | 主要表 | 作用 |
|---|---|---|
| DAG 定义相关 | dag、serialized_dag、dag_code、dag_pickle、dag_tag、import_error |
保存 DAG 基础信息、序列化信息、源码和解析错误 |
| 调度运行核心 | dag_run、task_instance |
保存 DAG 运行记录和任务实例运行记录 |
| 任务运行辅助 | task_fail、task_reschedule、task_map、rendered_task_instance_fields、xcom |
保存失败、重调度、动态任务、模板渲染和任务间传值 |
| 调度器与日志 | job、log、log_template、callback_request、sla_miss、sensor_instance、trigger |
保存调度器心跳、操作日志、日志模板、SLA 和 Trigger 信息 |
| 配置与资源 | connection、variable、slot_pool |
保存连接信息、变量和资源池 |
| Web 权限 | ab_user、ab_role、ab_permission、ab_view_menu 等 |
保存 Airflow Web UI 的用户、角色和权限 |
| 数据库版本 | alembic_version |
保存元数据库迁移版本 |
在实际排查问题时,不需要一开始就记住所有表。
最常用、最应该先理解的是:
dag
dag_run
task_instance
job
log
connection
variable
其中 dag_run 和 task_instance 是调度状态排查的核心。
二、当前元数据库表清单
当前 Airflow 2.3.4 初始化后,airflow_db 中一共有 33 张表。
可以通过下面的 SQL 查看:
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = 'airflow_db'
ORDER BY table_name;
当前环境中的表如下:
| 表名 | 当前行数 | 说明 |
|---|---|---|
ab_permission |
11 | Web 权限动作 |
ab_permission_view |
89 | 权限和菜单视图的关联 |
ab_permission_view_role |
207 | 角色和权限视图的关联 |
ab_register_user |
0 | 注册用户信息 |
ab_role |
5 | Web 角色 |
ab_user |
0 | Web 用户 |
ab_user_role |
0 | 用户和角色关联 |
ab_view_menu |
48 | Web 菜单视图 |
alembic_version |
1 | 数据库迁移版本 |
callback_request |
0 | 回调请求 |
connection |
52 | Airflow Connection 配置 |
dag |
0 | DAG 基础状态 |
dag_code |
0 | DAG 源码 |
dag_pickle |
0 | Pickle 格式 DAG |
dag_run |
0 | DAG 运行记录 |
dag_tag |
0 | DAG 标签 |
import_error |
0 | DAG 导入错误 |
job |
1 | Scheduler 等 Job 运行记录 |
log |
7 | 操作日志 |
log_template |
2 | 任务日志路径模板 |
rendered_task_instance_fields |
0 | 渲染后的任务字段 |
sensor_instance |
0 | Sensor 实例 |
serialized_dag |
0 | 序列化 DAG |
session |
5 | Web Session |
sla_miss |
0 | SLA Miss 记录 |
slot_pool |
0 | Pool 资源池 |
task_fail |
0 | 任务失败记录 |
task_instance |
0 | 任务实例记录 |
task_map |
0 | 动态任务映射 |
task_reschedule |
0 | 任务重新调度记录 |
trigger |
0 | Trigger 记录 |
variable |
0 | Airflow Variable |
xcom |
0 | 任务间通信数据 |
可以看到,在刚初始化完成、还没有运行 DAG 的情况下,大量调度相关表都是空的。
这也比较正常。
因为 dag_run、task_instance、task_fail、xcom 等表,只有在 DAG 被解析、触发、运行之后才会逐渐产生数据。
三、DAG 定义相关表
3.1 dag
dag 表保存 DAG 的基础状态信息。
这个表不是保存完整 DAG 代码的地方,而是保存 Airflow 识别到的 DAG 状态。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_id |
DAG 的唯一标识 |
is_paused |
DAG 是否暂停 |
is_subdag |
是否为 SubDAG |
is_active |
DAG 是否仍然活跃 |
last_parsed_time |
最近一次被解析的时间 |
fileloc |
DAG 文件路径 |
owners |
DAG 所属用户 |
description |
DAG 描述 |
schedule_interval |
调度周期 |
next_dagrun |
下一次 DAG Run 时间 |
next_dagrun_create_after |
下一次允许创建 DAG Run 的时间 |
max_active_tasks |
当前 DAG 最大并发任务数 |
max_active_runs |
当前 DAG 最大并发运行数 |
has_import_errors |
是否存在导入错误 |
timetable_description |
调度时间表描述 |
如果某个 DAG 在页面上看不到,或者页面上显示有导入错误,可以先查看 dag 和 import_error。
常用 SQL:
SELECT dag_id,
is_paused,
is_active,
last_parsed_time,
fileloc,
next_dagrun,
has_import_errors
FROM dag
ORDER BY last_parsed_time DESC;
3.2 serialized_dag
serialized_dag 表保存序列化后的 DAG 信息。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_id |
DAG ID |
fileloc |
DAG 文件路径 |
fileloc_hash |
文件路径 Hash |
data |
序列化后的 DAG JSON |
last_updated |
最近更新时间 |
dag_hash |
DAG 内容 Hash |
data_compressed |
压缩后的 DAG 数据 |
在 Airflow 2.x 中,Webserver 不一定直接解析所有 DAG 文件,而是可以读取序列化后的 DAG 数据进行展示。
所以 serialized_dag 对 Web UI 展示很重要。
3.3 dag_code
dag_code 表保存 DAG 文件的源码。
主要字段如下:
| 字段 | 说明 |
|---|---|
fileloc_hash |
DAG 文件路径 Hash,主键 |
fileloc |
DAG 文件路径 |
source_code |
DAG 源码内容 |
last_updated |
最近更新时间 |
如果在 Web UI 中查看 DAG Code,背后就可能会用到这类数据。
3.4 dag_tag
dag_tag 表保存 DAG 标签。
主要字段如下:
| 字段 | 说明 |
|---|---|
name |
标签名称 |
dag_id |
DAG ID |
主键是:
name + dag_id
一个 DAG 可以有多个标签,一个标签也可以被多个 DAG 使用。
3.5 import_error
import_error 表保存 DAG 文件导入失败记录。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
timestamp |
错误发生时间 |
filename |
发生错误的 DAG 文件 |
stacktrace |
错误堆栈 |
如果页面上显示 DAG Import Errors,可以直接查这张表:
SELECT timestamp, filename, stacktrace
FROM import_error
ORDER BY timestamp DESC;
这张表对排查 DAG 语法错误、依赖缺失、模块导入失败很有用。
四、调度运行核心表
4.1 dag_run
dag_run 表表示一次 DAG 运行。
一个 DAG 每被调度一次,或者被手动触发一次,都会产生一条 DAG Run 记录。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
自增主键 |
dag_id |
DAG ID |
execution_date |
逻辑执行时间 |
state |
DAG Run 状态 |
run_id |
运行 ID |
external_trigger |
是否外部触发 |
conf |
手动触发时传入的配置 |
start_date |
实际开始时间 |
end_date |
实际结束时间 |
run_type |
运行类型,例如 scheduled、manual |
last_scheduling_decision |
最近一次调度决策时间 |
creating_job_id |
创建该 DAG Run 的 Job ID |
queued_at |
入队时间 |
data_interval_start |
数据区间开始时间 |
data_interval_end |
数据区间结束时间 |
log_template_id |
日志模板 ID |
这里要注意 execution_date。
在 Airflow 2.x 中,它更接近“逻辑日期”,不一定等于任务真实开始执行的时间。
任务真实开始执行的时间要看 start_date。
常用查询:
SELECT dag_id,
run_id,
run_type,
state,
execution_date,
start_date,
end_date,
data_interval_start,
data_interval_end
FROM dag_run
ORDER BY execution_date DESC;
如果一个 DAG 没有按预期触发,可以先查 dag_run:
SELECT *
FROM dag_run
WHERE dag_id = 'your_dag_id'
ORDER BY execution_date DESC;
4.2 task_instance
task_instance 表表示一次任务实例运行记录。
这是 Airflow 元数据库中排查任务问题最重要的一张表。
一个 DAG 中有多个 Task,一次 DAG Run 下每个 Task 都会对应一条 Task Instance 记录。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_id |
DAG ID |
task_id |
Task ID |
run_id |
所属 DAG Run ID |
map_index |
动态任务映射下标,普通任务一般为 -1 |
state |
Task Instance 状态 |
try_number |
当前尝试次数 |
start_date |
开始执行时间 |
end_date |
结束执行时间 |
duration |
执行耗时 |
hostname |
执行任务的主机 |
unixname |
执行用户 |
job_id |
关联的 Job ID |
pool |
使用的 Pool |
queue |
队列名称 |
priority_weight |
优先级权重 |
operator |
Operator 类型 |
queued_dttm |
入队时间 |
pid |
执行进程 PID |
max_tries |
最大重试次数 |
executor_config |
Executor 配置 |
pool_slots |
占用 Pool Slot 数量 |
trigger_id |
关联 Trigger ID |
trigger_timeout |
Trigger 超时时间 |
next_method |
Deferrable Operator 恢复时调用的方法 |
next_kwargs |
恢复方法参数 |
task_instance 的主键是:
dag_id + task_id + run_id + map_index
也就是说,同一个 dag_id 和 task_id 可能会出现很多条记录,因为它们属于不同的 run_id。
常用查询:
SELECT dag_id,
task_id,
run_id,
state,
try_number,
start_date,
end_date,
duration,
operator,
hostname
FROM task_instance
ORDER BY start_date DESC;
查看失败任务:
SELECT dag_id,
task_id,
run_id,
state,
try_number,
start_date,
end_date
FROM task_instance
WHERE state = 'failed'
ORDER BY end_date DESC;
查看排队中的任务:
SELECT dag_id,
task_id,
run_id,
state,
queued_dttm,
pool,
queue,
priority_weight
FROM task_instance
WHERE state = 'queued'
ORDER BY queued_dttm;
4.3 dag_run 和 task_instance 的关系
dag_run 和 task_instance 是一对多关系。
可以简单理解为:
一次 DAG Run
|
+-- 多个 Task Instance
在当前 Airflow 2.3.4 的元数据库中,task_instance 通过下面的外键关联到 dag_run:
task_instance(dag_id, run_id) -> dag_run(dag_id, run_id)
排查任务时,经常会把这两张表关联查询。
例如查看某个 DAG 最近一次运行下所有任务状态:
SELECT dr.dag_id,
dr.run_id,
dr.state AS dag_state,
ti.task_id,
ti.state AS task_state,
ti.try_number,
ti.start_date,
ti.end_date
FROM dag_run dr
JOIN task_instance ti
ON dr.dag_id = ti.dag_id
AND dr.run_id = ti.run_id
WHERE dr.dag_id = 'your_dag_id'
ORDER BY dr.execution_date DESC, ti.task_id;
从这张图中可以看到,排查调度状态时,dag_run 表示一次工作流运行,task_instance 表示这次运行中的具体任务状态。失败、重调度、XCom 和模板渲染结果,都是围绕具体的 Task Instance 展开的。
五、任务运行辅助表
5.1 task_fail
task_fail 表保存任务失败记录。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
task_id |
Task ID |
dag_id |
DAG ID |
start_date |
失败这次尝试的开始时间 |
end_date |
失败这次尝试的结束时间 |
duration |
失败这次尝试的耗时 |
map_index |
动态任务映射下标 |
run_id |
DAG Run ID |
这张表适合统计任务失败次数。
例如:
SELECT dag_id,
task_id,
COUNT(*) AS fail_count
FROM task_fail
GROUP BY dag_id, task_id
ORDER BY fail_count DESC;
5.2 task_reschedule
task_reschedule 表保存任务重新调度记录。
它常见于 Sensor 任务,特别是 mode='reschedule' 的场景。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
task_id |
Task ID |
dag_id |
DAG ID |
try_number |
尝试次数 |
start_date |
本次检查开始时间 |
end_date |
本次检查结束时间 |
duration |
本次检查耗时 |
reschedule_date |
下一次重新调度时间 |
run_id |
DAG Run ID |
map_index |
动态任务映射下标 |
如果一个 Sensor 一直没有成功,可以查这张表看它是否在反复 reschedule。
5.3 rendered_task_instance_fields
rendered_task_instance_fields 表保存模板渲染后的任务字段。
例如 BashOperator 中写了模板变量:
bash_command='echo {{ ds }}'
Airflow 在具体运行时会将模板变量渲染成实际值,渲染结果就可能保存到这张表中。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_id |
DAG ID |
task_id |
Task ID |
run_id |
DAG Run ID |
map_index |
动态任务映射下标 |
rendered_fields |
渲染后的字段 |
k8s_pod_yaml |
Kubernetes Pod YAML |
5.4 xcom
xcom 表保存任务之间传递的小数据。
XCom 的全称是 Cross Communication。
它适合在 Task 之间传递少量元信息,例如上游任务生成的文件路径、运行结果标记、某个小的 JSON 对象。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_run_id |
DAG Run 主键 ID |
task_id |
Task ID |
key |
XCom Key |
value |
XCom Value,二进制保存 |
timestamp |
写入时间 |
dag_id |
DAG ID |
run_id |
DAG Run ID |
map_index |
动态任务映射下标 |
注意,XCom 不适合传递大量数据。
如果需要在任务之间传递大文件或大数据集,更合理的方式是把数据写到 HDFS、对象存储、MySQL、Hive 等外部系统中,然后在 XCom 中只传路径或标识。
5.5 task_map
task_map 表和动态任务映射有关。
主要字段如下:
| 字段 | 说明 |
|---|---|
dag_id |
DAG ID |
task_id |
Task ID |
run_id |
DAG Run ID |
map_index |
映射下标 |
length |
映射长度 |
keys |
映射 Key |
如果不使用 Dynamic Task Mapping,这张表通常不会有数据。
六、调度器和日志相关表
6.1 job
job 表保存 Airflow 内部 Job 的运行记录,例如 SchedulerJob。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
dag_id |
DAG ID,有些 Job 不绑定具体 DAG |
state |
Job 状态 |
job_type |
Job 类型 |
start_date |
开始时间 |
end_date |
结束时间 |
latest_heartbeat |
最近心跳时间 |
executor_class |
使用的 Executor |
hostname |
主机名 |
unixname |
系统用户 |
当前环境中已经有一条 Scheduler 记录:
job_type: SchedulerJob
state: running
executor_class: LocalExecutor
hostname: ubuntu-linux-2404
如果怀疑 Scheduler 没有正常工作,可以看 job 表中的心跳时间:
SELECT id,
state,
job_type,
start_date,
latest_heartbeat,
executor_class,
hostname
FROM job
ORDER BY latest_heartbeat DESC;
6.2 log
log 表保存 Airflow 操作日志。
注意,这里不是每个任务的完整运行日志。
任务运行日志通常保存在本地日志目录、远程日志系统或对象存储中,而 log 表保存的是 Airflow 的事件记录。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
dttm |
事件时间 |
dag_id |
DAG ID |
task_id |
Task ID |
event |
事件名称 |
execution_date |
执行时间 |
owner |
操作用户 |
extra |
额外信息 |
map_index |
动态任务映射下标 |
当前环境中已经记录了几条 CLI 操作:
cli_users_create
cli_check
cli_webserver
cli_scheduler
常用查询:
SELECT dttm,
dag_id,
task_id,
event,
owner
FROM log
ORDER BY dttm DESC;
6.3 log_template
log_template 表保存任务日志路径模板。
当前环境中有两条默认模板。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
filename |
日志文件路径模板 |
elasticsearch_id |
Elasticsearch 日志 ID 模板 |
created_at |
创建时间 |
例如其中一条模板类似:
dag_id={{ ti.dag_id }}/run_id={{ ti.run_id }}/task_id={{ ti.task_id }}/attempt={{ try_number }}.log
这说明 Airflow 会根据 DAG ID、Run ID、Task ID、尝试次数组织任务日志路径。
6.4 sensor_instance、trigger、callback_request、sla_miss
这几张表属于更细的调度能力。
| 表名 | 作用 |
|---|---|
sensor_instance |
保存 Sensor 实例状态 |
trigger |
保存 Trigger 信息,主要和 Deferrable Operator 相关 |
callback_request |
保存回调请求 |
sla_miss |
保存 SLA 未满足记录 |
初学阶段不需要优先记住这些表。
当遇到 Sensor、SLA、Deferrable Operator 相关问题时,再重点查看。
七、配置和资源相关表
7.1 connection
connection 表保存 Airflow 中配置的连接信息。
在 Web UI 中进入:
Admin -> Connections
看到的连接信息就来自这张表。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
conn_id |
连接 ID |
conn_type |
连接类型 |
host |
主机地址 |
schema |
数据库或 Schema |
login |
用户名 |
password |
密码 |
port |
端口 |
extra |
额外配置 |
is_encrypted |
密码是否加密 |
is_extra_encrypted |
extra 是否加密 |
description |
描述 |
当前环境初始化后,connection 表中有 52 条默认连接。
例如:
airflow_db
aws_default
azure_default
cassandra_default
databricks_default
查询示例:
SELECT conn_id,
conn_type,
host,
`schema`,
login,
port
FROM connection
ORDER BY conn_id;
注意,connection 表中可能保存密码,不建议在博客或截图中直接暴露完整内容。
7.2 variable
variable 表保存 Airflow Variables。
在 Web UI 中进入:
Admin -> Variables
看到的变量信息就来自这张表。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
key |
变量名 |
val |
变量值 |
is_encrypted |
是否加密 |
description |
描述 |
Variables 适合保存一些运行参数,例如环境标识、路径、开关配置等。
不要把大块业务数据放到 Variables 中。
7.3 slot_pool
slot_pool 表保存 Airflow Pool 配置。
Pool 用来限制某类任务的并发数。
主要字段如下:
| 字段 | 说明 |
|---|---|
id |
主键 |
pool |
Pool 名称 |
slots |
Slot 数量 |
description |
描述 |
例如某个外部接口最多只能同时被 3 个任务调用,就可以为这类任务配置一个 Pool,并设置 slots = 3。
任务实例表 task_instance 中也有 pool 和 pool_slots 字段,用来记录任务使用哪个 Pool、占用多少 Slot。
八、Web 用户和权限相关表
Airflow Web UI 使用 Flask AppBuilder 做权限管理,所以会有一组 ab_ 开头的表。
主要表如下:
| 表名 | 说明 |
|---|---|
ab_user |
Web 用户 |
ab_role |
角色 |
ab_user_role |
用户和角色关联 |
ab_permission |
权限动作 |
ab_view_menu |
菜单或视图 |
ab_permission_view |
权限动作和菜单视图关联 |
ab_permission_view_role |
角色和权限视图关联 |
ab_register_user |
注册用户 |
session |
Web Session |
这组表主要服务于 Web UI 登录和权限控制。
例如创建管理员用户:
airflow users create \
--username admin \
--firstname Admin \
--lastname User \
--role Admin \
--email admin@example.com \
--password admin
执行后,相关用户和角色信息会写入 ab_user、ab_role、ab_user_role 等表。
一般情况下,不建议直接手动修改这组权限表。
更推荐使用 Airflow CLI 或 Web UI 进行管理。
九、数据库迁移版本表
9.1 alembic_version
alembic_version 表用于记录 Airflow 元数据库迁移版本。
当前环境中的版本号是:
f5fcbda3e651
查看 SQL:
SELECT *
FROM alembic_version;
Airflow 使用 Alembic 管理数据库结构变更。
执行下面这些命令时,都可能会读取或更新这张表:
airflow db init
airflow db upgrade
airflow db check
如果升级 Airflow 版本后元数据库结构不一致,通常就需要关注这张表和 Airflow 的数据库迁移日志。
十、核心外键关系
从当前数据库中可以看到几个比较重要的外键关系。
10.1 DAG 标签关系
dag_tag(dag_id) -> dag(dag_id)
表示标签属于某个 DAG。
10.2 DAG Run 与 Task Instance
task_instance(dag_id, run_id) -> dag_run(dag_id, run_id)
这是调度运行中最核心的关系。
10.3 Task Instance 与失败、重试、XCom
task_fail(dag_id, task_id, run_id, map_index) -> task_instance(...)
task_reschedule(dag_id, task_id, run_id, map_index) -> task_instance(...)
xcom(dag_id, task_id, run_id, map_index) -> task_instance(...)
rendered_task_instance_fields(...) -> task_instance(...)
这些表都围绕具体任务实例展开。
也就是说,先找到 task_instance,再继续查失败、重试、XCom、模板渲染结果,排查思路会更清楚。
10.4 权限关系
ab_user_role(user_id) -> ab_user(id)
ab_user_role(role_id) -> ab_role(id)
ab_permission_view_role(role_id) -> ab_role(id)
ab_permission_view_role(permission_view_id) -> ab_permission_view(id)
这部分主要用于 Web UI 权限。
十一、常用排查 SQL
11.1 查看 DAG 状态
SELECT dag_id,
is_paused,
is_active,
last_parsed_time,
next_dagrun,
has_import_errors
FROM dag
ORDER BY last_parsed_time DESC;
11.2 查看最近 DAG Run
SELECT dag_id,
run_id,
run_type,
state,
execution_date,
start_date,
end_date
FROM dag_run
ORDER BY execution_date DESC
LIMIT 20;
11.3 查看失败任务
SELECT dag_id,
task_id,
run_id,
state,
try_number,
start_date,
end_date
FROM task_instance
WHERE state = 'failed'
ORDER BY end_date DESC;
11.4 查看排队任务
SELECT dag_id,
task_id,
run_id,
state,
queued_dttm,
pool,
queue
FROM task_instance
WHERE state = 'queued'
ORDER BY queued_dttm;
11.5 查看 Scheduler 心跳
SELECT id,
state,
job_type,
latest_heartbeat,
executor_class,
hostname
FROM job
ORDER BY latest_heartbeat DESC;
11.6 查看 DAG 导入错误
SELECT timestamp,
filename,
stacktrace
FROM import_error
ORDER BY timestamp DESC;
11.7 查看 Connection 配置
SELECT conn_id,
conn_type,
host,
`schema`,
login,
port
FROM connection
ORDER BY conn_id;
十二、总结
Airflow 元数据库中的表很多,但是排查问题时并不需要平均用力。
可以先按下面的顺序理解:
dag:Airflow 是否识别到 DAG。dag_run:DAG 有没有被调度或触发。task_instance:具体 Task 当前是什么状态。task_fail、task_reschedule、xcom:任务失败、重新调度和任务间通信。job:Scheduler 等内部进程是否还在正常心跳。log、import_error:操作记录和 DAG 导入错误。connection、variable、slot_pool:Airflow 配置和资源限制。
简单来说:
dag_run 看工作流运行,task_instance 看任务运行,job 看调度器心跳,import_error 看 DAG 是否解析失败。
理解这几张表之后,再看 Airflow Web UI 中的状态,就不会只是停留在页面展示,而是能进一步从数据库层面确认调度链路到底卡在哪里。