小表驱动大表更快吗,不是

news2025/5/31 21:29:03

背景

head头表(5000),line行表(15万),导出数据包含头和行,一对多。

以行表为维度导出15万数据。

 sql

如下两个sql查询,有如下差异

驱动方式:第一个大表驱动小表,第二个反之

第一个自己比较:根据ID排序,和不排序,explain分析

explain
        select
        head.code,
        head.company_code,
        head.company_name,
        head.expense_date,
        head.data_source_num,
        head.data_source,
        head.prepared_by,
        head.status,

        line.asset_number,
        line.tag_number,
        line.asset_name,
        line.asset_category_1,
        line.asset_category_2,
        line.retired_units,
        line.retired_date,
        line.retirement_type,
        line.retirement_cost,
        line.disposal_amount,
        line.comment
        from
         asset_fa_disposal_line line
        left join asset_fa_disposal head on line.parent_code = head.code
        order by line.id desc
        limit 0,1000
      
        
    无排序    
    SIMPLE	line		ALL					141466	100.0	
1	SIMPLE	head		eq_ref	uniq_code	uniq_code	194	fssc_backend_asset.line.parent_code	1	100.0	

    有排序
1	SIMPLE	line		index		PRIMARY	8		141466	100.0	Backward index scan
1	SIMPLE	head		eq_ref	uniq_code	uniq_code	194	fssc_backend_asset.line.parent_code	1	100.0	

--         asset_fa_disposal head
--         left join asset_fa_disposal_line line on head.code = line.parent_code
     --   order by line.id desc
        

explain
        select
        head.code,
        head.company_code,
        head.company_name,
        head.expense_date,
        head.data_source_num,
        head.data_source,
        head.prepared_by,
        head.status,

        line.asset_number,
        line.tag_number,
        line.asset_name,
        line.asset_category_1,
        line.asset_category_2,
        line.retired_units,
        line.retired_date,
        line.retirement_type,
        line.retirement_cost,
        line.disposal_amount,
        line.comment
        from
        asset_fa_disposal head
        left join asset_fa_disposal_line line on head.code = line.parent_code
        order by line.id desc
         limit 0,1000
        
        
    无排序     
    SIMPLE	head		ALL					4832	100.0	
1	SIMPLE	line		ref	disposal_line_parent_code	disposal_line_parent_code	194	fssc_backend_asset.head.code	31	100.0	

    有排序
1	SIMPLE	head		ALL					4832	100.0	Using temporary; Using filesort
1	SIMPLE	line		ref	disposal_line_parent_code	disposal_line_parent_code	194	fssc_backend_asset.head.code	31	100.0

问题

1,不是小表驱动大表吗,为什么这里大表驱动小表效率更高

2,针对第一个sql,line表驱动时,排序好像比不排序,效率更高,line表不排序type=all,排序反而是index为什么

3,数据库中总共有15万数据,需要导出,每次查询1000,分页查询,大表驱动小表和小表驱动大表,影响最终的分页结果吗,比如导出数量有影响吗

4,采用第一个sql,又想不遗漏head数据(head无line关联数据),需要保证正确导出分页,怎么使用union优化

回答

1

确实,在大多数JOIN优化场景中,我们通常推荐"小表驱动大表"的原则,但您这个案例却出现了相反的情况。让我详细解释为什么这里"大表驱动小表"反而更快:

为什么这个特例中大表驱动小表更快

  1. LEFT JOIN的特殊性

    • 您使用的是LEFT JOIN,不是INNER JOIN

    • LEFT JOIN的逻辑是:必须返回左表的所有行,无论右表是否有匹配

    • 第一个查询(大表驱动):

      sql

      复制

      下载

      FROM asset_fa_disposal_line(line) LEFT JOIN asset_fa_disposal(head)

      优化器知道必须返回line表所有141,466行,所以选择全表扫描line,然后对每行快速查找head

  2. 索引效率差异

    • head.code有uniq_code索引(唯一索引),查找速度极快(O(1)复杂度)

    • line.parent_code有普通索引,查找速度稍慢

    • 大表驱动时:141,466次唯一索引查找

    • 小表驱动时:4,832次普通索引查找,每次返回约31行(总共约150,000行)

  3. 数据分布特点

    • 每个head.code平均对应约31个line记录(141466/4832≈29.3)

    • 这种"一对多"关系使得从小表出发会导致更多中间结果

  4. 执行成本对比

    • 大表驱动:141,466次高效唯一索引查找

    • 小表驱动:4,832次范围查找,每次返回约31行,需要处理更多数据页

何时"大表驱动小表"更优

  1. LEFT JOIN且右表有极高效索引

  2. 右表的查找条件比左表的选择性更好

  3. 数据关系是一对多且"多"的一方数据量极大

  4. 查询需要返回左表大多数行时(如无WHERE条件)

验证方法

您可以尝试以下实验验证:

sql

复制

下载

-- 强制小表驱动大表
SELECT /*+ JOIN_ORDER(head, line) */ ... FROM head LEFT JOIN line ...

-- 强制大表驱动小表 
SELECT /*+ JOIN_ORDER(line, head) */ ... FROM line LEFT JOIN head ...

总结

"小表驱动大表"是通用原则,但在LEFT JOIN、特殊索引结构和特定数据分布下,可能会出现大表驱动更优的情况。优化器根据成本估算选择了更适合当前表结构和数据分布的执行计划。

2

您观察到了一个非常有意思的现象,这涉及到MySQL优化器的工作机制。让我详细解释为什么在第一种查询中(line表驱动),加上ORDER BY line.id DESC后执行效率反而更高:

核心原因:MySQL的"排序驱动优化"

当查询包含ORDER BY且使用LIMIT时,MySQL优化器会尝试"优先排序后连接"的策略:

  1. 无排序时(性能较低)

    • type=ALL表示全表扫描line表(141,466行)

    • 对每一行再通过索引查找head表

    • 最后才应用LIMIT 1000

  2. 有排序时(性能更高)

    • type=index且显示"Backward index scan"表示优化器选择:

      • 直接按id索引的逆序扫描(从最大id开始)

      • 只需扫描1000行满足LIMIT的数据

      • 然后对这1000行做关联查询

    • 避免了全表扫描

关键技术点

  1. Backward index scan(反向索引扫描)

    • InnoDB索引默认是升序排列的

    • 当使用ORDER BY id DESC时,MySQL 8.0+可以智能地从索引尾部开始反向扫描

    • 只需读取满足LIMIT的行数(1000行)即可停止

  2. LIMIT的优化作用

    sql

    复制

    下载

    ORDER BY line.id DESC LIMIT 1000
    • 告诉优化器"我只需要1000条排好序的记录"

    • 优化器会优先使用id索引直接定位到符合条件的记录

  3. 索引覆盖优势

    • 虽然您的查询选择了很多列,但排序只依赖line.id

    • id是主键,索引结构本身就包含全部排序信息

执行流程对比

无排序流程:

复制

下载

1. 全表扫描line表(141,466行)
2. 对每行查找head表
3. 组合所有结果
4. 应用LIMIT 1000

有排序流程:

复制

下载

1. 从line表的id索引尾部开始反向扫描
2. 扫描1000行后停止(不读全表)
3. 对这1000行查找head表
4. 返回结果

为什么第二种方式(head驱动)没有这种优化

当head表驱动时:

  • 排序字段(line.id)不在驱动表

  • MySQL必须先完成全部连接操作

  • 生成包含所有字段的临时结果集(约150,000行)

  • 然后排序这个巨大临时表

  • 最后应用LIMIT

最佳实践建议

  1. 排序字段应尽量来自驱动表

  2. 确保排序字段有索引(如您案例中的line.id)

  3. 结合LIMIT使用ORDER BY可以触发这种优化

  4. 对于分页查询,这种模式性能最好

这种优化特别适合:

  • 大型事实表关联小型维度表

  • 需要最新/最旧记录的查询

  • 分页展示场景

您的案例完美展示了当表结构设计合理(line.id有主键索引)时,MySQL优化器能够发挥的强大优化能力。

3

分页查询驱动方式对导出结果的影响分析

在您的场景中(15万数据,每次查询1000条分页导出),驱动方式的选择确实会影响最终导出结果,但影响的性质取决于您的具体需求。

两种驱动方式对结果的影响

1. 大表驱动小表(line LEFT JOIN head)
  • 结果特点

    • 保证line表的所有记录都会出现在结果中

    • 如果某些line记录没有对应的head记录,head相关字段为NULL

    • 结果数量 = line表的记录数(15万)

2. 小表驱动大表(head LEFT JOIN line)
  • 结果特点

    • 保证head表的所有记录都会出现在结果中

    • 如果某些head记录没有对应的line记录,line相关字段为NULL

    • 结果数量 ≥ head表的记录数(因为有1对多关系)

关键差异点

  1. 结果集基数不同

    • 大表驱动:结果行数=line表行数(15万)

    • 小表驱动:结果行数≈head表行数×平均关联数(4832×31≈15万)

  2. NULL记录出现位置不同

    • 大表驱动:可能出现head字段为NULL的记录

    • 小表驱动:可能出现line字段为NULL的记录

  3. 分页边界问题

    • 两种方式的分页切分点可能不同,因为记录排序不同

    • 特别是当使用ORDER BY时,排序字段的位置会影响分页边界

导出数量是否受影响?

  • 如果只是简单导出所有关联数据:两种方式导出的总记录数应该相同(15万),因为您描述的是1对多关系且数据完整

  • 如果有NULL记录过滤:导出数量可能会有差异

  • 如果使用ORDER BY:分页边界不同可能导致某些记录在不同页中出现

实际建议

  1. 业务需求决定驱动方式

    • 如果需要确保不遗漏任何line记录 → 使用line驱动

    • 如果需要确保不遗漏任何head记录 → 使用head驱动

  2. 导出一致性保障

    sql

    复制

    下载

    -- 推荐使用游标方式而非分页导出,确保数据一致性
    DECLARE cur CURSOR FOR 
    SELECT ... FROM line LEFT JOIN head ... ORDER BY line.id;
    
    -- 或者使用基于ID的范围查询
    SELECT ... FROM line LEFT JOIN head 
    WHERE line.id > last_id ORDER BY line.id LIMIT 1000;
  3. 性能考虑

    • 如您之前发现的,line驱动+ORDER BY line.id性能更好

    • 对于大批量导出,建议使用这种高效方式

  4. 导出验证

    • 可以在导出完成后检查两种方式的记录数是否匹配预期

    • 特别检查边界条件记录是否都被包含

总结:驱动方式主要影响的是结果集的组成和顺序,在1对多关系且数据完整的情况下,总导出量应该相同,但具体哪些记录出现在哪些分页中可能会有差异。

4

SELECT * FROM (
    -- 有关联的记录
    SELECT 
        /* 字段列表 */,
        1 as record_type,
        line.id as sort_key
    FROM asset_fa_disposal head
    JOIN asset_fa_disposal_line line ON head.code = line.parent_code
    
    UNION ALL
    
    -- 无关联的记录
    SELECT 
        /* 字段列表 */,
        2 as record_type,
        head.code as sort_key
    FROM asset_fa_disposal head
    WHERE NOT EXISTS (
        SELECT 1 FROM asset_fa_disposal_line 
        WHERE parent_code = head.code
    )
) combined
ORDER BY record_type, sort_key DESC
LIMIT 1000 OFFSET 0;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2391937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20250529-C#知识:运算符重载

C#知识:运算符重载 运算符重载能够让我们像值类型数据那样使用运算符对类或结构体进行运算,并且能够自定义运算逻辑。 1、运算符重载及完整代码示例 作用是让自定义的类或者结构体能够使用运算符运算符重载一定是public static的可以把运算符看成一个函…

如何在WordPress网站中添加相册/画廊

在 WordPress 网站上添加相册可以让您展示许多照片。无论您是在寻找标准的网格相册画廊还是独特的瀑布流相册画廊体验,学习如何在 WordPress 网站上添加相册总是一个好主意。在本教程中,我们将介绍两种为 WordPress 网站添加相册的方法:使用区…

Codeforces Round 1025 (Div. 2)

Problem - A - Codeforces 查有没有人说谎&#xff0c;有一个必错的情况&#xff1a; 两个人都说输了&#xff0c;必有人撒谎&#xff0c;还有就是所有人都赢了&#xff0c;也是撒谎 来看代码&#xff1a; #include <iostream> #include <vector> using namespa…

Ubuntu20.04操作系统ssh开启oot账户登录

文章目录 1 前提2 设置root密码3 允许ssh登录root账户3.1 编辑配置文件3.2 重启ssh服务 4 安全注意事项 1 前提 ssh可以使用普通用户正常登录。 2 设置root密码 打开终端&#xff0c;设置密码 sudo passwd root # 设置root密码3 允许ssh登录root账户 3.1 编辑配置文件 su…

类欧几里得算法(floor_sum)

文章目录 普通floor_sum洛谷P5170 【模板】类欧几里得算法 万能欧几里得算法求 ∑ i 1 n A i B ⌊ a i b c ⌋ \sum_{i1}^{n}A^iB^{\lfloor \frac{aib}{c} \rfloor} ∑i1n​AiB⌊caib​⌋求 ∑ i 0 n ⌊ a i b c ⌋ \sum_{i0}^n \lfloor\frac{aib}{c}\rfloor ∑i0n​⌊caib…

每日Prompt:卵石拼画

提示词 世界卵石拼画大师杰作&#xff0c;极简风格&#xff0c;贾斯汀.贝特曼的风格&#xff0c;彩色的鹅卵石&#xff0c;斑马头像&#xff0c;鹅卵石拼画&#xff0c;马卡龙浅紫色背景&#xff0c;自然与艺术的结合&#xff0c;新兴的艺术创作形式&#xff0c;石头拼贴画&am…

硬件服务器基础

1、硬件服务器基础 2、服务器后面板 3、组件 3.1 CPU 3.2 内存 3.3 硬盘 3.4 风扇 4、服务器品牌 4.1 配置 4.2 CPU 架构 4.2.1 CPU 命名规则 4.2.2 服务器 CPU 和家用 CPU 的区别 4.2.3 CPU 在主板的位置 4.2.4 常见 CPU 安装方式 4.3 内存中组件 4.3.1 内存的分类 4.3.1.1 …

TRS收益互换平台开发实践:从需求分析到系统实现

一、TRS业务概述 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;允许投资者通过支付固定或浮动利息&#xff0c;换取标的资产&#xff08;如股票、指数&#xff09;的收益权。典型应用场景包括&#xff1a; ​​跨境投资​​&#xff…

测试Bug篇

本节概要&#xff1a; 软件测试的生命周期 bug的概念 buh要素 bug等级 bug生命周期 对于bug的定级与开发发生冲突如何解决 一、 软件测试的⽣命周期 软件测试贯穿于软件的整个生命周期&#xff0c;针对这句话我们⼀起来看⼀下软件测试是如何贯穿软件的整个生命周期。 软…

【Linux系统移植】Cortex-A8 Linux系统移植(超详细)

目录 前言 一、ARM开发板ARM简介RISC和CISCARM产品分布核心板S5pv210 SOC嵌入式系统开发方式 二、嵌入式系统组成为什么要系统移植内核移植框图 三、嵌入式开发环境搭建搭建开发环境总流程设置ubuntu与windows共享目录修改用户为root用户安装NFS服务器安装tftp服务器安装交叉编…

第十五届蓝桥杯大赛软件赛国赛Python 大学 C 组试做【本期题单: 设置密码、栈】

早上好啊大伙&#xff0c;这一期依旧是蓝桥杯备赛刷题的记录。 本期题单&#xff1a;设置密码、栈 前言 前段时间准备省赛&#xff0c;运气好进国赛了。所以就开始准备6月份的国赛。但是近期还有别的比赛要准备&#xff0c;所以刷题的速度比较慢&#xff0c;可能每一期就会有一…

报错SvelteKitError: Not found: /.well-known/appspecific/com.chrome.devtools.json

报错信息 SvelteKitError: Not found: /.well-known/appspecific/com.chrome.devtools.json 解决方案一 更新所有依赖 npm update解决方案二&#xff08;不一定成功&#xff09; src\lib\hooks.server.ts&#xff0c;每次请求服务器时执行 import type { Handle } from &…

word添加页眉

问题一&#xff1a; 为word文档添加页眉。 方法&#xff1a; 1、在要添加页眉的第一页页面顶端双击页眉区域&#xff0c;如果添加页眉页上面还有其他页或者与上一页添加页眉内容不同&#xff0c;记得取消“链接到前一节”&#xff08;点击使其上面没有灰色即可&#xff09;&…

2025推客系统小程序开发:独立部署源码交付,高性价比裂变增长引擎

在私域流量争夺白热化的今天&#xff0c;源码独立部署的推客系统小程序已成为企业构建自主分销体系、降低获客成本的核心利器。基于成熟案例与技术沉淀&#xff0c;我们提供安全可控、高性价比的一站式解决方案&#xff0c;助力企业快速搭建专属推客生态&#xff0c;实现长效增…

CentOS 7.0重置root密码

文章目录 版本&#xff1a;CentOS 7.0内核版本&#xff1a;CentOS Linux, with Linux 3.10.0-123.el7.x86_64 服务器重启后&#xff0c;等待进入上述页面&#xff0c;按⬆⬇键&#xff0c;中断正常启动。在此页面按E&#xff0c;进入编辑模式 继续按⬇&#xff0c;找到linux16…

YOLOV11改进策略【最新注意力机制】CVPR2025局部区域注意力机制LRSA-增强局部区域特征之间的交互

1.1网络结构 1.2 添加过程 1.2.1 核心代码 import torch import torch.nn as nn import torch.nn.functional as F from einops import rearrangedef patch_divide(x, step, ps):"""Crop image into patches.Args:x (Tensor): Input feature map of shape(b, …

DAY33 简单神经网络

你需要自行了解下MLP的概念。 你需要知道 梯度下降的思想激活函数的作用损失函数的作用优化器神经网络的概念 神经网络由于内部比较灵活&#xff0c;所以封装的比较浅&#xff0c;可以对模型做非常多的改进&#xff0c;而不像机器学习三行代码固定。 1. 神经网络的概念 (Th…

OBOO鸥柏丨2025年鸿蒙生态+国产操作系统触摸屏查询一体机核心股

在信创产业蓬勃发展的当下&#xff0c;OBOO鸥柏积极响应纯国产化号召&#xff0c;推出基于华为鸿蒙HarmonyOS操作系统的触摸屏查询一体机及室内外场景广告液晶显示屏一体机上市&#xff0c;OBOO鸥柏品牌旗下显示产品均采用国产芯片&#xff0c;接入终端控制端需支持安卓Windows…

【观成科技】Ymir勒索软件组织窃密木马RustyStealer加密通信分析

1.概述 Ymir勒索软件首次发现于2024年7月&#xff0c;采用ChaCha20加密算法对受害者文件进行加密&#xff0c;加密文件后缀为.6C5oy2dVr6。在攻击过程中&#xff0c;Ymir勒索组织利用名为RustyStealer的窃密木马获取受害企业的账号凭证&#xff0c;为后续横向移动和权限提升奠…

Vuer开源程序 是一个轻量级的可视化工具包,用于与动态 3D 和机器人数据进行交互。它支持 VR 和 AR,可以在移动设备上运行。

​一、软件介绍 文末提供程序和源码下载 Vuer开源程序 是一个轻量级的可视化工具包&#xff0c;用于与动态 3D 和机器人数据进行交互。它支持 VR 和 AR&#xff0c;可以在移动设备上运行。 二、Our features include: 我们的功能包括&#xff1a; light-weight and performa…