前言

连接(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 算法的更多相关文章

  1. 后端程序员必备的 Linux 基础知识

    1. 从认识操作系统开始 正式开始 Linux 之前,简单花一点点篇幅科普一下操作系统相关的内容. 1.1. 操作系统简介 我通过以下四点介绍什么是操作系统: 操作系统(Operating Syste ...

  2. <转载> 优秀程序员必备的23条好习惯

    转自 优秀程序员必备的23条好习惯 编程是一项聪明人玩的游戏,它既是对智力的考验,也是对习惯的考验,智力的好坏取决于父母的基因,人们无从左右,但习惯的好坏却是可以不断培养.一项由美国芝加哥大学国家研究 ...

  3. Java 程序员必备的 15 个框架,前 3 个地位无可动摇!

    Java 程序员方向太多,且不说移动开发.大数据.区块链.人工智能这些,大部分 Java 程序员都是 Java Web/后端开发.那作为一名 Java Web 开发程序员必须需要熟悉哪些框架呢? 今天 ...

  4. 「编程羽录」上线,程序员必备的这些技能你能get到嘛?

    大家好,我是小羽. 好久不见,给大家带来个好消息,小羽的全新专题「编程羽录」系列正式上新,主要是介绍一些关于面试题和经验总结的文章. 会为大家提供一些技术栈之外,程序员还需要的其他方面硬核知识,做到全 ...

  5. 谈谈Java程序员进阶的那些知识和方向

    谈谈Java程序员进阶的那些知识和方向 记得前段时间看过一篇文章谈到一种程序员叫野生程序员,战斗力极强,可以搞定一切问题,但是通常看问题抓不到本质,或者说是google/baidu/stackover ...

  6. Sharepoint程序员应该了解的知识

    做为一个Sharepoint程序员应该了解的知识:注意,我说的是程序员.因为我一直把自己看一个普普通通的程序员. 前提: 要知道网络基础(包括DHCP.IP.掩码.DNS.网关.广播),会装操作系统( ...

  7. Java程序员必备的 15框开发工具

    15款Java程序员必备的开发工具 如果你是一名Web开发人员,那么用膝盖想也知道你的职业生涯大部分将使用Java而度过.这是一款商业级的编程语言,我们没有办法不接触它. 对于Java,有两种截然不同 ...

  8. 代码自动生成工具MyGeneration之一(程序员必备工具)

    代码自动生成工具MyGeneration之一(程序员必备工具) 转 分类: C#2008-08-06 18:12 16064人阅读 评论(12) 收藏 举报 工具数据库相关数据库stringbrows ...

  9. Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器!

    Sublime Text 2 - 性感无比的代码编辑器!程序员必备神器! http://www.iplaysoft.com/sublimetext.html 代码编辑器或者文本编辑器,对于程序员来说, ...

  10. Java程序员必备的一些流程图

    Java程序员必备的一些流程图 转自https://juejin.im/post/5d214639e51d4550bf1ae8df 前言: 整理了一些Java基础流程图/架构图,做一下笔记,大家一起学 ...

随机推荐

  1. 通过jmeter连接人大金仓数据库

    某项目用的人大金仓数据库,做性能测试,需要用jmeter来连接数据库处理一批数据.jmeter连接人大金仓,做个记录. 1. 概要 在"配置元件"中添加"JDBC Con ...

  2. 【第6篇】AI语音测试简介

    1.4 AI语音测试简介 AI语音测试是针对人工智能语音交互的测试,人工智能语音交互一般包含语音识别技术(ASR).自然语言处理技术(NLP)和语音合成技术(TTS)等3大部分,通俗地来讲AI语音测试 ...

  3. Vue2组件间通讯

    Vue2组件通信的基础方式 自己的理解:组件化通信,无非就是数据你传我,我传你,两个组件的相互交流,方法很多,下方有图示(此篇建议小白阅读,大神的话也不会看,哈哈哈哈!仅供参考,有不同的意见可以一起交 ...

  4. GO开发工具litelDE的安装与使用

    1.MinGW的下载与安装 地址:http://sourceforge.net/projects/mingw/ 下载安装 D:\Program Files\MinGW 然后打开D:\Program F ...

  5. js 传递路径参数到后台的转码和解码

    在开发中遇到前端页面需要将一个附件的路径传递后台实现业务逻辑,但不进行编码一直报404的错误,上代码. 前端编码:JavaScript函数encodeURL() 说明:1 .encodeURL函数主要 ...

  6. Tkinter根据屏幕分辨率最大化适应屏幕

    还不能够实现所有组件随分辨率自动变化 # 实现的是界面覆盖整个屏幕 from tkinter import * import win32api, win32con # 获取屏幕的分辨率 width = ...

  7. 我今天吃了SHI,请对下联

    最近看到不少好玩的.实用的 Github 项目,就来给大家推荐一把. 1. 跨平台终端 Tabby(前身是 Terminus) 是一个可高度配置的终端模拟器和 SSH 或串口客户端,支持 Window ...

  8. day36-ThreadLocal

    ThreadLocal 线程数据共享和安全 1.什么是ThreadLocal? ThreadLocal的作用,可以实现在同一个线程数据共享,从而解决多线程数据安全问题 当http请求发送到Tomcat ...

  9. 0停机迁移Nacos?Java字节码技术来帮忙

    摘要:本文介绍如何将Spring Cloud应用从开源Consul无缝迁移至华为云Nacos. 本文分享自华为云社区<0停机迁移Nacos?Java字节码技术来帮忙>,作者:华为云PaaS ...

  10. 深度学习GPU加速配置方法

    深度学习GPU加速配置方法 一.英伟达官方驱动及工具安装 首先检查自己的电脑驱动版本,未更新至最新建议先将驱动更新至最新,然后点击Nvidia控制面板 2.在如下界面中点击系统信息,点击显示可以看见当 ...