diff --git a/yunke-server/app/db/models.py b/yunke-server/app/db/models.py index 0cd3d01..72f1608 100644 --- a/yunke-server/app/db/models.py +++ b/yunke-server/app/db/models.py @@ -537,6 +537,18 @@ class Message(Base): "ix_message_fetched_at", "fetched_at", ), + # 该回未回决赛圈点查(owner+talker 定位末条消息)在 channel=None + # (全平台计数——管理驾驶舱 action_counts 复用)时无渠道前缀条件, + # 上面全部以 channel 打头的索引用不上,逐候选退化扫表+filesort: + # 2026-09-10 服务器实测 dashboard 34~37s 撞前端 15s 超时 + # (ECONNABORTED);补无渠道前缀索引后 1.3~1.8s(100 万行在线建 + # 索引 49s)。「模型+迁移双声明」防 alembic autogenerate 反 DROP。 + Index( + "ix_message_owner_talker_time", + "owner_account_id", + "talker_id", + "timestamp", + ), ) diff --git a/yunke-server/migrations/versions/d8e9f0a1b2c3_message_owner_talker_time_index.py b/yunke-server/migrations/versions/d8e9f0a1b2c3_message_owner_talker_time_index.py new file mode 100644 index 0000000..9aabaee --- /dev/null +++ b/yunke-server/migrations/versions/d8e9f0a1b2c3_message_owner_talker_time_index.py @@ -0,0 +1,41 @@ +"""message 补无渠道前缀索引 (owner_account_id, talker_id, timestamp) + +Revision ID: d8e9f0a1b2c3 +Revises: c9d0e1f2a3b4 +Create Date: 2026-09-10 + +背景:该回未回决赛圈点查在 channel=None(管理驾驶舱 action_counts)时 +无渠道前缀条件,现有索引全部以 channel 打头用不上——服务器 100 万行 +实测 dashboard 34~37s 撞前端 15s 超时;索引落地后 1.3~1.8s。 +幂等:sa.inspect 先查,存在即跳过(服务器已先行手工建同名索引,本迁移 +兜底对齐 schema 版本)。「模型+迁移双声明」防 autogenerate 反 DROP。 +""" +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +revision: str = "d8e9f0a1b2c3" +down_revision: str | Sequence[str] | None = "c9d0e1f2a3b4" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + +_IDX = "ix_message_owner_talker_time" + + +def _has_index(bind) -> bool: + insp = sa.inspect(bind) + if not insp.has_table("message"): + return False + return _IDX in {ix["name"] for ix in insp.get_indexes("message")} + + +def upgrade() -> None: + if not _has_index(op.get_bind()): + op.create_index(_IDX, "message", + ["owner_account_id", "talker_id", "timestamp"]) + + +def downgrade() -> None: + if _has_index(op.get_bind()): + op.drop_index(_IDX, table_name="message")