qidao123.com技术社区-IT企服评测·应用市场

标题: N+1查询:数据库性能的隐形杀手与终极拯救指南 [打印本页]

作者: 科技颠覆者    时间: 2025-5-5 23:42
标题: N+1查询:数据库性能的隐形杀手与终极拯救指南
title: N+1查询:数据库性能的隐形杀手与终极拯救指南
date: 2025/05/06 00:16:30
updated: 2025/05/06 00:16:30
author: cmdragon
excerpt:
N+1查询题目是ORM中常见的性能陷阱,表现为在查询主对象时,对每个关联对象举行单独查询,导致查询次数过多。以博客系统为例,查询10位作者及其文章会产生11次查询。通过Tortoise-ORM的prefetch_related方法,可以将查询优化为2次,显著提升性能。优化后的实现方案包括利用SQL JOIN语句加载关联数据,并联合FastAPI举行实践。进阶优化本领包括多层预加载、选择性字段加载和分页查询联合。常见报错涉及模型注册、连接关闭和字段匹配题目,需针对性解决。
categories:
tags:
扫描二维码
关注大概微信搜一搜:编程智域 前端至全栈交流与成长
探索数千个预构建的 AI 应用,开启你的下一个伟大创意https://tools.cmdragon.cn/
第一章:明白N+1查询题目本质

1.1 什么是N+1查询题目?

N+1查询是ORM利用过程中常见的性能陷阱。假设我们有一个博客系统,当查询作者列表时,如果每个作者关联了多篇文章,常规查询会先获取N个作者(1次查询),然后为每个作者单独执行文章查询(N次查询),统共产生N+1次数据库查询。
示例场景:
1.2 题目复现与性能影响

利用Tortoise-ORM创建数据模型:
  1. # models.py
  2. from tortoise.models import Model
  3. from tortoise import fields
  4. class Author(Model):
  5.     id = fields.IntField(pk=True)
  6.     name = fields.CharField(max_length=50)
  7. class Article(Model):
  8.     id = fields.IntField(pk=True)
  9.     title = fields.CharField(max_length=100)
  10.     content = fields.TextField()
  11.     author = fields.ForeignKeyField('models.Author', related_name='articles')
复制代码
题目查询代码示例:
  1. async def get_authors_with_articles():
  2.     authors = await Author.all()
  3.     result = []
  4.     for author in authors:
  5.         articles = await author.articles.all()
  6.         result.append({
  7.             "author": author.name,
  8.             "articles": [a.title for a in articles]
  9.         })
  10.     return result
复制代码
利用EXPLAIN ANALYZE分析查询筹划:
  1. -- 主查询
  2. EXPLAIN
  3. ANALYZE
  4. SELECT "id", "name"
  5. FROM "author";
  6. -- 单个作者的文章查询
  7. EXPLAIN
  8. ANALYZE
  9. SELECT "id", "title", "content"
  10. FROM "article"
  11. WHERE "author_id" = 1;
复制代码
第二章:prefetch_related异步预加载实战

2.1 预加载机制原理

Tortoise-ORM的prefetch_related利用SQL JOIN语句在单个查询中加载关联数据。对于1:N关系,它通过以下步骤实现:
2.2 优化后的实现方案

完整FastAPI示例:
  1. # main.py
  2. from fastapi import FastAPI
  3. from tortoise.contrib.fastapi import register_tortoise
  4. from pydantic import BaseModel
  5. app = FastAPI()
  6. # Pydantic模型
  7. class ArticleOut(BaseModel):
  8.     title: str
  9. class AuthorOut(BaseModel):
  10.     id: int
  11.     name: str
  12.     articles: list[ArticleOut]
  13.     class Config:
  14.         orm_mode = True
  15. # 数据库配置
  16. DB_CONFIG = {
  17.     "connections": {"default": "postgres://user:pass@localhost/blogdb"},
  18.     "apps": {
  19.         "models": {
  20.             "models": ["models"],
  21.             "default_connection": "default",
  22.         }
  23.     }
  24. }
  25. # 路由端点
  26. @app.get("/authors", response_model=list[AuthorOut])
  27. async def get_authors():
  28.     authors = await Author.all().prefetch_related("articles")
  29.     return [
  30.         AuthorOut.from_orm(author)
  31.         for author in authors
  32.     ]
  33. # 初始化ORM
  34. register_tortoise(
  35.     app,
  36.     config=DB_CONFIG,
  37.     generate_schemas=True,
  38.     add_exception_handlers=True,
  39. )
复制代码
2.3 执行筹划对比分析

优化后的SQL查询示例:
  1. EXPLAIN
  2. ANALYZE
  3. SELECT a.id,
  4.        a.name,
  5.        ar.id,
  6.        ar.title,
  7.        ar.content
  8. FROM author a
  9.          LEFT JOIN article ar ON a.id = ar.author_id;
复制代码
性能对比指标:
指标优化前 (N=10)优化后查询次数112平均相应时间 (ms)32045网络往返次数112内存占用 (KB)850650第三章:进阶优化与最佳实践

3.1 多层预加载本领

处置惩罚多级关联关系:
  1. await Author.all().prefetch_related(
  2.     "articles__comments",  # 文章关联的评论
  3.     "profile"  # 作者个人资料
  4. )
复制代码
3.2 选择性字段加载

优化查询字段选择:
  1. await Author.all().prefetch_related(
  2.     articles=Article.all().only("title", "created_at")
  3. )
复制代码
3.3 分页与预加载联合

分页查询优化方案:
  1. from tortoise.functions import Count
  2. async def get_paginated_authors(page: int, size: int):
  3.     return await Author.all().prefetch_related("articles")
  4.         .offset((page - 1) * size).limit(size)
  5.         .annotate(articles_count=Count('articles'))
复制代码
课后Quiz

常见报错解决方案

报错1:TortoiseORMError: Relation does not exist
报错2:OperationalError: connection closed
报错3:ValidationError: field required (type=value_error.missing)
环境配置与运行

安装依靠:
  1. pip install fastapi uvicorn tortoise-orm[asyncpg] pydantic
复制代码
启动服务:
  1. uvicorn main:app --reload --port 8000
复制代码
测试端点:
  1. curl http://localhost:8000/authors
复制代码
余下文章内容请点击跳转至 个人博客页面 大概 扫码关注大概微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:N+1查询:数据库性能的隐形杀手与终极拯救指南 | cmdragon's Blog
往期文章归档:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4