跳到主要内容

GitHub 事件数据质量实测:GH Archive 与 OpenDigger 还可靠吗?

· 阅读需 19 分钟
wubx
Data AI

阿里云 MaxCompute 文档里有一个案例:用 GitHub 公共事件数据,给开源数据库做热度和活跃度排名。我想在 Databend Cloud 上用最新数据复现它。

这次我没有手写 SQL:AI Agent Evot 通过 Databend 官方的 databend-cloud skill 操作 Databend Cloud,我用自然语言提要求,建表、导入和检查的 SQL 都由 Evot 生成并执行。

动手前,我让 Evot 先导入一个小时试试:2026 年 10 月 24 日 0 点(UTC)。

GH Archive(按小时归档 GitHub 公共事件的公开数据集)的这个文件里,一条 Push 都没有。换成 OpenDigger 的同名文件,有 288,431 条 Push——但按记录里的 created_at,没有一条属于这个小时,全部是前一天的。

两份数据导入时都是零错误、零重复,问题出在数据本身。这一小时的样本里,有三个会直接影响排名的问题:

  1. GH Archive 会整类缺失事件:这一小时没有 Push、Create、Delete。
  2. 归档小时不等于事件时间:OpenDigger 文件中只有 33.47% 的事件属于该小时;GitHub 官方 API 的响应里,也能看到同样的类型错位。
  3. 关键字段已被 GitHub 官方裁剪:事件里已经没有 commit 数、PR 语言和代码增删。

一句话结论:近期的 GitHub 事件数据适合观察趋势、发现线索,不适合直接做精确统计和排名。

范围说明:结论来自两个来源各一个完整小时文件,加上少量邻近小时和 API 抽查,不是全站长期审计。第 3 点有 GitHub 官方公告;前两点是实测现象,根因尚未完全确定。

问题一:GH Archive 部分小时缺少 PushEvent​

GH Archive 的 0 点文件有 2,850 条事件,涵盖 12 种类型,但 PushEvent、CreateEvent、DeleteEvent 都是 0。

先排除导入问题:Evot 把原始压缩文件完整解析了一遍,总数、类型分布、每分钟分布都和表内一致。

再看邻近小时:

UTC 归档小时事件数PushEvent
10-23 00 点2,8280
10-24 00 点2,8500
10-24 01 点2,6620
10-24 06 点22,70316,752
10-24 12 点31,53823,569

在抽查到 Push 的几个小时里,它约占全部事件的四分之三。没有 Push 的那几个小时,总量也只有其他小时的十分之一左右。

所以不是 GitHub 取消了 PushEvent,而是部分小时的归档文件缺了这一类事件。原因目前没有官方说明;GH Archive 社区也有用户报告过事件量下降、获星事件捕获率下降等问题。

对排名来说,这种缺口最危险:汇总后,它看起来就是“这一小时没有活动”。

问题二:OpenDigger 的归档时间与 created_at 不一致​

OpenDigger 自 2026 年 9 月起独立采集 GitHub 事件,提供与 GH Archive 兼容的小时文件(发布说明)。同一个 0 点文件约 508 MB,导入 644,116 条,与它的 manifest 声明一致,事件 ID 也没有重复。

按文件统计,Push 有 288,431 条,缺口似乎补上了。再把 created_at 限定在 24 日 0 点:

类型文件内条数created_at 在 24 日 0 点
PushEvent288,4310
CreateEvent37,4670
DeleteEvent18,3210
PullRequestEvent101,67588,992
WatchEvent11,73410,783

这 28 万条 Push 的时间都在 10 月 23 日 00:30 到 23:17 之间。整个文件只有 33.47% 的事件属于该小时,其余全部更早;早于 30 天的有 783 条,最早一条是 2015 年的 PublicEvent。

OpenDigger 的 manifest 记录的是 first_observed_at 和 last_observed_at。结合数据看,这个文件更像是“这一小时采集到的事件”,而不是“这一小时发生的事件”。这是根据输出做的推断,我们没有读到它线上采集器的源码。

所以,直接拿同名小时文件做对比没有意义。把两个来源都按 created_at 限定到 24 日 0 点后,OpenDigger 有 215,598 条,GH Archive 有 2,850 条,共同的事件 ID 为 2,633,双方各有对方没有的事件。没有全量基准,行数多不能直接等同于更完整。

GitHub Events API 本身也有时间错位​

时间错位不一定是归档造成的。Evot 绕过两个归档,直接请求了一页 GitHub 官方 /events(2026-10-27 03:25 UTC,匿名,100 条):

  • 85 条 Push、9 条 Create、3 条 Delete,created_at 都在前一天;
  • 2 条 PR、1 条 Issue,是约 5 分钟前的。

GitHub 文档说明,事件可能延迟 30 秒到 6 小时;而这一页里的 Push 都滞后 6.5 小时以上,最长约 23 小时。

这只是当前的一页响应,不能还原 24 日当时的采集情况,也解释不了 2015 年的记录。但至少说明:按类型滞后的现象,在 GitHub 原始响应里就存在。

问题三:GitHub Events API 已裁剪 payload 字段​

这是三个问题里唯一有官方定论的。GitHub 在 2025 年 8 月 8 日公告,计划于 10 月 7 日起精简 Events API 的 payload:PushEvent 不再包含 commit 摘要和数量,PullRequestEvent 移除部分详细字段,多类事件移除 author_association。

样本对照:

字段2025-09-24 样本2026-10-24 两个来源
Push 的 commits、size有值全部为空
PR 的语言、merged、代码增删、变更文件数有值全部为空

(2025-09-24 只取了文件前 5,000 条,用来确认字段是否存在,其中 PR 语言非空 333/362 条。GH Archive 24 日 0 点没有 Push,Push 字段用同日 6、9、12 点文件核对。)

原案例中的 commit 统计、编程语言分布、代码贡献量,因此无法在近期事件数据上复现。换归档源、换数据库都补不回来,只能另外通过 GitHub REST API 按仓库或对象补取。

用 GitHub 事件数据做排名前,先做哪三项检查?​

如果要用近期的 GitHub 事件数据,至少先做三项检查:

  1. 按小时看类型分布:某类事件突然归零,先怀疑采集缺口,而不是没有活动。
  2. 核对 created_at 与归档小时:不一致的比例高,就不能按文件名统计;按 created_at 统计,还要考虑迟到事件可能出现在后续文件里。
  3. 统计关键字段覆盖率:缺失要保留为 NULL,不能当成 0。

还有几个口径要分开:获星事件数不是 Star 总数,Push 次数不是 commit 数,没有记录不等于没有活动。

我最后没有继续做排名。若要比较数据库项目,我会改用仓库 API 的 PR、Issue、Release 数据或定期的 Star 快照,而不是把公共事件流当成完整账本。

如何用 Databend Cloud 和 Evot 检查数据质量​

整个过程只往数据库里加载了两个小时文件,没有搭任何数据管道。

Databend Cloud 负责把数据装进来、查清楚。 两个来源各建一张表,结构完全相同。顶层的标量字段用普通类型,JSON 对象原样存为 VARIANT,不提前拆平,需要哪个字段再取:

CREATE TABLE wubx.github_events_opendigger (
id STRING NOT NULL, -- 事件 ID
type STRING NOT NULL, -- 事件类型,如 PushEvent
actor VARIANT NULL, -- 发起账号,完整 JSON
repo VARIANT NULL, -- 仓库,完整 JSON
payload VARIANT NULL, -- 事件内容,完整 JSON
public BOOLEAN NULL,
created_at TIMESTAMP NOT NULL, -- 事件时间,UTC
org VARIANT NULL, -- 组织,可选
_extra_fields VARIANT NOT NULL, -- 未单独建列的顶层字段
_source_file STRING NOT NULL -- 来源文件名
);

GH Archive 的表 wubx.github_events 用同样的结构,只是表名不同。

508 MB 的压缩文件,一条 COPY INTO 直接从 HTTPS 地址加载,不用下载到本地,也不用先上传 Stage。下面以 OpenDigger 为例:

COPY INTO wubx.github_events_opendigger
(id, type, actor, repo, payload, public, created_at, org,
_extra_fields, _source_file)
FROM (
SELECT $1:id::STRING, $1:type::STRING,
$1:actor, $1:repo, $1:payload,
$1:public::BOOLEAN, $1:created_at::TIMESTAMP, $1:org,
OBJECT_DELETE($1, 'id', 'type', 'actor', 'repo', 'payload',
'public', 'created_at', 'org'),
METADATA$FILENAME
FROM 'https://gharchive.open-digger.net/2026-10-24-0.json.gz'
)
FILE_FORMAT = (TYPE = NDJSON COMPRESSION = GZIP)
ON_ERROR = ABORT;

导入 GH Archive 时,只需改两处:目标表换成 wubx.github_events,FROM 后的地址换成 GH Archive 的文件:

COPY INTO wubx.github_events
(id, type, actor, repo, payload, public, created_at, org,
_extra_fields, _source_file)
FROM (
SELECT $1:id::STRING, $1:type::STRING,
$1:actor, $1:repo, $1:payload,
$1:public::BOOLEAN, $1:created_at::TIMESTAMP, $1:org,
OBJECT_DELETE($1, 'id', 'type', 'actor', 'repo', 'payload',
'public', 'created_at', 'org'),
METADATA$FILENAME
FROM 'https://data.gharchive.org/2026-10-24-0.json.gz'
)
FILE_FORMAT = (TYPE = NDJSON COMPRESSION = GZIP)
ON_ERROR = ABORT;

GH Archive 的文件地址格式是 https://data.gharchive.org/YYYY-MM-DD-H.json.gz,小时取 0 到 23,不补零,时间为 UTC。

OBJECT_DELETE 把没有单独建列的顶层字段收进 _extra_fields。本次两个样本里它全为空,说明顶层字段都已覆盖。METADATA$FILENAME 记录每行来自哪个文件,一次加载多个小时也能区分来源。

发现时间问题的,也只是一条普通聚合:

SELECT type,
COUNT(*) AS in_file,
SUM(CASE WHEN created_at >= '2026-10-24 00:00:00'::TIMESTAMP
AND created_at < '2026-10-24 01:00:00'::TIMESTAMP
THEN 1 ELSE 0 END) AS in_hour
FROM wubx.github_events_opendigger
GROUP BY type
ORDER BY in_file DESC;

Evot 通过 databend-cloud skill 操作 Databend Cloud。 这是 Databend 官方提供的 Agent Skill:Agent 用 API token 连接指定的组织和计算集群,可以查阅 Databend SQL 语法、查看库表结构、执行查询,也能结合查询历史和 profile 诊断慢查询、失败查询。

安装只需把文档里的一段话发给 Agent,它会引导你在本地保存 token,不在聊天里索取。文档示例是国内站 api.databend.cn;本次连接的是国际站,地址换成 api.databend.com 即可。

之后我全程用自然语言提要求,例如:

在 wubx 库下建表,先试导一个小时,没问题再加载 10 月 24 日全天的数据。

看下这个数据时间分布,是不是比较离谱。

看一下 OpenDigger 数据采集的逻辑,为什么这么乱。

Evot 连接 Databend Cloud 完成建表、导入和检查;遇到异常,再去完整解析原始文件、查 GitHub 变更公告和 OpenDigger 的发布说明、直接请求 GitHub API。每条 SQL 和返回结果都保存了下来,文中的数字都能对应到具体查询。

有一点要注意:skill 本身不限制写操作,能做什么取决于 token 对应账号的权限。所以我事先约定了只建新表、不覆盖、不删除;需要严格只读时,应该在数据库权限上控制。另外,Agent 会把查询结果交给模型处理,这次的检查查询只返回聚合结果,不导出原始事件内容。

它也判断错过一次:看到 OpenDigger 文件里有 28 万条 Push,一度认为它补上了 GH Archive 的缺口。按 created_at 重新统计后,这个判断被推翻。

这也是先导一个小时的价值。原计划是加载全天;如果直接那样做,得到的会是一张看起来正常、却解释不清的排行榜。

复现:数据源与 SQL​

本文用到的数据源,都是 2026 年 10 月 24 日 0 点(UTC):

完整 SQL 包含建表、导入和全部检查,把库名 wubx 换成自己的测试库,分段执行即可。OpenDigger 文件较大,注意计算和存储费用。证据快照 保存了本次所有聚合结果。

也可以用 Agent 复现:按 Agent Skills 文档 安装 databend-cloud skill、配置好 token,再把上面的数据源地址和你的要求用自然语言交给它。

如果想先把归档文件存到自己的对象存储,再从 Stage 加载,可以参考使用 OpenDAL 直接写对象存储。

公开文件之后可能被更新,重跑结果不一定与本文完全一致。本文未独立校验完整文件的 SHA-256,也不是数据库性能测试。

总结:GitHub 事件数据还能用来做什么?​

这次遇到的问题并不是个例,GH Archive 社区里已经有不少相关报告:

  • #310:2025 年 5 月 23 日之后,事件量明显下降,这个 issue 至今仍未关闭。
  • #312:2025 年 10 月 9 日起,事件量一度下降约 100 倍,后来恢复。
  • #320:2025 年 6 月起,获星事件(WatchEvent)的捕获率明显下降。
  • #317:社区提交的分页采集修复,尚未合并。
  • #323:OpenDigger 自 2026 年 9 月起,提供兼容的独立归档。

再加上 GitHub 官方裁剪了 payload,近期的事件数据已经不适合做精确统计:绝对数量、单日排名、commit 数和代码量,都不可靠。

但它仍然是一份免费、公开、能跨整个 GitHub 观察开源协作的数据。只要不追求精确,而是用它找线索,价值依然很大:

  • 发现热点项目。 把多天的数据放在一起,看哪些仓库的获星、Fork、PR 在持续增长。时间拉长后,单个小时的缺口影响会变小。要比较的是同一来源内的相对变化,不是绝对数量。找到候选项目后,再用仓库 API 核实 Star 数和发布记录。
  • 发现活跃贡献者。 PR、Review、Issue 评论这些事件,仍然记录着“谁在哪个仓库做了什么”。按多天、多个仓库汇总,就能找到长期在某个领域投入的人,比如持续给多个数据库项目提交和评审 PR 的开发者。
  • 让 AI 给贡献者打分,辅助招聘和组队。 把一个人的公开贡献交给 AI,按擅长的领域、协作方式和贡献深度打分,再推荐适合某个方向的人选,或者合适的团队组合。

给人打分时,要守住几条底线:

  1. 分数只是线索,不是评价。 数据有缺口,缺失的分布也不均匀,没有记录不等于不活跃。
  2. 先排除机器人账号。 本次两个样本里,名字以 [bot] 结尾的账号产生的事件,都占到约 31%。
  3. 看贡献质量,不看次数。 被合并的 PR、评审的深度、跨项目协作,比 Push 次数和评论条数更能说明问题。
  4. 只看公开的工作成果。 不推断与工作无关的个人信息;联系候选人之前要由人工核实,并遵守当地的隐私法规。

这些分析可以沿用这次的做法:用 Databend Cloud 加载多天的归档,再按 JSON 字段聚合;Evot 通过 databend-cloud skill 把自然语言需求变成查询,再把结果交给模型评估。这次只导入了单个小时,多天加载和 AI 打分还没有实际跑过,是下一步要做的事。

数据质量检查不是终点,而是这类分析的第一步。 先弄清数据缺在哪里,得出的结论才不会越界。

参考资料​