SQL如何在数据库中执行

news2024/11/27 8:34:41

数据库的服务端,可分为执行器(Execution Engine)存储引擎(Storage Engine) 两部分:

  • 执行器负责解析SQL执行查询
  • 存储引擎负责保存数据

1 SQL如何在执行器中执行

# 查询用户ID大于50的用户的所有订单
SELECT u.id AS user_id, u.name AS user_name, o.id AS order_id
FROM users u INNER JOIN orders o ON u.id = o.user_id
WHERE u.id > 50

很简单的一个联查。DB收到查询请求后,先解析SQL语句,把这一串文本解析成便于程序处理的结构化数据,这是通用的语法解析过程。跟编程语言的编译器编译时,解析源代码过程一样。

转换后的结构化数据,就是抽象语法树(AST,Abstract Syntax Tree)。上面这SQL的AST:

img

执行器解析AST后,生成一个逻辑执行计划,即如何一步步执行查询和计算,最终得到执行结果的一个分步骤的计划。这个逻辑执行计划:

LogicalProject(user_id=[$0], user_name=[$1], order_id=[$5])
    LogicalFilter(condition=[$0 > 50])
        LogicalJoin(condition=[$0 == $6], joinType=[inner])
            LogicalTableScan(table=[users])
            LogicalTableScan(table=[orders])

和SQL、AST不同,这个逻辑执行计划已经很像可以执行的程序代码了。这执行计划像编程语言的函数调用栈,外层方法调用内层方法。所以,得从内往外看:

  1. 最内层的2个LogicalTableScan:把USERS和ORDERS这两个表的数据都读出来
  2. 拿这两个表所有数据做一个LogicalJoin,JOIN条件:第0列(u.id)=第6列(o.user_id)
  3. 再执行一个LogicalFilter过滤器,过滤条件:第0列(u.id)>50
  4. 做个LogicalProject投影,只保留第0(user_id)、1(user_name)、5(order_id)三列。“投影(Project)”:把不需要的列过滤

把这个逻辑执行计划翻译成代码,然后按照顺序执行,就正确查询出数据。但按执行计划,要执行2个全表扫描,再把2个表的所有数据做一个JOIN操作,性能差。

如user表1,000条数据,订单表10,000条数据,JOIN要遍历行数1,000 x 10,000 = 10,000,000行

这种从SQL的AST直译过来的逻辑执行计划,一般性能差,所以,要对执行计划优化。不同DB不同优化方法,优化总体思路:在执行计划中,尽早减少须处理的数据量。即尽量在执行计划最内层减少要处理的数据量。

简单优化后的逻辑执行计划:

LogicalProject(user_id=[$0], user_name=[$1], order_id=[$5])
    LogicalJoin(condition=[$0 == $6], joinType=[inner])
        LogicalProject(id=[$0], name=[$1])              // 尽早执行投影
            LogicalFilter(condition=[$0 > 50])          // 尽早执行过滤
                LogicalTableScan(table=[users])
        LogicalProject(id=[$0], user_id=[$1])           // 尽早执行投影
            LogicalTableScan(table=[orders])

对比原始逻辑执行计划,两点简单优化:

  1. 尽早执行投影,去除不需要的列
  2. 尽早执行数据过滤,去除不需要的行

JOIN前,把要JOIN的数据尽量减少,显然比原始执行计划快。

到这,执行器只在逻辑层分析SQL,优化查询执行逻辑,执行计划中操作的数据,仍是表、行和列。在数据库中,表、行、列都是逻辑概念,所以,这个执行计划叫“逻辑执行计划”。

执行查询接下来的部分,涉及数据库的物理存储结构。

2 SQL是如何在存储引擎中执行

数据真正存储时,无论在磁盘or内存中,都没法直接存储这种带行列的二维表。数据库中的二维表存储就是存储引擎负责,存储引擎主要功能就是把逻辑的表行列,用合适物理存储结构保存到文件。

不同数据库,物理存储结构完全不一样,各种数据库之间巨大性能差距的根本原因。MySQL在设计层对存储引擎抽象,存储引擎可替换。默认InnoDB,InnoDB中数据表的物理存储结构是以主键为关键字的B+树,每行数据直接就保存在B+树的叶节点。如上面的订单表组织成B+树:

img

这树以订单表的主键orders.id为关键字组织,其中“62:[row data]”,表示的是订单号为62的一行订单数据。在InnoDB中,表的索引也是以B+树的方式来存储的,和存储数据的B+树的区别是,在索引树中,叶子节点保存的不是行数据,而是行的主键值。

若通过索引检索记录,需先后查询索引树、数据树两棵树:

  • 先在索引树检索到行记录的主键值
  • 再用主键值去数据树中去查找这行数据

优化后的逻辑执行计划将会被转换成物理执行计划,物理执行计划和数据的物理存储结构相关。InnoDB直接将逻辑执行计划转换为物理执行计划:

InnodbProject(user_id=[$0], user_name=[$1], order_id=[$5])
    InnodbJoin(condition=[$0 == $6], joinType=[inner])
        InnodbTreeNodesProject(id=[key], name=[data[1]])
            InnodbFilter(condition=[key > 50])
                InnodbTreeScanAll(tree=[users])
        InnodbTreeNodesProject(id=[key], user_id=[data[1]])
            InnodbTreeScanAll(tree=[orders])

物理执行计划同样可以根据数据的物理存储结构、是否存在索引以及数据多少等各种因素进行优化。这一块儿的优化规则同样是非常复杂的,如把对用户树的全树扫描再按照主键过滤这两个步骤,优化为对树的范围查找:

PhysicalProject(user_id=[$0], user_name=[$1], order_id=[$5])
    PhysicalJoin(condition=[$0 == $6], joinType=[inner])
        InnodbTreeNodesProject(id=[key], name=[data[1]])
            InnodbTreeRangeScan(tree=[users], range=[key > 50])  // 全树扫描再按照主键过滤,直接可以优化为对树的范围查找
        InnodbTreeNodesProject(id=[key], user_id=[data[1]])
            InnodbTreeScanAll(tree=[orders])

按优化后的物理执行计划,一步步执行查找和计算,就得到SQL查询结果。

知道InnoDB索引实现后,就很容易明白为何主键不能太长,因为表的每个索引保存的都是主键值,过长主键会导致每个索引都很大。

SELECT * FROM user WHERE left(department_code, 5) = '00028';
SELECT * FROM user WHERE department_code LIKE '00028%';

为什么一个不能命中索引,一个能命中?InnoDB对物理执行计划进行优化的时候,能识别LIKE这种过滤条件,转换为对索引树的范围查找。第一条SQL,优化规则就没那么“智能”。

它并没有识别出来,这条件同样可转换为对索引树的范围查找,而走全表扫描。并不是说第一个SQL写不好,而是数据库不智能。能做的就是了解数据库脾气,按它能力,尽量写出它能优化的SQL。

总结

一条SQL在数据库中执行,经过语法解析成AST,然后AST转换为逻辑执行计划,逻辑执行计划经优化后,转换为物理执行计划,再经物理执行计划优化后,按照优化后的物理执行计划执行完成数据的查询。数据库都由执行器存储引擎两部分组成:

  • 执行器负责执行计算
  • 存储引擎负责保存数据

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

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

相关文章

详解回调函数

目录 前言 一、 实现一个简单的计算器 1.1 - 代码一 1.2 - 代码二 二、qsort 函数的介绍 三、改进冒泡排序函数 前言 回调函数就是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其…

微信公众号调用扫一扫功能

手把手教你调用微信扫一扫,三分钟包会_前端人的博客-CSDN博客_调用微信扫一扫 第一次搞公众号,还以为跟上回调用企业微信扫一扫一样。。。调起扫一扫功能的过程自然是不同的,要注意的地方还挺多,记录一下 。 其实,在使…

2023最新网络安全自学路线,内容涵盖3-5年技能提升

前言 先预祝大家新年快乐! 【一一帮助网络安全入门和提升点这里一一】 01 什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究…

改进MBR(操作显卡来输出字符)

文章目录前言前置知识mbr.S代码实验操作前言 本博客记录《操作系统真象还原》第二章第一个实验操作~ 实验环境:ubuntu18.04VMware , Bochs下载安装 实验内容:在屏幕上打印字符串【采用的方式是直接修改显存实现】 实验原因:mb…

Flutter性能优化

原理 Flutter的架构主要分成三层:Framework,Engine和Embedder Framework 使用 dart 实现,包括 Material Design 风格的Widget,Cupertino(针对iOS)风格的Widgets,UI/文本/图片/按钮等基础 Widgets,渲染&…

设计模式学习(一):Bridge桥接模式

一、什么是Bridge模式Bridge模式的作用是在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。1.1 类的功能层次结构主要作用就是增加新的功能。当我们要增加新的功能时,我们可以从各个层次的类中找出最符合自己需求的类,然后以它为父类编写子类…

(Week 10)最小生成树(C++,prim,Kruskal,并查集)

文章目录Einstein学画画(C,欧拉路)题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示解题思路:并查集(C)[蓝桥杯 2017 国 C] 合根植物(C,并查集)题目描述输入格…

基于Java+SpringBoot+vue+element实现校园闲置物品交易网站

基于JavaSpringBootvueelement实现校园闲置物品交易网站 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录基于JavaSpri…

Element UI 走马灯的使用

目录 走马灯是什么 原生js实现 Element UI的走马灯使用 el-carousel Carousel Events el-carousel-item 走马灯是什么 在有限空间内,循环播放同一类型的图片、文字等内容,走马灯也叫轮播图。 比如 原生js实现 JS实现轮播图效果(同时…

mysql学习总结二

聚合函数 聚合函数表示对 值的集合 进行操作的 组(集合)函数。 # 华为手机价格的平均值 SELECT AVG(price) FROM products WHERE brand 华为; # 计算所有手机的平均分 SELECT AVG(score) FROM products; # 手机中最低和最高分数 SELECT MAX(score) FR…

SpringBoot解决全局和局部跨域问题的两种方式

前言 在如今前后端分离的开发模式下,跨域是一个非常经典的问题,解决的方式也有很多,比如代理服务器,使用JSONP 我之前也写过一篇解决跨域问题的文章,感兴趣的可以参考:解决Vue前后端跨域问题的多种方式 …

【现代机器人学】学习笔记九:运动规划

本节和前一节 【现代机器人学】学习笔记八:轨迹生成 不同,侧重于避障的内容。有一些我认为的重要的基本的概念:1.路径规划是一个纯几何问题,寻找一条无碰撞路径,不涉及动力学和时间相关内容。因此路径规划是运动规划的…

请查收 | 2022 阿里妈妈技术文章回顾

新年伊始,万象更新!转眼,阿里妈妈技术已陪伴大家走过601天~在此,感谢每位读者朋友的支持与关注回顾2022,我们分享了60篇原创技术文章、发布了1本营销科学系列白皮书、开源了1项向量召回技术方案;阿里妈妈营…

Netty基础入门——NIO【1】

Netty基础入门——NIO【1】 1 NIO 1.1 三大组件 1.1.1 Channel && Buffer Channle channel类似于stream,是读写数据的双向通道,而stream要么是输入要么是输出 #mermaid-svg-9w1vFFYCVQmRvHja {font-family:"trebuchet ms",verdana…

YACC移进规约冲突案例分析

总结 总结: bison给出的用例是发现冲突的最便捷方法。 第一种用例:明确用例(一个Example),直接反应问题。第二种用例:混淆用例(两个Example),解析器无法区分两条语句。…

jenkins 节点部署

1、节点注册 登陆jenkins master界面 路径:首页-->系统管理--> 节点管理-->新建节点(New Node) 插曲:我在新的服务器部署master节点,显示剩余交换空间为0B 处理方式请查看:Jenkins - Free Swap…

关于MCU的BootLoader的一些理解

一、关于STM32单片机IAP升级中if(((*(__IO uint32_t*)Addr_App) & 0x2FFE0000) 0x20000000)语句的理解 参考自:https://blog.csdn.net/weixin_45394120/article/details/122732203?spm1001.2014.3001.5502 疑问: 1、为什么要用Addr_App里的数据…

Web操作系统漏洞发现——工具使用总结

目录 (一)web层面 1、信息收集 0x01 网站源码自己开发 0x02 网站源码使用开源CMS 2、可维护Poc 0x01 pocassist 0x02 afrog 3、APP渗透 0x01 在BP上添加转发端口 0x02 Xray进行监听 0x03 触发数据 4、Goby (二)操作系统层…

xss.haozi靶场通关

做完xss-labs靶场后,再继续做这个靶场,感觉这个不是很难,毕竟在第一个靶场也获取了一些经验,但是这个靶场偏向技巧,所以还是以了解为主。 0x00: 分析:对我们的代码未作出限制,因此这里可以使用…

如何用 nodejs 进行 sha1 加密验证,微信公众号开发验证

如何用 nodejs 进行 sha1 加密验证,微信公众号开发验证 一、问题 今天在摆弄微信公众号的时候,遇到这样一个问题: 我的后台是 nodejs 写的,express 框架,官方开发接入的验证代码是 php 写的,其中有一个部…