程序员必备的数据库知识 2:Join 算法
前言
连接(Join)是关系数据库重要特性,它和事务常被作为数据库与文件系统的两个重要区别项。程序员江湖一直流传着某某 baba 的神秘开发宝典,其中数据库部分有重要一条避免过多表的 Join,奈何 Join 特性实在是好用,广大程序员们无视着宝典的谆谆教诲,依旧每天乐此不疲的使用这 Join 特性。那数据库有哪些连接算法呢?它们的实现方式是怎样呢?它们之间又有什么区别呢?为什么需要这么多不同的连接算法呢?如果你也好奇这些问题,那么请继续往下阅读,本文将逐一回答上述问题。
关联算法简介
关系型数据库主要有三种 Join 算法:Nested Loop Join,Hash Join、 Merge Join,像 Oracle、SqlServer 、DB2 这几位数据库中的老炮均支持三种 Join 方式;MySQL 长久以来只支持 NLJ 或其变种,直到8.0.18 版本后才有限的支持 Hash Join。在 「程序员必备的数据库知识:数据存储结构」一文中介绍了数据库几种常见的数据存储结构,存储引擎之上是计算引擎。以 MySQL 数据库为例,计算引擎层通常包括 SQL 接口、解析器、查询优化器、缓存等组件,数据库 Join 实现就在计算引擎的查询优化器中。
以 MySQL 数据库为例,计算引擎层
然而数据库具体选择哪种连接算法,是由本身决定的,主要根据当前的优化器模式、表大小、连接列是否有索引和排序等因素决定。
多表连接方式又分为:内连接(等值连接)、外连接和交叉连接,外连接又分为:左外连接、右外连接和全外连接。对于不同方式的连接查询,使用相同的 Join 算法也会有不同的成本产生,这和实现方式紧密相关的。本文不涉及同一个 Join 算法在不同连接方式的情况。
Nested Loop Join
NLJ 是 MySQL 最重要的连接方式,也是 MySQL 长期唯一支持的连接方式,直到 8.0.18 版本 MySQL 才有限的支持 Hash Join。那什么是 NLJ 呢?从概念上讲,NLJ 相当于两个嵌套循环,用第一张表做 Outter Loop,第二张表做 Inner Loop,Outter Loop 的每一条记录跟 Inner Loop 的记录作比较,最终符合条件的就将该数据记录。
可以用以下伪代码表示:
NLJ 可以用伪代码表示
如果忽略内存和 CPU 的时间,它的成本是:
Cost(NLJ) = Read(M) + M * Read(N) (其中M和N表示需要读两个关联表中的数据行数)
NLJ 的算法比较简单,并且对 Join 的连接条件没有特殊要求(Hash Join 通常只支持等值,Merge Join 一般不支持不等和like),在有索引过滤性较好的 OLTP 场景下,它的查询效率很高。缺点也同样明显,由于它的成本是:Read(M) + M * Read(N) 。在 OLAP 需要大表间 Join 场景下,它的查询效率变得比较差。在 MySQL 中 NLJ 还有两个变种:Index Nested Loop Join(INLJ)、Block Nested Loop Join(BNLJ),本文不涉及这方面的扩展,有兴趣的同学可以深入研究。
Hash Join
Hash Join 是Oracle、SQLServer 、PostgreSQL 中重要的关联算法,当两个表关联时,选择一张表按照 join 条件给的列构建 hash 表,然后将第二张表的每行记录去探测 hash 表中的数据,如果符合连接条件就输出该数据。前一张表我们叫做 build 表,后一张表我们的叫做 probe 表。为了减少内存使用量,通常选择小表作为 hash 表,大表作为 probe 表。
Hash Join
经典 Hash Join 主要有两个步骤:选择 hash 表,扫描该表并创建 hash 表;将另一个作为 probe 表,扫描每一行数据,然后在 hash 表中找寻对应的满足条件的记录。忽略内存和 CPU 时间,它的成本是:
Cost(HJ) = Read(M) + Read(N)
Hash Join 需要把表放到内存中,如果内存不够怎么办?为了处理这种情况,又诞生一些 Hash Join 的变种,比如 Grace Hash Join 。简单说是通过分区方式实现,根据关联字段将两个表的数据分区,然后对同一分区的数据再进行原生 Hash join 的 build 与 probe 过程,最后将所有分区的数据合并成最后的结果集。当然在实际中会更复杂,比如在大数据量的情况下,有概率出现不同数据的 HASH 值却是相同的问题。
总的来说,Hash Join 是处理大表间 Join 的不错选择。MySQL 在 8.0.18 前一直没有 Hash join 的实现,甚至在5.5以前只有最原始的 NLJ,5.5后才有 NLJ 优化变种的 B(Block)NLJ。但 Oracle 早在7.3版本之后就引入了 Hash join 算法,在 OLAP 领域中 Hash join 更是绝对的标配,Greenplum 和 Spark SQL 就充分利用了它。但是它也有缺点,比如只能使用等值查询、需要更多的内存资源等。
Merge Join
Merge Join ,准确地说它叫 Sort Merge Join, 在合并关联查询时要先确保两个关联表是按关联字段相同排序的。如果关联字段有可用的索引(配合聚集索引服用效果更佳)并且排序一致,则可以直接进行Merge 操作,否则要先对关联表按照关联字段进行一次排序。排好序后,再从每个表取一条记录开始匹配,如果符合关联条件,则放入结果集中;否则将关联字段值较小的记录抛弃,从这条记录对应的表中取下一条记录继续进行匹配,直到整个循环结束。因此它的成本是这样的:
COST(MJ) = Read(M) + Sort(M) + Read(N) + Sort(N)
显然,Merge Join 适合在关联列上有索引的表,最好在关联列还有相同的排序方式,在这种情况下它的关联查询效率是最高的。但是关联字段如果没有排序,那么它的排序阶段则比较耗时。
总结
通过前文的分析,我们基本可以回答文章最开头的几个问题了,更多信息可以看下表格。另外,除了上述常见的三种数据库Join方式外,还有 Hive 支持 Map Join 和 Reduce Join。
常见 Join 算法的优势对比总览
作者
司马辽太杰是 NineData 工程师。NineData 向企业和个人提供高效、安全的数据库 SQL 开发、数据库备份、数据复制/迁移/集成、数据对比等功能,是一个 SaaS 服务开箱即用,可以快速提升企业 SQL 开发效率,保障企业数据安全。NineData 地址:https://www.ninedata.cloud/
程序员必备的数据库知识 2:Join 算法的更多相关文章
- 后端程序员必备的 Linux 基础知识
1. 从认识操作系统开始 正式开始 Linux 之前,简单花一点点篇幅科普一下操作系统相关的内容. 1.1. 操作系统简介 我通过以下四点介绍什么是操作系统: 操作系统(Operating Syste ...
- <转载> 优秀程序员必备的23条好习惯
转自 优秀程序员必备的23条好习惯 编程是一项聪明人玩的游戏,它既是对智力的考验,也是对习惯的考验,智力的好坏取决于父母的基因,人们无从左右,但习惯的好坏却是可以不断培养.一项由美国芝加哥大学国家研究 ...
- Java 程序员必备的 15 个框架,前 3 个地位无可动摇!
Java 程序员方向太多,且不说移动开发.大数据.区块链.人工智能这些,大部分 Java 程序员都是 Java Web/后端开发.那作为一名 Java Web 开发程序员必须需要熟悉哪些框架呢? 今天 ...
- 「编程羽录」上线,程序员必备的这些技能你能get到嘛?
大家好,我是小羽. 好久不见,给大家带来个好消息,小羽的全新专题「编程羽录」系列正式上新,主要是介绍一些关于面试题和经验总结的文章. 会为大家提供一些技术栈之外,程序员还需要的其他方面硬核知识,做到全 ...
- 谈谈Java程序员进阶的那些知识和方向
谈谈Java程序员进阶的那些知识和方向 记得前段时间看过一篇文章谈到一种程序员叫野生程序员,战斗力极强,可以搞定一切问题,但是通常看问题抓不到本质,或者说是google/baidu/stackover ...
- Sharepoint程序员应该了解的知识
做为一个Sharepoint程序员应该了解的知识:注意,我说的是程序员.因为我一直把自己看一个普普通通的程序员. 前提: 要知道网络基础(包括DHCP.IP.掩码.DNS.网关.广播),会装操作系统( ...
- Java程序员必备的 15框开发工具
15款Java程序员必备的开发工具 如果你是一名Web开发人员,那么用膝盖想也知道你的职业生涯大部分将使用Java而度过.这是一款商业级的编程语言,我们没有办法不接触它. 对于Java,有两种截然不同 ...
- 代码自动生成工具MyGeneration之一(程序员必备工具)
代码自动生成工具MyGeneration之一(程序员必备工具) 转 分类: C#2008-08-06 18:12 16064人阅读 评论(12) 收藏 举报 工具数据库相关数据库stringbrows ...
- Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!
Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器! http://www.iplaysoft.com/sublimetext.html 代码编辑器或者文本编辑器,对于程序员来说, ...
- Java程序员必备的一些流程图
Java程序员必备的一些流程图 转自https://juejin.im/post/5d214639e51d4550bf1ae8df 前言: 整理了一些Java基础流程图/架构图,做一下笔记,大家一起学 ...
随机推荐
- MyBatis笔记03------XXXMapper.xml文件解析
SQL映射文件的配置解析 当我们写好mapper(dao)层接口时,然后在对应的XXXMapper.xml文件中写业务逻辑对应的SQL映射语句,通过这个文件中可以实现CRU操作,那么下面说明如何编写这 ...
- day16-Servlet05
Servlet05 14.HttpServletRequest HttpServletRequest对象代表客户端的请求 当 客户端/浏览器 通过HTTP协议访问服务器时,HTTP请求头中的所有信息都 ...
- WPF之BackgroundWorker
BackgroundWorker类允许您在单独的线程上执行某个可能导致用户界面(UI)停止响应的耗时操作,下面来介绍一下这个线程类BackgroundWorker,大家可以结合这位大佬的这篇文章,说的 ...
- 【ELK解决方案】ELK集群+RabbitMQ部署方案以及快速开发RabbitMQ生产者与消费者基础服务
前言: 大概一年多前写过一个部署ELK系列的博客文章,前不久刚好在部署一个ELK的解决方案,我顺便就把一些基础的部分拎出来,再整合成一期文章.大概内容包括:搭建ELK集群,以及写一个简单的MQ服务. ...
- Json web token(JWT)攻防
免责声明: 本文章仅供学习和研究使用,严禁使用该文章内容对互联网其他应用进行非法操作,若将其用于非法目的,所造成的后果由您自行承担,产生的一切风险与本文作者无关,如继续阅读该文章即表明您默认遵守该内容 ...
- 【云原生 · Kubernetes】部署高可用kube-scheduler集群
个人名片: 因为云计算成为了监控工程师 个人博客:念舒_C.ying CSDN主页️:念舒_C.ying 部署高可用kube-scheduler集群 13.1 创建 kube-scheduler 证 ...
- PHP 0817
原题链接:http://www.wechall.net/challenge/php0817/index.php 点看题目 意思是他有一个php写的系统,但是他很容易收到LFI的影响,然后我们的任务就是 ...
- (C++) 类与 static_cast 与 dynamic_cast
static_cast static_cast相当于C语言里面的强制转换,适用于: 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换.进行上行转换(把派生类的指针或引用转换成基类表示) ...
- easui 两个combobox相互选中时至对方为空的解决方案
combobox HTML: <select id="monthplan" class="zxui-combobox" name="monthp ...
- Kafka技术专题之「性能调优篇」消息队列服务端出现内存溢出OOM以及相关性能调优实战分析
内存问题 本篇文章介绍Kafka处理大文件出现内存溢出 java.lang.OutOfMemoryError: Direct buffer memory,主要内容包括基础应用.实用技巧.原理机制等方面 ...