SQL 查询优化的常用模式:避免 N+1、合理分页与去重
📅 2026/7/19 17:12:42
👁️ 次浏览
SQL 查询优化的常用模式避免 N1、合理分页与去重一、识别 N1 查询问题N1 查询是 ORM 使用中最常见、影响最大的性能问题。场景:你需要查询 10 个用户及其各自的订单数量。代码可能是这样:// 第 1 个查询: 获取 10 个用户 const users await db.query(SELECT * FROM users LIMIT 10); // N 个查询对每个用户再查一次他们的订单 for (const user of users) { user.orders await db.query( SELECT * FROM orders WHERE user_id ?, [user.id] ); }总共执行了 11 次查询 (1 10)。如果有 100 个用户就是 101 次查询。如果每次查询耗时 5ms通常不会注意到但如果数据库部署在云端每次查询的网络往返 (RTT) 可能是 50ms101 次查询就是 5 秒——用户体验从「瞬间加载」变成了「明显卡顿」。这种性能瓶颈的核心在于查询结构的耗时分布。在 N1 查询模式下系统需要执行 1 次获取主记录的查询耗时约 5ms以及 N 次逐条获取关联记录的查询耗时约 N × 50ms导致总耗时随数据量线性增长5 50N ms。相比之下批量查询优化只需 1 次获取主记录5ms和 1 次批量获取所有关联记录耗时约 50ms总耗时固定为 55ms完全不受用户数量影响。解决方案:方案一JOIN 或子查询。用一条 SQL 完成所有查询:SELECT u.*, COUNT(o.id) as order_count FROM users u LEFT JOIN orders o ON u.id o.user_id GROUP BY u.id LIMIT 10。这样 1 次查询完成原来 1N 次查询的工作。方案二IN 批量查询。先查询所有用户的 ID 列表再用WHERE user_id IN (1,2,3,...,10)一次批量查询所有用户的订单在应用层按 user_id 分组。方案三ORM 的 eager loading。大多数 ORM 支持预加载关联数据。Prisma 中的include,Sequelize 中的include,ActiveRecord 中的includes——在查询主记录时就告诉 ORM「顺便把关联数据一起查了」。二、深分页的性能问题分页查询是独立产品中最常见的列表功能。用LIMIT 20 OFFSET 10000来获取第 10001 到 10020 条记录——这种「深分页」在某些数据库中是性能杀手,因为它需要扫描前 10000 条记录然后丢弃,只返回最后 20 条。解决方案:方案一:游标分页(Cursor-based Pagination)。用WHERE id last_id ORDER BY id LIMIT 20替代OFFSET。第一次查询返回前 20 条,记录最后一条的 ID,下次查询WHERE id 20 LIMIT 20。这种方式「只扫描需要的记录」,性能与页码无关。缺点:不能跳页(如直接从第 1 页跳到第 10 页)。方案二:延迟关联(Deferred Join)。先查 ID(索引扫描,快),再根据 ID 查完整数据。SELECT * FROM posts WHERE id IN (SELECT id FROM posts ORDER BY id LIMIT 20 OFFSET 10000)。内层查询只扫描索引,速度远快于全表扫描;外层查询用 IN 批量获取 20 条完整记录。方案三:对于小型数据量( 10 万条),OFFSET 分页完全够用。深分页优化只在数据量达到一定规模(百万级以上)时才需要。不要过早优化。三、重复数据的处理数据去重是独立产品中常见的数据质量需求。方案一:DISTINCT。最简单的去重方式:SELECT DISTINCT category FROM posts。DISTINCT 适合「对单列去重」或「取唯一组合」,但 DISTINCT 要求返回的所有字段的组合唯一。方案二:GROUP BY 聚合函数。与 DISTINCT 类似,但可以在去重的同时做聚合:SELECT category, COUNT(*) FROM posts GROUP BY category。方案三:ROW_NUMBER() 窗口函数。当需要「保留每组中的一条记录」时,使用ROW_NUMBER():为每个分组中的记录编号,取出每组编号为 1 的记录。WITH ranked AS ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY created_at DESC ) as rn FROM user_logs ) SELECT * FROM ranked WHERE rn 1; 这样能获取每个用户的最新一条日志,且一次查询完成,不需要循环遍历。 ## 四、查询计划分析与验证 优化 SQL 查询后,如何验证「优化确实有效」最重要的工具还是 EXPLAIN(或 EXPLAIN ANALYZE)。 优化前运行 EXPLAIN ANALYZE,记录扫描行数和实际执行时间。优化后再次运行,对比两个指标的变化。不要只凭「感觉」判断优化效果——用数据说话。具体验证流程如下 1. 发现慢查询后先通过 EXPLAIN ANALYZE 记录优化前的扫描行数和执行时间 2. 执行优化操作比如用 JOIN 替代 N1 查询、用游标替代 OFFSET 深分页等 3. 再次运行 EXPLAIN ANALYZE对比优化前后的扫描行数和执行时间 4. 若扫描行数显著减少比如从百万级降到几十行说明优化有效可上线若扫描行数无明显变化说明优化方向有误需继续分析调整。 ## 五、总结 SQL 查询优化的常用模式,核心是三个「避免」:**避免循环中的逐条查询(N1)→ 用 JOIN 或批量 IN 替代**;**避免扫描大量不需要的记录(OFFSET 深分页)→ 用游标或延迟关联**;**避免应用层处理重复数据(逐条过滤)→ 用窗口函数在数据库层完成**。 对于独立产品:在查询数据量不大( 10 万条)的阶段,用 ORM 的 eager loading(预加载)解决 N1 问题;数据增长到百万级后,逐步用游标分页和延迟关联优化;去重用窗口函数(数据库层)比应用层遍历高效得多。 SQL 优化的效果,用 EXPLAIN 前后的扫描行数变化来验证。扫描行数减少 90% 的优化,是值得做的;扫描行数几乎不变的优化,可能是方向上弄错了。
Intern-S2-Preview-397B API完全指南:从本地部署到官方服务调用 【免费下载链接】Intern-S2-Preview-397B 项目地址: https://ai.gitcode.com/InternLM/Intern-S2-Preview-397B
想要掌握Intern-S2-Preview-397B这一强大的3970亿参数多模态AI模型的API调用方…
📅 2026/7/19 17:12:42
Fargate CLI负载均衡器配置:HTTP/HTTPS和TCP负载均衡实战教程 【免费下载链接】fargatecli CLI for AWS Fargate 项目地址: https://gitcode.com/gh_mirrors/fa/fargatecli
Fargate CLI是一款专为AWS Fargate设计的命令行工具,提供了简单高效的负…
📅 2026/7/19 17:12:42
AMD Qwen3.5-9B CPU推理优化:未来发展方向与趋势分析 【免费下载链接】Qwen3.5-9B-w4a16-tao-symgroup-torchao-v0.17.0 项目地址: https://ai.gitcode.com/hf_mirrors/amd/Qwen3.5-9B-w4a16-tao-symgroup-torchao-v0.17.0
在人工智能模型部署领域ÿ…
📅 2026/7/19 17:12:42
内容:
前两天跟几个搞跨境的老哥喝茶,聊起那个叫 geo cross 的东西,有人说是黑科技,有人说是洪水猛兽。我听着直乐,这帮人就是喜欢把简单的事儿整复杂。其实吧,这玩意儿没那么玄乎,就是让数据“跨过山和大海”,但前提是别踩红线。咱们得先说清楚,geo cross 不是让你去偷…
📅 2026/7/20 2:40:16
本文关键词:geo creeps昨晚凌晨两点,我盯着手机屏幕,后背发凉。不是因为恐怖片,而是因为我发现一张半年前在公园随手拍的照片,竟然出现在了某个陌生社交账号的“附近的人”推荐里,而且定位精确到了我常去的那家咖啡馆门口。那种感觉,就像有人正透过你的窗户,拿着放大镜…
📅 2026/7/20 2:35:40
每次去球馆前,看着那一堆湿漉漉的球衣和满是汗味的护具,我就想砸东西。那种混合着廉价洗衣液和酸臭味的味道,真的让人窒息。以前我随便买个塑料袋或者那种几块钱的网兜,结果呢?拉链坏得快,衣服拿出来皱得像咸菜干,而且根本装不下我的球鞋。这种体验简直糟糕透顶。直到我…
📅 2026/7/20 2:33:32
你是不是也跟我一样,盯着购物车里的 geo craig90 v5 看了半个月,心里跟猫抓似的?别在那儿纠结了,看完这篇,你心里就有数了,这钱到底该不该花。说真的,刚拿到这玩意儿的时候,我第一反应是“就这?”包装简陋得像个快递盒,里面连个像样的说明书都没有,就塞了一张皱巴巴…
📅 2026/7/20 2:31:11
你是不是也遇到过这种糟心事?明明内容写得挺用心,关键词也堆了不少,结果搜索排名就是不上去。看着隔壁竞品蹭蹭涨流量,心里那个急啊,跟热锅上的蚂蚁似的。其实,很多时候不是你不够努力,而是你没找对路子。最近圈子里都在聊 geo craig90,刚开始我也觉得是啥高大上的黑科…
📅 2026/7/20 2:28:54
说实话,刚听到geo cosmos这词儿的时候,我也是一脸懵。那时候正赶上项目焦头烂额,老板非说要用什么新架构。我查了一堆资料,满屏都是高大上的术语。什么分布式、什么实时同步,听得我脑仁疼。我就想,这玩意儿到底能不能落地?真的,一开始我是拒绝的。直到上个月,我们团队…
📅 2026/7/20 2:25:57
7月18日智东西消息,在2026 WAIC期间,努比亚联合字节豆包打造的二代“豆包手机”努比亚NaviX Ultra首次亮相,相比一代有诸多升级。智能体手机理念中兴通讯终端事业部总裁、努比亚总裁倪飞表示,智能体手机要从人操作手机变为手机帮人…
📅 2026/7/20 0:00:43
努比亚NaviX Ultra:外观与功能双升级在2026 WAIC期间,首次亮相的努比亚NaviX Ultra吸引了众多目光。它是努比亚联合字节豆包打造的二代“豆包手机”,与一代努比亚M153相比,外观设计变化较大。其机身背部搭载横向排布的大尺寸影像模…
📅 2026/7/20 0:00:43
目录
方法1:使用Split和Convert.ToInt64
方法2:使用LINQ的Select和ToList
方法3:使用TryParse进行异常安全转换(推荐) 如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天…
📅 2026/7/20 0:00:43
1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…
📅 2026/7/20 1:03:02
1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…
📅 2026/7/20 1:03:02
更多请点击:
https://intelliparadigm.com
第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…
📅 2026/7/20 1:03:02
目录
第一步:选对模板,省心一半
第二步:打开扫码点餐功能
开启功能按钮
桌台管理与桌码生成
第三步:个性化设计,打造品牌感
调整点餐页面
设置点餐规则 你还在让顾客站着排队点餐吗?2025年ÿ…
📅 2026/7/19 7:02:11
在业务中快速构建一个能理解私有文档、准确回答专业问题的智能助手,是很多开发团队面临的共同挑战。传统方案往往需要从零开始搭建复杂的 RAG(检索增强生成)系统,涉及文档解析、向量化、检索、大模型调用等多个环节,整…
📅 2026/7/19 17:02:37
FAE放射组学分析工具:医学影像特征探索的完整解决方案 【免费下载链接】FAE FeAture Explorer 项目地址: https://gitcode.com/gh_mirrors/fae/FAE
你是否曾经面对海量医学影像数据感到无从下手?想要从CT、MRI等影像中提取有价值的定量特征&#…
📅 2026/7/19 5:02:03