功能:新手引导视频 + 美团券首页分页索引

新手引导视频:运营后台上传 MP4(上限 100MB,魔数校验只认 ISO BMFF),
App 端在领券等候浮层前 N 次以引导视频替代广告。新增 guide_video
的 model/schema/repository/router(App 侧 + 后台侧)与播放记录表迁移。

美团券:首页「销量最高 / 智能推荐」两个 tab 改游标分页,配套两条
(city_id, dedup_key, 排序键 DESC) 复合索引,让 Postgres 顺着索引流式
去重,免掉每翻一页重排整城券的开销。美团 CPS client 在 lifespan 预热
并在关闭时释放连接池。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
左辰勇
2026-07-23 22:50:44 +08:00
parent b7cfcf7495
commit a2270ee1b2
22 changed files with 1456 additions and 83 deletions
+5 -1
View File
@@ -42,8 +42,12 @@
## 索引与约束
- 唯一约束 `uq_meituan_coupon_source_sign`(`source`, `product_view_sign`)—— ETL upsert 的冲突目标。
- 单列 index:`source``city_id``brand_name``sale_volume_num``commission_percent``dedup_key``last_seen`
- 复合 index(`meituan_coupon_feed_indexes` 迁移,2026-07-23 加):
- `ix_meituan_coupon_city_dedup_sales`(`city_id`, `dedup_key`, `sale_volume_num DESC`, `commission_percent DESC`)——「销量最高」。
- `ix_meituan_coupon_city_dedup_comm`(`city_id`, `dedup_key`, `commission_percent DESC`)——「智能推荐」。
- 列顺序对齐 `WHERE city_id=? … ORDER BY dedup_key, <排序键> DESC`,让 `DISTINCT ON` 顺着索引流式去重、免掉排序。
- **字段核对(2026-06-10)**:模型 ↔ 迁移(`meituan_coupon_table`)↔ ETL 解析三处一致;解析截断宽度均 ≤ 列宽(name256 / brand128 / poi128 / head512 / sign128,`dedup_key` = md5 32 字符 ≤ 64)。
- **索引评估**:`rec` / `sales` 查询的理想索引是复合 `(dedup_key, sale_volume_num DESC)``(dedup_key, commission_percent DESC)`(匹配 `DISTINCT ON`);但当前**单城几千行,seq scan 即亚毫秒**,现有单列索引已够用,复合索引留作多城 / 放量时再加,避免过早优化
- **索引评估修订(2026-07-23)**:此前判断「单城几千行 seq scan 即亚毫秒,复合索引留作放量时再加」——**漏算了 `raw`**。原查询用 `select(MeituanCoupon)` 做子查询,`raw`(JSONB,每行数 KB)被一起拖进 `DISTINCT ON` 与分页两轮排序,整城体量轻松推过 `work_mem` → 落盘外部归并,且**每翻一页重来一次**,是首页「滑到底越来越慢」的主因之一。现已双管齐下:接口侧改成「先在小列上排出本页 id,再按 id 回表取 `raw`」(`api/v1/meituan.py``_paged_dedup_ids`),库侧补上上面两条复合索引
## 过期清理策略
- ETL 每轮(每小时)末尾按 **`last_seen` 的 TTL** 清理:`DELETE WHERE last_seen < now() - prune_hours`(默认 24h,`--prune-hours` 可调,0=不清)。