- 排行榜改为按天预计算快照(admin_leaderboard_daily_stats),请求只读取合并,重统计移至后台调度;过去日不可变、当天 TTL 刷新,避免在线聚合长时间占用数据库 - 为 runs/tool_call_metrics/llm_call_metrics/threads_meta 增加面向时间段+用户聚合的复合索引(model __table_args__ + migration 20260608_01) - 连接池可调:DatabaseConfig 新增 max_overflow / pool_timeout,接入 postgres/mysql 引擎;config.yaml 上调 pool_size=20/max_overflow=20 - 新增手动执行的索引脚本 scripts/sql/leaderboard_indexes_mysql.sql(幂等,适用于已存在数据、create_all 不补索引且 alembic 关闭的部署) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
4.4 KiB
SQL
86 lines
4.4 KiB
SQL
-- ============================================================================
|
|
-- 排行榜(admin leaderboard)统计所需的复合索引 —— MySQL 手动执行版
|
|
-- ============================================================================
|
|
--
|
|
-- 背景:
|
|
-- 这些索引声明在各 ORM model 的 __table_args__ 里,但 Base.metadata.create_all
|
|
-- 对【已存在的表】不会补建索引,而 alembic 自动迁移在本项目里是关闭的
|
|
-- (engine.py 中 `# await _run_alembic_upgrade(url)` 被注释)。
|
|
-- 因此在【已有数据的 MySQL 部署】上,这些索引很可能并未真正创建,排行榜的
|
|
-- 单日聚合查询会退化为全表扫描,拖慢并占用连接。
|
|
--
|
|
-- 用法:
|
|
-- 1) 先选中目标库(与 database.mysql_url 里的库名一致):
|
|
-- USE your_deerflow_db;
|
|
-- 2) 直接整文件执行即可。本脚本是【幂等】的:已存在的索引会跳过,不会报错,
|
|
-- 可重复执行。
|
|
--
|
|
-- 注意:
|
|
-- * 大表上建索引可能耗时;MySQL 8.0 在线 DDL 默认 ALGORITHM=INPLACE 一般不阻塞
|
|
-- 写入,但仍建议在低峰期执行,或先在备库/预发验证。
|
|
-- * 涉及列均为 String(64) / String(20) 等短列,utf8mb4 下复合索引长度远低于
|
|
-- 3072 字节上限,无需前缀索引。
|
|
-- * 索引名与 migration 20260608_01 保持一致,日后若启用 alembic 也不会重复创建。
|
|
-- ============================================================================
|
|
|
|
-- ---- 通用幂等封装:仅当索引不存在时才创建 --------------------------------------
|
|
DELIMITER //
|
|
DROP PROCEDURE IF EXISTS _create_index_if_missing //
|
|
CREATE PROCEDURE _create_index_if_missing(
|
|
IN p_table VARCHAR(64),
|
|
IN p_index VARCHAR(64),
|
|
IN p_columns VARCHAR(255)
|
|
)
|
|
BEGIN
|
|
DECLARE v_exists INT DEFAULT 0;
|
|
SELECT COUNT(*) INTO v_exists
|
|
FROM information_schema.statistics
|
|
WHERE table_schema = DATABASE()
|
|
AND table_name = p_table
|
|
AND index_name = p_index;
|
|
|
|
IF v_exists = 0 THEN
|
|
SET @ddl = CONCAT('CREATE INDEX `', p_index, '` ON `', p_table, '` (', p_columns, ')');
|
|
PREPARE stmt FROM @ddl;
|
|
EXECUTE stmt;
|
|
DEALLOCATE PREPARE stmt;
|
|
SELECT CONCAT('CREATED ', p_table, '.', p_index) AS result;
|
|
ELSE
|
|
SELECT CONCAT('SKIPPED ', p_table, '.', p_index, ' (already exists)') AS result;
|
|
END IF;
|
|
END //
|
|
DELIMITER ;
|
|
|
|
-- ---- runs ---------------------------------------------------------------------
|
|
CALL _create_index_if_missing('runs', 'ix_runs_created_user_status', '`created_at`, `user_id`, `status`');
|
|
CALL _create_index_if_missing('runs', 'ix_runs_user_created', '`user_id`, `created_at`');
|
|
CALL _create_index_if_missing('runs', 'ix_runs_thread_created', '`thread_id`, `created_at`');
|
|
|
|
-- ---- tool_call_metrics -------------------------------------------------------
|
|
CALL _create_index_if_missing('tool_call_metrics', 'ix_tool_call_metrics_created_user', '`created_at`, `user_id`');
|
|
CALL _create_index_if_missing('tool_call_metrics', 'ix_tool_call_metrics_run_created', '`run_id`, `created_at`');
|
|
|
|
-- ---- llm_call_metrics --------------------------------------------------------
|
|
CALL _create_index_if_missing('llm_call_metrics', 'ix_llm_call_metrics_created_user', '`created_at`, `user_id`');
|
|
CALL _create_index_if_missing('llm_call_metrics', 'ix_llm_call_metrics_run_created', '`run_id`, `created_at`');
|
|
|
|
-- ---- threads_meta ------------------------------------------------------------
|
|
CALL _create_index_if_missing('threads_meta', 'ix_threads_meta_created_user', '`created_at`, `user_id`');
|
|
CALL _create_index_if_missing('threads_meta', 'ix_threads_meta_user_created', '`user_id`, `created_at`');
|
|
|
|
-- ---- 清理临时存储过程 --------------------------------------------------------
|
|
DROP PROCEDURE IF EXISTS _create_index_if_missing;
|
|
|
|
-- ---- 执行后自检:确认 9 个索引都已存在 ---------------------------------------
|
|
SELECT table_name, index_name
|
|
FROM information_schema.statistics
|
|
WHERE table_schema = DATABASE()
|
|
AND index_name IN (
|
|
'ix_runs_created_user_status', 'ix_runs_user_created', 'ix_runs_thread_created',
|
|
'ix_tool_call_metrics_created_user', 'ix_tool_call_metrics_run_created',
|
|
'ix_llm_call_metrics_created_user', 'ix_llm_call_metrics_run_created',
|
|
'ix_threads_meta_created_user', 'ix_threads_meta_user_created'
|
|
)
|
|
GROUP BY table_name, index_name
|
|
ORDER BY table_name, index_name;
|