热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

oracle如何读取到从n行到m行的数据_关系型数据库进阶之查询优化

点上面“东哥IT笔记”,关注并星标每天一篇业界最新技术分享在前面几篇文章中,我们已经介绍了总体构架,客户端管理和查询管理。在查询管理中&#

点上面“东哥IT笔记”,关注并星标

每天一篇业界最新技术分享


在前面几篇文章中,我们已经介绍了总体构架,客户端管理和查询管理。在查询管理中,有一个很重要的部分我们没有介绍,那就是查询优化,这也是本文所要介绍的内容。

所有的现代数据库都是基于cost进行优化的(Cost Based Optimization, CBO)。总体的思想就是看每一个操作的cost是多少,然后找出一个cost最小的路径来执行这些操作并获取结果。

本文会首先以最常的三种join为例来进行介绍,看看哪怕是简单的join,它背后的优化是如何进行工作的。在这些例子中,我们关注时间复杂度,这个其实和真实的数据库优化器的计算是有所不同的,它真实会关注CPU消耗,磁盘I/O消耗以及内存的需求。时间复杂度和CPU消耗其实是差不多的,我们这种懒人就直接使用时间复杂度来进行分析了。当然有时候我们还需要考虑磁盘的I/O消耗,因为很多时候真正的瓶颈就是在于磁盘I/O而不是CPU的使用率。

JOIN的操作

我们会来看三个最常见的join操作,merger Join, Hash Join以及Nested Loop Join。在正式开始之前,我们来引入两个名词,outer relationinner relation,很简单outer relation就是join左边的数据集,inner relation就是join右边的数据集。比如A JOIN B, 我们把A称之为outer relationB称之为inner relation。并且假设outer relationN个元素,inner relationM个元素。我们可以很容易知道A JOIN BB JOIN A其实是不太相同的。

Nested loop join

Nested loop join是最简单的一种情况

a59ed0ec42c3fa55a20c0248ae6353b9.png

它的思想是,对每一个在outer ralation中的行,都去看是否在inner relation中存在。要是从伪码的角度来看,它是这样实现的:

nested_loop_join(arrayouter, array inner) for each row a in outer for each row b in inner if (match_join_condition(a,b)) write_result_in_output(a,b) end if end for end for

其实说白了就是全遍历的循环,所以时间复杂度是O(N*M)

从磁盘I/O的角度来看,对每一个在outer relation中的行(N),都需要从inner relation中读出M行。所以就需要从磁盘读取N + N*M次。但是假如inner relation的行比较少,我们就不需要每次都从磁盘中读取,而是可以把它保存在memory中,这种情况下磁盘的读取就会少很多,只需要N+M次。所以,这种情况下,我们希望N的大小足够小,这样就可以使用memory来保存而不需要每次都进行磁盘的读取。

当然了,假如inner relation能够有index,那么它读取的磁盘I/O的次数会更少。

那么假如inner relation的数据很大,不能够保存在memory中怎么办呢,是不是就一定要进行N+N*M次的磁盘读取呢?其实并不尽然,这里有一个常见的优化方法,就是每次都读一个bunch的行,然后把这些行放到memory中,让他们和outer relation先进行匹配,等他们做完了,在读下一个bunch,这样一来,最终磁盘I/O的访问就得到了优化。

下面是伪码的实现:

//improved version to reduce the disk I/O.nested_loop_join_v2(fileouter, file inner) for each bunch ba in outer // ba is now in memory for each bunch bb in inner // bb is now in memory for each row a in ba for each row b in bb if (match_join_condition(a,b)) write_result_in_output(a,b) end if end for end for end for end for

有了这种优化之后,时间复杂度还是一样的,但是磁盘I/O的访问次数就变成了 bunch的次数(outer)+bunch 次数(outer)*bunch次数(inner)。所以总得来说可以通过增加bunch的大小来减少bunch的次数,这样的磁盘I/O访问就会变小。

Hash Join

Hash join相比上面的nested loop join稍微复杂一点,但是它的cost在大多数情况下则会小很多。

3f5c7fee10e28b9e291da1d1746b523d.png

Hash Join的思想如下:

  1. inner relation中得到所有的元素

  2. 创建一个in     memoryhash

  3. outer     realtion一个一个的得到元素

  4. 计算每一个的hash值,然后找到inner     relation中对应的bucket

  5. 然后在bucket中查看是否有匹配的元素

在计算相关的时间复杂度之前,我们来做一些假设:

  • Inner     relation被分成了Xbucket

  • Hash函数针对outer     relationinner relation都可以均匀分布,也就是说每一个bucket的大小差不多

  • Bucket内部查找匹配的时候的消耗就是bucket的大小

所以时间复杂度就是 (M/X)*N + 创建hash表的cost + N*hash函数的cost。假如hash函数所创造的bucket的大小足够小,那么其实时间复杂度相当于O(M+N)

另外还有一种hash join的算法,对内存来说很友好,但是磁盘I/O消耗则大一些:

  1. inner relationouter relaition都创建hash

  2. 把他们放到磁盘中

  3. 然后把两者通过一个bucket一个bucket地进行比较

Merge Join

Merge Join是唯一的会产生排好序的结果的Join

Merge Join可以分成两步:

  • (可选)排序:两个输入都按照join key进行排序

  • Merge     join:把排好序的两者merge到一起

排序

使用merge sort(算法)进行排序是一个很好的选择,当然假如内存不是问题,我们还会有更好的方法。

只是有时候,很多时候其实已经都排好序了,比如:

  • 表格本身就排好序,比如join的表格是基于index进行组织的

  • join的条件是表的index

  • join的一方是一个临时的结果,但这个结果是已经排好序的

Merge Join

f4f920051a85891e391deb30f0da876d.png

总得思想和merge sort是类似的,只是这里我们只选择两者相等的元素:

  1. 比较两个relation中的元素

  2. 假如相等,把他们放到result中,然后比较两者中的下一个元素

  3. 不相等,就比较小的relation中的下一个元素

  4. 重复上面步骤

这是一个很简单的示例,现实情况可能会比这个复杂一点,比如他们中间有多个相等的内容如何比较等等。不过我们就先来看看这个简单的情况。

先来看看时间复杂度,假如两个relation是都已经排序了,那么复杂度就是O(N+M)。假如两者都需要进行排序,那么复杂度就是 O(N*Log(N) +M*Log(M))

三种join的比较

毫无疑问,这三个join都有其使用的场景,没有哪一个是必然比另外两个好的(废话,否则为什么要三种),选择哪一种join其实取决于很多因素:

  • 空闲memory的数量:假如没有足够的memory,显然没法使用hash     join

  • 两个数据集的大小:比如你有一个很大的表和一个很小的表进行join,那么nested     loop可能是一个比hash join更快的选择,因为你不需要花费时间去创建hash表。而假如你的两个表都很大,那么nested     loop可能需要非常耗费CPU

  • index:假如有B+ TREEindex,那么merge join可能是一个更好的选择

  • 结果需要排序:假如你所需要的结果要求排序,那么可能选择merge     join更好,因为你终归是要进行排序的。

  • 假如relation已经排序了:这个没啥好说的,merge     join是一个不错的选择。

  • join的类型:比如你使用的是相等join还是inner     join, outer join或者self-join等等,这些都会影响最终的选择。

  • 数据的分布:比如你想join名字中的姓,因为有很多重复的数据,那么你使用hash     join可能就会遇到很多问题。

  • 你是否想让多进程/线程执行join操作:这也会影响最终join类型的选择。

本文介绍了查询优化中两个表的join操作,我们了解了三种常见的join类型: nested loop join, merge join以及hash join,并把他们的原理和使用场景进行了解释。不过总得来说我们还是使用的最简单的两个表join来进行说明的,那么假如是多个表的join该如何处理呢,我们将会在下一篇文章中进行介绍。



推荐阅读
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • 本文详细介绍了在 Oracle 数据库中使用 MyBatis 实现增删改查操作的方法。针对查询操作,文章解释了如何通过创建字段映射来处理数据库字段风格与 Java 对象之间的差异,确保查询结果能够正确映射到持久层对象。此外,还探讨了插入、更新和删除操作的具体实现及其最佳实践,帮助开发者高效地管理和操作 Oracle 数据库中的数据。 ... [详细]
  • 本文介绍了几种常用的图像相似度对比方法,包括直方图方法、图像模板匹配、PSNR峰值信噪比、SSIM结构相似性和感知哈希算法。每种方法都有其优缺点,适用于不同的应用场景。 ... [详细]
  • 本文详细介绍了如何使用OpenSSL自建CA证书的步骤,包括准备工作、生成CA证书、生成服务器待签证书以及证书签名等过程。 ... [详细]
  • 本文深入解析了JDK 8中HashMap的源代码,重点探讨了put方法的工作机制及其内部参数的设定原理。HashMap允许键和值为null,但键为null的情况只能出现一次,因为null键在内部通过索引0进行存储。文章详细分析了capacity(容量)、size(大小)、loadFactor(加载因子)以及红黑树转换阈值的设定原则,帮助读者更好地理解HashMap的高效实现和性能优化策略。 ... [详细]
  • 在安装并配置了Elasticsearch后,我在尝试通过GET /_nodes请求获取节点信息时遇到了问题,收到了错误消息。为了确保请求的正确性和安全性,我需要进一步排查配置和网络设置,以确保Elasticsearch集群能够正常响应。此外,还需要检查安全设置,如防火墙规则和认证机制,以防止未经授权的访问。 ... [详细]
  • 在Ubuntu上安装MySQL时解决缺少libaio.so.1错误及libaio在MySQL中的重要性分析
    在Ubuntu系统上安装MySQL时,遇到了缺少libaio.so.1的错误。本文详细介绍了如何解决这一问题,并深入探讨了libaio库在MySQL性能优化中的重要作用。对于初学者而言,理解这些依赖关系和配置步骤是成功安装和运行MySQL的关键。通过本文的指导,读者可以顺利解决相关问题,并更好地掌握MySQL在Linux环境下的部署与管理。 ... [详细]
  • 通过将常用的外部命令集成到VSCode中,可以提高开发效率。本文介绍如何在VSCode中配置和使用自定义的外部命令,从而简化命令执行过程。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 本文总结了在SQL Server数据库中编写和优化存储过程的经验和技巧,旨在帮助数据库开发人员提升存储过程的性能和可维护性。 ... [详细]
  • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
  • 本地存储组件实现对IE低版本浏览器的兼容性支持 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
  • 本文介绍了如何利用ObjectMapper实现JSON与JavaBean之间的高效转换。ObjectMapper是Jackson库的核心组件,能够便捷地将Java对象序列化为JSON格式,并支持从JSON、XML以及文件等多种数据源反序列化为Java对象。此外,还探讨了在实际应用中如何优化转换性能,以提升系统整体效率。 ... [详细]
author-avatar
手浪用户2502939427_143
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有