当前位置: 首页 > article >正文

MySQL JOIN 优化详解

我刚工作的时候有次上线了个新功能结果有个 JOIN 查询慢得要命用户投诉电话被打爆。DBA 帮我一看执行计划发现驱动表选错了扫描了 2000 万行。从那以后我每次写 JOIN 查询都会用EXPLAIN看看执行计划避免线上事故。今天咱们就来彻底搞懂 MySQL 的 JOIN 优化看完这篇你就能写出高性能的 JOIN 查询了。JOIN 的两种算法MySQL 的 JOIN 有两种算法Nested Loop Join嵌套循环连接和Hash Join哈希连接MySQL 8.0。1. Nested Loop JoinNLJ默认NLJ 的思路很简单双层循环。for each row in t1 { -- 外层循环驱动表 for each row in t2 { -- 内层循环被驱动表 if (join_condition) { output row } } } **问题**如果 t1 有 1000 行t2 有 10000 行那内层循环要执行 1000 × 10000 1000 万次 **优化**给被驱动表的关联字段加索引这样内层循环不需要全表扫描变成 **Index Nested Loop JoinINLJ**。 ### 2. Hash JoinHJMySQL 8.0 Hash Join 的思路先给被驱动表建哈希表再遍历驱动表去匹配。– 第 1 步给 t2 建哈希表按关联字段hash_table build_hash_table(t2, join_key)– 第 2 步遍历 t1去哈希表匹配for each row in t1 {if (hash_table.contains(t1.join_key)) {output row}}优势不需要索引适合大表关联。劣势只能做等值关联不能做非等值关联、、BETWEEN。驱动表 vs 被驱动表JOIN 查询有两个表驱动表Driver Table和被驱动表Driven Table。驱动表外层循环全表扫描或者用索引被驱动表内层循环用索引匹配关键驱动表选错了性能会差 10 倍怎么选驱动表原则小表驱动大表驱动表的数据量少。-- 假设 users 表有 1000 行orders 表有 1000000 行SELECT*FROMusers uJOINorders oONu.ido.user_id;优化器会选择users 作为驱动表1000 行orders 作为被驱动表用user_id索引匹配。为什么因为外层循环的次数决定了内层循环要执行多少次。如果驱动表小内层循环执行次数就少。验证一下EXPLAINSELECT*FROMusers uJOINorders oONu.ido.user_id;输出---------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---------------------------------------------------------------------------------------------- | 1 | SIMPLE | u | ALL | PRIMARY | NULL | NULL | NULL | 1000 | | | 1 | SIMPLE | o | ref | idx_user_id | idx_user_id | 5 | db.u.id | 10 | | ----------------------------------------------------------------------------------------------解读第 1 行table uusers 是驱动表type ALL全表扫描因为要遍历所有行第 2 行table oorders 是被驱动表type ref用索引匹配ref db.u.id关联字段总扫描行数1000驱动表 1000 × 10被驱动表平均每次匹配 10 行 11000 行。如果驱动表选反了orders 是驱动表总扫描行数1000000驱动表 1000000 × 1被驱动表假设 users 每张表只有 1 行匹配 2000000 行。性能差距2000000 / 11000 ≈ 181 倍优化 JOIN 的实战步骤咱们来个实际案例假设要查所有用户的订单SQL 很慢。第 1 步看执行计划EXPLAINSELECT*FROMusers uJOINorders oONu.ido.user_idWHEREu.age20;输出-------------------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | -------------------------------------------------------------------------------------------------------- | 1 | SIMPLE | u | range | idx_age | idx_age | 5 | NULL | 500000 | Using where | | 1 | SIMPLE | o | ref | idx_user_id | idx_user_id | 5 | db.u.id | 10 | | --------------------------------------------------------------------------------------------------------问题驱动表uusers扫了 500000 行rows 500000被驱动表oorders平均每次匹配 10 行总匹配 500000 × 10 5000000 行总扫描行数500000 5000000 5500000 行第 2 步优化驱动表的选择问题WHERE u.age 20过滤后还有 500000 行驱动表太大了。方案如果orders表过滤后行数更少可以换个写法让优化器选择orders作为驱动表。-- 假设我们要查 2024 年下的订单 的用户信息EXPLAINSELECT*FROMusers uJOINorders oONu.ido.user_idWHEREo.created_at2024-01-01;输出---------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---------------------------------------------------------------------------------------------- | 1 | SIMPLE | o | range | idx_created | idx_created | 6 | NULL | 1000 | | | 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | db.o.user_id | 1 | | ----------------------------------------------------------------------------------------------优化效果驱动表变成了oorders只扫 1000 行被驱动表uusers平均每次匹配 1 行总匹配 1000 × 1 1000 行总扫描行数1000 1000 2000 行从 5500000 降到 20002750 倍提升第 3 步确保被驱动表的关联字段有索引如果被驱动表的关联字段没索引会导致Block Nested Loop JoinBNLJ性能极差。-- 假设 orders.user_id 没有索引EXPLAINSELECT*FROMusers uJOINorders oONu.ido.user_id;输出------------------------------------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ------------------------------------------------------------------------------------------------------------------- | 1 | SIMPLE | u | ALL | NULL | NULL | NULL | NULL | 1000 | | | 1 | SIMPLE | o | ALL | NULL | NULL | NULL | NULL | 1000000 | Using join buffer (Block Nested Loop) | -------------------------------------------------------------------------------------------------------------------问题Extra Using join buffer (Block Nested Loop)用了 BNLJ 算法BNLJ 会把驱动表的数据放到join buffer然后全表扫描被驱动表匹配如果被驱动表很大join buffer放不下会分多次全表扫描性能炸裂解决方案给被驱动表的关联字段加索引。CREATEINDEXidx_user_idONorders(user_id);常见 JOIN 类型及优化1. INNER JOIN内连接SELECT*FROMusers uINNERJOINorders oONu.ido.user_id;优化给被驱动表的关联字段加索引让小表驱动大表优化器会自动选但你可以用STRAIGHT_JOIN强制顺序-- 强制驱动表顺序u 是驱动表o 是被驱动表SELECT*FROMusers u STRAIGHT_JOIN orders oONu.ido.user_id;2. LEFT JOIN左连接SELECT*FROMusers uLEFTJOINorders oONu.ido.user_id;特点LEFT JOIN 的左表一定是驱动表因为要保留左表的所有行。优化给右表被驱动表的关联字段加索引如果右表太大考虑用子查询过滤后再 JOIN-- 优化前右表太大SELECT*FROMusers uLEFTJOINorders oONu.ido.user_id;-- 优化后先过滤右表SELECT*FROMusers uLEFTJOIN(SELECT*FROMordersWHEREcreated_at2024-01-01)oONu.ido.user_id;3. RIGHT JOIN右连接SELECT*FROMusers uRIGHTJOINorders oONu.ido.user_id;特点RIGHT JOIN 的右表一定是驱动表。建议尽量用 LEFT JOIN 代替 RIGHT JOIN更符合阅读习惯。4. 多表 JOIN2 张表SELECT*FROMt1JOINt2ONt1.idt2.t1_idJOINt3ONt2.idt3.t2_id;优化给所有被驱动表的关联字段加索引调整 JOIN 顺序让小表驱动大表如果优化器选错了驱动表用STRAIGHT_JOIN强制顺序SELECT*FROMt1 STRAIGHT_JOIN t2ONt1.idt2.t1_id STRAIGHT_JOIN t3ONt2.idt3.t2_id;实战建议1. 给被驱动表的关联字段加索引最重要这是最重要的建议。如果被驱动表的关联字段没索引会导致 BNLJ性能极差。-- 检查关联字段有没有索引EXPLAINSELECT*FROMusers uJOINorders oONu.ido.user_id;-- 如果被驱动表 type ALL说明没走索引2. 让小表驱动大表如果优化器选错了驱动表用STRAIGHT_JOIN强制顺序。-- 强制驱动表顺序SELECT*FROMsmall_table STRAIGHT_JOIN big_tableON...;3. 先过滤再 JOIN如果某张表可以先过滤WHERE 条件先过滤再 JOIN减少驱动表的数据量。-- 优化前先 JOIN 再过滤SELECT*FROMusers uJOINorders oONu.ido.user_idWHEREo.created_at2024-01-01;-- 优化后先过滤再 JOINSELECT*FROMusers uJOIN(SELECT*FROMordersWHEREcreated_at2024-01-01)oONu.ido.user_id;4. 避免 SELECT ***SELECT *** 会查所有字段增加网络传输和内存消耗而且无法用覆盖索引。建议只查需要的字段。-- 优化前SELECT*FROMusers uJOINorders oONu.ido.user_id;-- 优化后SELECTu.id,u.name,o.id,o.amountFROMusers uJOINorders oONu.ido.user_id;5. 用 EXPLAIN 检查执行计划每次写完 JOIN 查询都用EXPLAIN看看执行计划确保被驱动表的关联字段走了索引type ref或eq_ref驱动表的数据量尽量小rows尽量小Extra里没有Using join buffer (Block Nested Loop)总结MySQL 的 JOIN 有两种算法Nested Loop Join默认和 Hash JoinMySQL 8.0JOIN 查询有驱动表外层循环和被驱动表内层循环要让小表驱动大表优化 JOIN 的步骤看执行计划 → 确保被驱动表的关联字段有索引 → 让小表驱动大表 → 先过滤再 JOIN常见 JOIN 类型INNER JOIN、LEFT JOIN、RIGHT JOIN、多表 JOIN实战建议给被驱动表的关联字段加索引、让小表驱动大表、先过滤再 JOIN、避免 SELECT *、用 EXPLAIN 检查执行计划如果你能把 JOIN 的两种算法、驱动表的选择、优化步骤讲清楚面试官绝对觉得你是高级开发。实战代码都在我本地跑过你可以放心复制。如果有问题欢迎评论区交流

相关文章:

MySQL JOIN 优化详解

我刚工作的时候,有次上线了个新功能,结果有个 JOIN 查询慢得要命,用户投诉电话被打爆。DBA 帮我一看执行计划,发现驱动表选错了,扫描了 2000 万行。 从那以后,我每次写 JOIN 查询都会用 EXPLAIN 看看执行计…...

MySQL 分库分表实战

&#xfeff;# MySQL 分库分表实战数据量到了千万级&#xff0c;单表扛不住了&#xff0c;就要分库分表。这篇说说怎么做。## 什么时候需要分库分表&#xff1f; 单表数据量&#xff1a; - < 500万&#xff1a;不用分&#xff0c;加索引、优化 SQL - 500万~2000万&#xff1…...

BabelDOC:3步搞定学术论文PDF翻译,公式表格完美保留!

BabelDOC&#xff1a;3步搞定学术论文PDF翻译&#xff0c;公式表格完美保留&#xff01; 【免费下载链接】BabelDOC Yet Another Document Translator 项目地址: https://gitcode.com/GitHub_Trending/ba/BabelDOC 你是否正在为英文学术论文的阅读而烦恼&#xff1f;复杂…...

3步终极解决方案:快速修复Zotero-GPT插件“密钥未配置“错误,开启AI文献管理新时代

3步终极解决方案&#xff1a;快速修复Zotero-GPT插件"密钥未配置"错误&#xff0c;开启AI文献管理新时代 【免费下载链接】zotero-gpt GPT Meet Zotero. 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-gpt 还在为Zotero-GPT插件报错"your secretK…...

实时控制系统中VoU传输优化框架的设计与实践

1. 实时控制系统的网络传输挑战 在工业物联网和网络化控制系统中&#xff0c;传感器、控制器和执行器之间的实时数据传输质量直接影响整个系统的控制性能。传统控制系统通常假设通信链路是理想的——零延迟、无丢包且带宽无限。然而在实际无线多跳网络环境中&#xff0c;这种假…...

Windows Cleaner:4步高效解决C盘空间不足的开源终极方案

Windows Cleaner&#xff1a;4步高效解决C盘空间不足的开源终极方案 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服&#xff01; 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner Windows Cleaner是一款完全免费开源的Windows…...

大语言模型提示工程优化:精准解决机器翻译中的零代词恢复难题

1. 项目概述&#xff1a;当大语言模型遇上机器翻译的“隐形主语”在机器翻译的日常工程实践中&#xff0c;我们常常会遇到一个看似微小却影响深远的“幽灵”问题&#xff1a;零代词。尤其是在处理像中文到英文这类语言差异巨大的翻译任务时&#xff0c;这个问题尤为突出。中文讲…...

8051指令集手册获取与开发优化指南

1. 8051指令集手册获取指南作为一名从事嵌入式开发十余年的工程师&#xff0c;我深知指令集手册在单片机开发中的核心地位。对于8051架构开发者而言&#xff0c;准确理解每条指令的机器周期、标志位影响和寻址方式是写出高效代码的基础。本文将系统梳理获取权威8051指令集资源的…...

ONNX模型‘解剖’指南:用Netron和Python代码查看、编辑与调试模型结构

ONNX模型‘解剖’指南&#xff1a;用Netron和Python代码查看、编辑与调试模型结构当你面对一个推理结果异常的ONNX模型&#xff0c;或是需要对其进行定制化修改时&#xff0c;仅仅使用Netron进行可视化查看是远远不够的。本文将带你深入ONNX模型的内部结构&#xff0c;通过编程…...

从零到专业:Sunshine虚拟手柄配置的5个关键突破点

从零到专业&#xff1a;Sunshine虚拟手柄配置的5个关键突破点 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 你是否曾在深夜准备享受游戏时&#xff0c;发现手柄在Sunshine串流中…...

终极指南:如何用Sketch MeaXure插件实现高效设计标注

终极指南&#xff1a;如何用Sketch MeaXure插件实现高效设计标注 【免费下载链接】sketch-meaxure 项目地址: https://gitcode.com/gh_mirrors/sk/sketch-meaxure 在UI/UX设计工作流中&#xff0c;设计标注是连接设计与开发的关键桥梁。Sketch MeaXure作为一款基于Type…...

TCME:用大模型与受控环境解锁非结构化隐私计算新范式

1. 项目概述&#xff1a;当隐私计算遇见大模型&#xff0c;TCME如何破局&#xff1f;在数据驱动的时代&#xff0c;我们每天都在与不信任的第三方打交道。无论是企业间的联合数据分析、个人与平台的服务交互&#xff0c;还是跨机构的合规审计&#xff0c;一个核心矛盾始终存在&…...

PotPlayer字幕翻译插件:5分钟实现外语影视无障碍观看的终极免费方案

PotPlayer字幕翻译插件&#xff1a;5分钟实现外语影视无障碍观看的终极免费方案 【免费下载链接】PotPlayer_Subtitle_Translate_Baidu PotPlayer 字幕在线翻译插件 - 百度平台 项目地址: https://gitcode.com/gh_mirrors/po/PotPlayer_Subtitle_Translate_Baidu 还在为…...

Frida Hook Java层还原Android客户端签名算法

1. 这不是“调用API”&#xff0c;而是拆解签名生成的完整逻辑链 你有没有遇到过这种情况&#xff1a;App每次请求都带一个叫 api-sign 的字段&#xff0c;值像一串随机字符串&#xff0c;长度固定、格式规整&#xff0c;但无论你怎么翻网络请求日志、抓包重放、甚至改参数重…...

专业级AMD Ryzen调试工具SMUDebugTool:深度解析与实战应用指南

专业级AMD Ryzen调试工具SMUDebugTool&#xff1a;深度解析与实战应用指南 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: ht…...

CSS Flexbox高级技巧:构建灵活的响应式布局

CSS Flexbox高级技巧&#xff1a;构建灵活的响应式布局 引言 Flexbox是CSS3引入的一维布局模型&#xff0c;它提供了强大的灵活布局能力。本文将深入探讨Flexbox的高级技巧和最佳实践&#xff0c;帮助你构建更优雅的响应式布局。 一、Flexbox核心概念回顾 .container {display:…...

终极免费方案:5分钟解锁Windows多用户远程桌面完整指南

终极免费方案&#xff1a;5分钟解锁Windows多用户远程桌面完整指南 【免费下载链接】rdpwrap RDP Wrapper Library 项目地址: https://gitcode.com/gh_mirrors/rd/rdpwrap 还在为Windows家庭版限制远程桌面连接而烦恼吗&#xff1f;RDP Wrapper Library为您提供完美的解…...

Flutter Provider状态管理完全指南

Flutter Provider状态管理完全指南 引言 Provider是Flutter生态中最流行的状态管理方案之一&#xff0c;它基于InheritedWidget实现&#xff0c;提供了简单、高效的状态管理方式。本文将深入探讨Provider的核心概念、使用方法和最佳实践。 一、Provider基础 1.1 添加依赖 depen…...

Mermaid Live Editor:为什么每个开发者都需要这个实时图表编辑神器?

Mermaid Live Editor&#xff1a;为什么每个开发者都需要这个实时图表编辑神器&#xff1f; 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trendin…...

Wand-Enhancer终极指南:3步免费解锁WeMod Pro高级功能完整教程

Wand-Enhancer终极指南&#xff1a;3步免费解锁WeMod Pro高级功能完整教程 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/gh_mirrors/we/Wand-Enhancer 还在为每月支付WeMod Pro订阅…...

【Claude文档分析高阶战法】:3个被90%用户忽略的PDF/OCR/多语言混合解析技巧

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Claude文档分析高阶战法总览 Claude在处理长文本、结构化文档与跨段落语义推理方面展现出独特优势&#xff0c;但要释放其全部潜力&#xff0c;需超越基础提问&#xff0c;构建系统化的分析范式。本章聚…...

机器学习加速格点QCD计算:流采样、轮廓变形、控制变量与代理观测量的无偏优化

1. 项目概述&#xff1a;当格点模拟遇见机器学习在计算物理&#xff0c;特别是格点量子色动力学&#xff08;Lattice QCD&#xff09;这个领域里&#xff0c;我们这些常年和超级计算机打交道的人&#xff0c;最常挂在嘴边的一个词可能就是“算力瓶颈”。一次完整的非微扰计算&a…...

如何用BooruDatasetTagManager将AI图像标注效率提升500%:从零构建高质量训练数据集

如何用BooruDatasetTagManager将AI图像标注效率提升500%&#xff1a;从零构建高质量训练数据集 【免费下载链接】BooruDatasetTagManager 项目地址: https://gitcode.com/gh_mirrors/bo/BooruDatasetTagManager 你是否正在为AI绘画模型准备训练数据&#xff0c;却因手动…...

JetBrains IDE试用期重置终极指南:三步轻松恢复30天试用

JetBrains IDE试用期重置终极指南&#xff1a;三步轻松恢复30天试用 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter 你是否曾因JetBrains IDE试用期到期而苦恼&#xff1f;ide-eval-resetter正是解决这一痛点的终…...

机器学习增强恒电位分子动力学:原子尺度模拟锂枝晶生长机制

1. 项目概述&#xff1a;当机器学习“遇见”分子动力学&#xff0c;我们如何看清锂枝晶的生长&#xff1f;在锂金属电池的研究中&#xff0c;锂枝晶的生长问题就像一个挥之不去的幽灵&#xff0c;它直接关系到电池的安全性和循环寿命。我们总在说“枝晶刺穿隔膜导致短路”&…...

【紧急预警】2024Q3起医保DRG/DIP结算将强制接入AI行为审计日志!医疗机构AI Agent日志治理4级合规改造倒计时

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;AI Agent医疗行业应用 AI Agent正以前所未有的深度融入医疗健康全链条&#xff0c;从辅助诊断、个性化治疗规划到慢病管理与药物研发&#xff0c;展现出强推理、多工具协同与持续学习的核心能力。不同于传统静…...

Java SE与Spring Boot在电商场景中的面试问题

Java SE和Spring Boot的微服务架构在电商场景中的应用面试官&#xff08;严肃&#xff09;&#xff1a;面试开始&#xff0c;我们先从基础开始说起&#xff0c;你能简单讲讲Java SE的几个主要特性吗&#xff1f; 燕双非&#xff08;搞笑&#xff09;&#xff1a;当然可以&#…...

Sunshine虚拟手柄终极指南:解决游戏串流控制难题

Sunshine虚拟手柄终极指南&#xff1a;解决游戏串流控制难题 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine 在游戏串流体验中&#xff0c;最令人沮丧的莫过于手柄连接失败、按键映…...

5大原神游戏痛点与BetterGI的智能解决方案

5大原神游戏痛点与BetterGI的智能解决方案 【免费下载链接】better-genshin-impact &#x1f4e6;BetterGI 更好的原神 - 自动拾取 | 自动剧情 | 全自动钓鱼(AI) | 全自动七圣召唤 | 自动伐木 | 自动刷本 | 自动采集/挖矿/锄地 | 一条龙 | 全连音游 | 自动烹饪 - UI Automatio…...

NCM转MP3完整指南:3步解锁网易云音乐加密文件

NCM转MP3完整指南&#xff1a;3步解锁网易云音乐加密文件 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾在网易云音乐下载了喜爱的歌曲&#xff0c;却发现只能在官方客户端播放&#xff1f;NCM加密格式限制了音乐的自由使用…...