67 lines
2.4 KiB
Python
67 lines
2.4 KiB
Python
|
|
"""message 补 (channel, room_id, timestamp) 索引(群聊消息窗超时修复)
|
||
|
|
|
||
|
|
Revision ID: e6f7a8b9c0d1
|
||
|
|
Revises: d9e0f1a2b3c4
|
||
|
|
Create Date: 2026-09-07
|
||
|
|
|
||
|
|
内容
|
||
|
|
----
|
||
|
|
群聊消息窗(/api/messages?channel=&room_id=)**刻意不带 owner**——chat_group
|
||
|
|
按 room_id 去重后表的 owner 被最后写入的员工覆盖,与消息实际归属错位,带上
|
||
|
|
会查空(2026-08-22 实测)。于是 ix_message_channel_owner_room_time 的
|
||
|
|
owner 前缀用不上,优化器退化到 room 单列索引(ix_message_room)取全行再
|
||
|
|
filter channel + filesort:
|
||
|
|
|
||
|
|
- 16,290 条群: count 4.7s / 首页 5.1s / ASC 末页 10.7s(EXPLAIN ANALYZE
|
||
|
|
实测,Sort row IDs 全量 16k 行、128MB buffer pool 下逐行回表磁盘随机读);
|
||
|
|
- 真凶对话 53132846289@chatroom 40,863 条(count+首页+末页串行 ~40s),
|
||
|
|
前端 axios 15s 超时 → 聊天记录加载失败 ECONNABORTED(2026-09-07 用户报)。
|
||
|
|
|
||
|
|
本迁移补 (channel, room_id, timestamp) 三列索引:群查询与单聊同享
|
||
|
|
seek + 排序直达 + 覆盖 count。模型侧 models.py 同步双声明,防 alembic
|
||
|
|
autogenerate 反 DROP。窄列 BTREE,~94 万行在线建索引预计几十秒
|
||
|
|
(参照 c53f90b1e6a4 实测 86 万行 16s)。
|
||
|
|
|
||
|
|
配套:同日 /api/messages 加 order=desc + before_ts/before_id 游标参数,
|
||
|
|
聊天窗首屏「DESC 取最新一页 + 反转」、上翻游标,避开 ASC 深 OFFSET
|
||
|
|
逐行回表(单聊 1 万条末页实测 2.5s → DESC LIMIT 15ms)。
|
||
|
|
|
||
|
|
幂等:``sa.inspect`` 先查同名索引,存在即跳过。SQLite/MySQL 通用。
|
||
|
|
"""
|
||
|
|
from collections.abc import Sequence
|
||
|
|
|
||
|
|
import sqlalchemy as sa
|
||
|
|
from alembic import op
|
||
|
|
|
||
|
|
|
||
|
|
# revision identifiers, used by Alembic.
|
||
|
|
revision: str = 'e6f7a8b9c0d1'
|
||
|
|
down_revision: str | Sequence[str] | None = 'd9e0f1a2b3c4'
|
||
|
|
branch_labels: str | Sequence[str] | None = None
|
||
|
|
depends_on: str | Sequence[str] | None = None
|
||
|
|
|
||
|
|
_INDEX_NAME = 'ix_message_channel_room_time'
|
||
|
|
|
||
|
|
|
||
|
|
def _has_index(table: str, index: str) -> bool:
|
||
|
|
insp = sa.inspect(op.get_bind())
|
||
|
|
return index in {i['name'] for i in insp.get_indexes(table)}
|
||
|
|
|
||
|
|
|
||
|
|
def upgrade() -> None:
|
||
|
|
"""Upgrade schema."""
|
||
|
|
if _has_index('message', _INDEX_NAME):
|
||
|
|
return # 已建过(或先手工建过),幂等跳过
|
||
|
|
op.create_index(
|
||
|
|
_INDEX_NAME,
|
||
|
|
'message',
|
||
|
|
['channel', 'room_id', 'timestamp'],
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def downgrade() -> None:
|
||
|
|
"""Downgrade schema."""
|
||
|
|
if not _has_index('message', _INDEX_NAME):
|
||
|
|
return
|
||
|
|
op.drop_index(_INDEX_NAME, table_name='message')
|