体育数据缓存设计:让“最后成功更新时间”真正可信
围绕基础资料与即时比分,说明缓存分层、原子写入、失败回退及时间展示的设计方法,区分请求尝试时间、缓存提交时间与数据业务时间。

体育数据页面不仅要展示内容,还应说明内容的新鲜程度。请求刚刚结束,并不代表缓存已经成功更新。本文先列出百查数据体育 API 已核验的接口事实,再讨论接入方可采用的通用工程方案;缓存策略、时间字段和刷新流程均为设计建议,不代表接口自带能力。
一、从接口目录划分缓存对象
已核验目录包含国家列表,以及联赛、赛季、阶段、球队、球员、教练、裁判、场馆的分页列表;另有比赛-即时比分接口,均使用 GET。以下列出部分路径,域名为 https://api.superscore.cn。目录未说明刷新频率、响应字段或分页参数,实施前应核对相应接口文档。
| 已核验接口 | 方法 | 路径 |
|---|---|---|
| 国家列表 | GET | /football/base/countryList |
| 联赛分页列表 | GET | /football/base/leaguePage |
| 球队分页列表 | GET | /football/base/teamPage |
| 比赛-即时比分 | GET | /football/change/live |
工程上可将基础资料与即时比分分别管理,按业务需要设置刷新周期,而不是统一使用一个过期时间。缓存键应包含接口路径、规范化请求参数及必要的数据隔离维度;分页数据还要区分各页,避免不同查询互相覆盖。具体参数名称以接口文档为准。

二、定义“最后成功”而非“最后请求”
建议在接入方记录三个概念:最近尝试时间、最后成功更新时间、数据业务时间。前两者由本地刷新流程产生;第三者只有在接口文档明确提供相关字段与语义时才使用。最后成功更新时间应表示:响应通过校验,并已作为可读取版本提交到缓存的时刻,而非请求发出或收到响应的时刻。
- 请求失败、解析失败或缓存提交失败:记录错误,不推进最后成功更新时间。
- 合法空结果:按接口约定判断,不能仅因列表为空就判定失败。
- 分页全量快照:完成预定分页抓取与校验后再发布,避免新旧页混用。
- 内容与成功时间:作为同一版本原子提交,避免新时间对应旧数据。
以下为接入方伪代码。fetch、validate 与 atomic_publish 都是本地抽象,不是百查接口字段或方法。原子发布应保证失败时旧版本仍可读取;存储侧可按技术栈选用事务或版本指针切换。
# 接入方伪代码;时间使用 UTC
def refresh(key):
record_attempt(key, utc_now())
try:
payload = fetch(key)
validate(payload)
atomic_publish(key, {
"data": payload,
"last_success_at": utc_now()
})
except Exception as error:
record_failure(key, error)
# 不覆盖旧数据,不推进成功时间
三、失败回退与页面展示
缓存过期与缓存删除应分开处理。刷新失败时,可在业务允许的陈旧窗口内继续展示旧值,并标注“最近成功更新于……”和刷新异常状态。超过可接受窗口后,应提示数据可能过时;从未成功加载时显示“暂无可用数据”,不要填入当前时间冒充成功记录。
同一缓存键宜限制并发刷新,或通过版本校验防止较早请求晚到后覆盖新结果。后台统一存储 UTC 时间,前端转换为用户时区;监控重点关注距最后成功更新的时长与连续失败次数。测试至少覆盖请求超时、合法空结果、分页中断和写入失败,确保失败不会让页面看起来更“新”。
