28a86c3b2c
GET /api/v1/compare/records 新增 ordered / keyword 两个查询参数,过滤全部下推到 SQL。 不能分页之后再由客户端 filter —— 一页里可能一条都不命中,列表看着就是空的, 得翻很多页才蹦出一条。 顺带修掉这条链路上几处随数据量线性变慢的地方: - 列表查询 defer raw_payload / llm_calls / llm_price_snapshot 三个重型 JSON 列。 出参 ComparisonRecordOut 根本不读,却是每页几百 KB~几 MB 的白读 + 白反序列化, 是「比价记录/全部记录」页慢的主要来源;详情接口不 defer,raw_payload 照常返回。 - 「已下单」标记改为只按本页店名(≤ limit 条)反查 savings,不再把该用户全部下单 店名捞进内存跟 50 条记录取交集。 - 新增 (user_id, created_at, id) 复合索引:反向扫恰好等于列表的 ORDER BY created_at DESC, id DESC,PG 免排序直接取前 n 条。 迁移走 CREATE INDEX CONCURRENTLY,不阻塞线上 harvest 写入。 - keyword 转义 LIKE 通配符后再匹配,避免搜一个「%」把整表拉回来。 - nginx 对 application/json 开 gzip:此前 gzip off + gzip_types 只含 text/html + gzip_proxied off 三个默认值凑一起,等于所有接口都在裸奔;记录列表这种 字段名和中文店名高度重复的 JSON 压缩比稳定 8~10 倍。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: 左辰勇 <exinglang@gmail.com> Reviewed-on: #156 Co-authored-by: zuochenyong <zuochenyong@wonderable.ai> Co-committed-by: zuochenyong <zuochenyong@wonderable.ai>
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
"""add composite index (user_id, created_at, id) on comparison_record
|
|
|
|
C 端「我的比价记录」列表(GET /api/v1/compare/records)是
|
|
`WHERE user_id=? ORDER BY created_at DESC, id DESC LIMIT n` —— 原来只有单列 user_id 索引,
|
|
过滤完还要把该用户的**全部**记录取出来排序才能拿前 n 条,重度用户随记录数线性变慢。
|
|
|
|
本复合索引的反向扫恰好等于 (created_at DESC, id DESC),规划器直接取前 n 条、免排序。
|
|
列序 (user_id, created_at, id) 与查询一一对应,不要调整。
|
|
|
|
Revision ID: comparison_user_created_idx
|
|
Revises: merge_active_phone
|
|
Create Date: 2026-07-21
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from alembic import op
|
|
|
|
revision = "comparison_user_created_idx"
|
|
down_revision = "merge_active_phone"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
INDEX_NAME = "ix_comparison_user_created"
|
|
COLUMNS = ["user_id", "created_at", "id"]
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
if bind.dialect.name == "postgresql":
|
|
# 线上 comparison_record 已有数据量,普通 CREATE INDEX 持表写锁会阻塞比价 harvest 写入;
|
|
# 用 CONCURRENTLY 不锁表(须脱离事务,autocommit_block 切到自动提交)。
|
|
# 同 comparison_status_created_idx 的做法。
|
|
with op.get_context().autocommit_block():
|
|
op.create_index(
|
|
INDEX_NAME, "comparison_record", COLUMNS,
|
|
unique=False, postgresql_concurrently=True,
|
|
)
|
|
else:
|
|
op.create_index(INDEX_NAME, "comparison_record", COLUMNS, unique=False)
|
|
|
|
|
|
def downgrade() -> None:
|
|
bind = op.get_bind()
|
|
if bind.dialect.name == "postgresql":
|
|
with op.get_context().autocommit_block():
|
|
op.drop_index(
|
|
INDEX_NAME, table_name="comparison_record",
|
|
postgresql_concurrently=True,
|
|
)
|
|
else:
|
|
op.drop_index(INDEX_NAME, table_name="comparison_record")
|