算法|为什么不让用join?《死磕MySQL系列 十六》

算法|为什么不让用join?《死磕MySQL系列 十六》

文章图片

算法|为什么不让用join?《死磕MySQL系列 十六》

文章图片


大家好 , 我是咔咔 不期速成 , 日拱一卒
在平时开发工作中join的使用频率是非常高的 , 很多SQL优化博文也让把子查询改为join从而提升性能 , 但部分公司的DBA又不让用 , 那么使用join到底有什么问题呢?


一、什么是Nested-Loop Join在MySQL中 , 使用Nested-Loop Join的算法进行优化join的使用 , 此算法翻译过来为嵌套循环连接 , 并且使用了三种算法来实现 。
  1. Index Nested-Loop Join :简称NLJ
  2. Block Nested-Loop Join :简称BNLJ
  3. Simple Nested-Loop Join :简称 BNL
这几种算法大致意思为索引嵌套循环连接、缓存块嵌套循环连接、粗暴嵌套循环连接 , 你现在看的顺序就是MySQL选择join算法的优先级 。
从名字上给人感觉Simple Nested-Loop Join算法是非常简单同样也是最快的 , 但实际情况是MySQL并没有使用这种算法而是优化成使用Block Nested-Loop Join , 带着各种疑问一起来探索其中的奥秘 。
都看到这里了 , 是不是对嵌套循环连接的意思不太明白?其实是非常简单的 , 一个简单的案例你就能明白什么是嵌套循环连接 。
假设现在有一张文章表article , 一张文章评论表article_detail , 需求是查询文章的id查询出所有的评论现在的首页 , 那么SQL就会是以下的样子
  1. select * from article a left join article_detail b on a.id = b.article_id

若使用代码来描述这段SQL的实现原理大致如下 , 这段代码使用切片和双层循环实现冒泡排序 , 这段代码就能非常代表SQL中join的实现原理 , 第一层for即为驱动表 , 第二层for则为被驱动表 。

func bubble_sort(arr [
int) {
    a := 0 
    for j := 0; j < len(arr)-1; j++ {
        for i := 0; i < len(arr)-1-j; i++ {
            if arr[i
 > arr[i+1
 {
                a = arr[i
        
                arr[i
 = arr[i+1
 
                arr[i+1
 = a
            
        
    


好了 , 现在你知道了什么是Nested-Loop Join , 也知道了实现Nested-Loop Join的三种算法 , 接下来咱们就围绕这三种算法来进行讨论 , 为什么不让用join 。
二、Index Nested-Loop Join为了防止优化器对SQL进行粗暴优化 , 接下来会使用STRAIGHT_JOIN来进行查询操作 。
为什么会需要STRAIGHT_JOIN , 在开发过程中有没有遇到明明是驱动表的却莫名其妙的成为了被驱动表 , 在MySQL中驱动表的概念是当指定了连接条件时 , 满足条件并记录行数少的表为驱动表 。 当没有指定查询条件时 , 则扫描行数少的为驱动表 , 优化器总是以小表驱动大表的方式来决定执行顺序的 。
索引嵌套循环连接是基于索引进行连接的算法 , 索引是基于被驱动表的 , 通过驱动表查询条件直接与被驱动表索引进行匹配 , 防止跟被驱动表的每条记录进行比较 , 利用索引的查询减少了对被驱动表的匹配次数 , 从而提升join的性能 。
使用前提
使用索引嵌套查询的前提是驱动表与被驱动表关联字段上有设置索引 。
接下来使用一个案例来详细解析索引嵌套查询的具体执行流程 , 以下SQL是所有的表和数据 , 直接复制就可以用

CREATE TABLE `article` (`id` INT (11) NOT NULL AUTO_INCREMENT COMMENT 'ID'`author_id` INT (11) NOT NULLPRIMARY KEY (`id`)) ENGINE=INNODB CHARSET=utf8mb4 COLLATE utf8mb4_general_ci COMMENT='文章表';