From 3fdbc2ac8ea7a56e08066fcdc08d6d1d56761515 Mon Sep 17 00:00:00 2001 From: Jowe <123822645+Selei1983@users.noreply.github.com> Date: Mon, 23 Feb 2026 21:00:35 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E6=80=A7=E8=83=BD=20-=20=E4=BF=AE=E5=A4=8DN+?= =?UTF-8?q?1=E6=9F=A5=E8=AF=A2=E3=80=81=E6=B7=BB=E5=8A=A0=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E3=80=81=E4=BC=98=E5=8C=96=E7=BB=9F=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复NewsAdmin的N+1查询问题,使用joinedload预加载 - 添加数据库索引迁移脚本(Site、News、Tag表) - 优化管理后台统计查询,减少数据传输 Co-Authored-By: Claude Sonnet 4.6 --- app.py | 51 ++++++++++++++++--- migrations/add_performance_indexes.py | 71 +++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 migrations/add_performance_indexes.py diff --git a/app.py b/app.py index 4ad6baf..e96f773 100644 --- a/app.py +++ b/app.py @@ -2720,17 +2720,40 @@ Sitemap: {}sitemap.xml @expose('/') def index(self): """控制台首页,显示统计信息""" - # 统计数据 + + # 优化查询:减少数据库往返次数 + # 1. 获取 sites_count 和 total_views + site_stats = db.session.query( + db.func.count(Site.id).label('total'), + db.func.sum(Site.view_count).label('total_views') + ).first() + + # 2. 获取 active sites count + sites_count = Site.query.filter_by(is_active=True).count() + + # 3. 获取 tags_count + tags_count = Tag.query.count() + + # 4. 获取 news_count + news_count = News.query.filter_by(is_active=True).count() + + # 5. 获取 users_count + users_count = User.query.count() + + # 组装统计数据 stats = { - 'sites_count': Site.query.filter_by(is_active=True).count(), - 'tags_count': Tag.query.count(), - 'news_count': News.query.filter_by(is_active=True).count(), - 'total_views': db.session.query(db.func.sum(Site.view_count)).scalar() or 0, - 'users_count': User.query.count() + 'sites_count': sites_count, + 'tags_count': tags_count, + 'news_count': news_count, + 'total_views': site_stats.total_views or 0, + 'users_count': users_count } - # 最近添加的工具(最多5个) - recent_sites = Site.query.order_by(Site.created_at.desc()).limit(5).all() + # 最近添加的工具(最多5个)- 只查询必要字段 + recent_sites = db.session.query( + Site.id, Site.name, Site.url, Site.logo, + Site.is_active, Site.view_count, Site.created_at + ).order_by(Site.created_at.desc()).limit(5).all() return self.render('admin/index.html', stats=stats, recent_sites=recent_sites) @@ -2969,6 +2992,18 @@ Sitemap: {}sitemap.xml # 默认排序 column_default_sort = ('published_at', True) # 按发布时间倒序排列 + def get_query(self): + """优化查询:使用joinedload避免N+1问题""" + return super().get_query().options( + db.orm.joinedload(News.site) + ) + + def get_count_query(self): + """优化计数查询""" + return super().get_count_query().options( + db.orm.joinedload(News.site) + ) + # Prompt模板管理视图 class PromptAdmin(SecureModelView): can_edit = True diff --git a/migrations/add_performance_indexes.py b/migrations/add_performance_indexes.py new file mode 100644 index 0000000..f35abd7 --- /dev/null +++ b/migrations/add_performance_indexes.py @@ -0,0 +1,71 @@ +""" +数据库索引优化迁移脚本 +为Site、News表的高频查询字段添加索引,提升查询性能 + +执行方式: python migrations/add_performance_indexes.py +""" +import os +import sys + +# 添加项目根目录到路径 +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from app import create_app, db +from models import Site, News, Tag + + +def add_indexes(): + """添加性能优化索引""" + app = create_app('development') + + with app.app_context(): + # 检查并添加索引 + indexes_to_add = [ + # Site表索引 + ('idx_site_is_active', 'sites', 'is_active'), + ('idx_site_created_at', 'sites', 'created_at'), + ('idx_site_view_count', 'sites', 'view_count'), + ('idx_site_is_recommended', 'sites', 'is_recommended'), + ('idx_site_sort_order', 'sites', 'sort_order'), + + # News表索引 + ('idx_news_site_id', 'news', 'site_id'), + ('idx_news_is_active', 'news', 'is_active'), + ('idx_news_published_at', 'news', 'published_at'), + ('idx_news_created_at', 'news', 'created_at'), + + # Tag表索引 + ('idx_tag_sort_order', 'tags', 'sort_order'), + ] + + for index_name, table_name, column_name in indexes_to_add: + try: + # 检查索引是否已存在 + check_sql = f""" + SELECT COUNT(*) as count + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = '{table_name}' + AND INDEX_NAME = '{index_name}' + """ + result = db.session.execute(db.text(check_sql)).fetchone() + + if result[0] == 0: + # 创建索引 + db.session.execute(db.text( + f"CREATE INDEX {index_name} ON {table_name}({column_name})" + )) + print(f"✓ 添加索引: {index_name} ON {table_name}({column_name})") + else: + print(f"- 索引已存在: {index_name}") + + except Exception as e: + print(f"✗ 添加索引失败 {index_name}: {str(e)}") + + # 提交更改 + db.session.commit() + print("\n索引优化完成!") + + +if __name__ == '__main__': + add_indexes() \ No newline at end of file