tva
← 洞察

使用 FastAPI 和 TimescaleDB 构建 Amazon 数据仓库

Amazon 的 SP-API 提供大量运营数据,包括订单、库存、结算和产品目录表现,广告数据则由独立的 Amazon Ads API 提供。但实际上,数据只有以可查询的形式存在,才有用。Amazon 自带报告是临时性的,受速率限制,也不是为多站点卖家最关心的运营问题设计的。您可以看到发生过什么,却很难同时了解所有站点当前的情况,或查询某个 ASIN 在特定履约区域过去九十天的需求趋势。

本文介绍我们采用的 Amazon 数据仓库架构:FastAPI 作为 API 层,TimescaleDB 作为时序数据存储,Celery 负责异步 SP-API 报告管道。目标是将我们从 Amazon 各个 API 获取的数据,都以可查询、由您控制的形式存储,并提供足够的结构来支撑需求预测和运营分析,而无需由专门的数据工程团队维护。

为什么 Amazon 自带工具不够用

对于单站点、小规模产品目录,Seller Central 报告可以满足大多数运营需求。但规模扩大后,缺口就会显现。报告按需生成,而且会过期;没有持久化查询层,运行报告、下载文件后,文件本身就是数据。六个月后运行同一报告,得到的是六个月后的数据,而不是今天的数据。

因此,任何历史分析都要求有人按适当间隔下载并保存所有相关报告,或通过交易级导出重建历史。然而,这些导出本身也受速率限制,只能在有限时间窗口内分页,而且结构面向逐条记录检索,而非批量分析。

Amazon 的 Business Reports 仪表盘提供汇总趋势数据,但粒度和保留窗口取决于 Amazon 的产品决策,而非您的分析需求。广告控制台采用独立数据模型,无法直接与履约数据顺畅关联。结算数据每两周到达一次,要与订单数据对账,还需要较复杂的解析。

自建数据仓库同时解决持久化和可查询性问题。您拥有存储、定义保留期限,并编写查询来回答业务真正需要回答的问题,而不是局限于 Amazon 仪表盘设计时考虑的问题。

技术选型

我们选择 TimescaleDB 作为主要存储,因为多数 Amazon 运营数据天然具有时序特征。订单有时间戳,库存快照对应某个时间点,结算周期有起止日期,广告支出和展示量随时间累积。TimescaleDB 在 PostgreSQL 上增加超表扩展,让大型数据集的时间范围查询明显更高效,同时完整保留 SQL 兼容性。无需学习新的查询语言,运维模式与标准 Postgres 安装也没有实质差异。

FastAPI 在从 SP-API 拉取数据的 Celery 工作进程与查询仓库的内部使用方之间提供 API 层。它以很少的样板代码处理身份验证、请求校验和响应序列化。对于不暴露于公网的内部服务,FastAPI 自动生成的 OpenAPI 文档尤其有用:它提供可查询的接口定义,使开发新的使用方更直接,无需从数据库模式反向推断数据模型。

Celery 管理异步 SP-API 报告管道。SP-API 的报告生成本来就是异步的:先请求报告,再轮询状态,准备就绪后下载结果并处理文件。这自然对应一条 Celery 任务链:发起报告请求、定期轮询报告状态、下载并保存原始文件,以及解析结构化数据并加载到 TimescaleDB。Redis 用作 Celery 的消息代理和结果后端。

数据模型

核心表对应 SP-API 的主要数据域。设计原则是:每张表都应能回答某一类具体运营问题,而无需关联多种报告类型。

订单表是按 purchase_date 分区的超表。每行代表一个订单商品项,而非一张订单,因为最常用的分析粒度是商品级,即按 ASIN、履约渠道和站点分析。订单级汇总从商品级记录计算。超表分区间隔设为一周,以便需求预测所用的滚动窗口查询获得良好性能。

库存快照表保存拉取库存报告时的结果。FBA 库存不是事件流,而是不断变化的状态,无法仅凭订单记录完整重建:退货、调整和调拨都会影响库存,却不一定有对应订单。我们每天拉取两次库存快照,按时间点存储。TimescaleDB 的连续聚合功能可从这些快照计算滚动的每日和每周平均值,无需每次查询扫描完整快照历史。

结算表存储解析后的结算报告,每个明细项一行。结算数据模型是 SP-API 生态中较复杂的解析挑战之一:Amazon 在不同站点使用不同交易类型代码,费用明细的命名规则在各区域也不完全一致。我们维护映射表,将不同市场的费用类型名称统一到一致的内部分类体系。

产品目录表存储 ASIN、标题、品类和变体关系等产品目录快照,每周更新。它主要为其他表提供查询参照,不使用超表结构,因为这些数据没有明显的时序特征。

TimescaleDB 的实际使用

超表是 TimescaleDB 的核心功能。将标准 PostgreSQL 表转为超表,只需一条命令,指定时间列和分区间隔。之后,标准 SQL 查询无需修改;TimescaleDB 查询规划器会自动排除数据块,跳过查询时间范围之外的分区,使大型数据集上的限定时间范围查询也能快速执行。

连续聚合是我们广泛使用的第二项功能。连续聚合是一种物化视图,TimescaleDB 在新数据进入超表时对其增量更新。为支撑需求预测,我们按 ASIN 和站点维护每日销售量的连续聚合,并据此计算滚动 7、30 和 90 天的销售总量。聚合按规定时间表自动更新,查询读取预计算数据,而非完整超表,因此仪表盘分析能快到满足交互使用的需求。

CREATE MATERIALIZED VIEW daily_sales_by_asin
WITH (timescaledb.continuous) AS
SELECT
  time_bucket('1 day', purchase_date) AS day,
  asin,
  marketplace_id,
  SUM(quantity_ordered) AS units_sold,
  SUM(item_price_amount) AS revenue
FROM order_items
GROUP BY day, asin, marketplace_id;

该视图由 TimescaleDB 的后台策略自动刷新,我们将其配置为每小时运行一次。预测使用的 90 天滚动总量,通过对该物化视图执行窗口函数查询计算,而不是查询原始订单商品表。

Celery 管道

SP-API 报告管道由一组 Celery 任务组成,按 Celery Beat 管理的时间表运行。每种报告类型有独立调度:订单每小时拉取,库存每天两次,结算按与 Amazon 结算周期一致的两周周期拉取。

每种报告的管道都遵循相同结构:定时任务通过 SP-API 发起报告请求,将请求 ID 存入 Redis。轮询任务每隔几分钟通过 SP-API 检查状态,报告准备就绪后将下载任务入队。下载任务获取报告文档,将原始文件存入磁盘,再将处理任务入队。处理任务解析文件,并将结构化记录加载到 TimescaleDB。

去重在处理阶段完成,而非数据库层。SP-API 偶尔会在重叠的报告窗口中返回重复记录。我们对每条记录的标识字段计算哈希,并在插入前检查是否已存在,只要每种报告都按顺序处理,就能防止重复行随时间积累;若并发处理,还需要在数据库中设置唯一性约束。

错误处理遵循简单策略:临时 SP-API 错误,例如速率限制响应或服务暂时不可用,会触发采用指数退避的 Celery 重试。持续性错误,例如报告文档格式错误、意外的数据模式变化或解析失败,会连同完整上下文记录日志,并暂停该报告类型的处理,不影响其他管道。我们在每周运营检查中审阅错误日志;除非影响结算对账数据,否则不将其视为紧急事件。

需求预测的数据基础

数据仓库本身不直接实现需求预测,而是提供让预测成为可能的数据基础:以 ASIN、站点和日期为粒度的干净时序销售数据,以及足够长的历史,以识别季节规律并计算具有统计意义的趋势估计。

上述每日连续聚合为计算滚动 7、30 和 90 天需求信号提供数据。这些信号输入简单预测模型:稳定产品采用加权移动平均和线性趋势外推,已知有季节性的产品采用人工调整因子。模型以 FastAPI 端点形式运行,查询连续聚合,并返回点预测及置信区间。

数据基础到位后,更复杂的预测方法也变得可行。我们曾对季节周期明显的产品尝试 Holt-Winters 指数平滑,对广告支出、价格变化等变量能显著驱动需求的产品,尝试基于这些外部信号的简单回归。但实际上,对于多数产品,这些复杂方法相较于调校良好的加权移动平均,预测准确率提升有限;数据质量和历史长度比模型复杂性更重要。

由此实现的能力

值得具体说明数据仓库带来的能力,因为这代表运营能力的质变,而不只是报告速度的渐进改善。

跨站点库存规划变得可行。结合 ASIN、站点级销售速度数据和快照表中的当前 FBA 库存,我们可以计算每个站点中每个 SKU 的库存可供销售天数,并自动生成补货建议,而无需逐个站点人工审查。

结算对账变得可审计。订单与结算数据存于同一系统后,就能核验每个订单商品项是否按预期费率结算,识别费用差异、错误赔偿或结算错误。这些问题原本只有逐行人工对账的卖家才可能发现。该检查以每周仓库报告形式运行,无需人工处理。

ASIN 级广告分析也成为可能。通过共享标识符关联广告与订单数据,我们可以跟踪广告支出与自然销售随时间共同变化的情况。Amazon 广告控制台无法直接回答这一问题,因为它显示广告归因销售,却不显示同一时期的自然销售。关联后的数据体现的是相关性;要将自然排名的变化归因于广告,还需要排名数据和受控比较。


相关洞察

延伸阅读