perf(analytics): 5表 updated_at 窄索引——health MAX 消除全表扫(128MB buffer pool)
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
"""5 表 updated_at 窄索引——/api/analytics/health MAX 消除全表扫
|
||||
|
||||
Revision ID: c3d4e5f6a7b8
|
||||
Revises: f0a1b2c3d4e5
|
||||
Create Date: 2026-09-04
|
||||
|
||||
health 端点每页面加载对 6 表各 MAX 一次;5 表 updated_at 无索引时
|
||||
MySQL 全表扫(media 5万宽行在 128MB buffer pool 下最伤)。单列窄索引
|
||||
各几 MB,MAX 变 O(1) 索引定位。幂等:_has_index 先查。SQLite/MySQL 通用。
|
||||
模型侧 models.py 同步双声明,防 alembic autogenerate 反 DROP。
|
||||
"""
|
||||
from collections.abc import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision: str = "c3d4e5f6a7b8"
|
||||
down_revision: str | Sequence[str] | None = "f0a1b2c3d4e5"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
# (表名, 索引名) —— 与 models.py 各模型 __table_args__ 一字对应
|
||||
_INDEXES: tuple[tuple[str, str], ...] = (
|
||||
("customer", "ix_customer_updated_at"),
|
||||
("chat_group", "ix_chat_group_updated_at"),
|
||||
("phone_call", "ix_phone_call_updated_at"),
|
||||
("media", "ix_media_updated_at"),
|
||||
("moments_post", "ix_moments_post_updated_at"),
|
||||
)
|
||||
|
||||
|
||||
def _has_index(bind, table: str, index: str) -> bool:
|
||||
insp = sa.inspect(bind)
|
||||
if not insp.has_table(table):
|
||||
return False
|
||||
return index in {ix["name"] for ix in insp.get_indexes(table)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
for table, index in _INDEXES:
|
||||
if not _has_index(bind, table, index):
|
||||
op.create_index(index, table, ["updated_at"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
bind = op.get_bind()
|
||||
for table, index in _INDEXES:
|
||||
if _has_index(bind, table, index):
|
||||
op.drop_index(index, table_name=table)
|
||||
Reference in New Issue
Block a user