日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL - Join关联查询优化 --- NLJ及BNL 算法初探

發布時間:2025/3/21 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL - Join关联查询优化 --- NLJ及BNL 算法初探 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 生猛干貨
  • Demo Table
  • 表關聯常見有兩種算法
    • 嵌套循環連接 Nested-Loop Join(NLJ) 算法 (NLP)
      • 定義
      • 示例
      • 執行過程
      • 規律
    • 基于塊的嵌套循環連接 Block Nested-Loop Join(BNL)算法
      • 定義
      • 示例
      • 執行過程
      • join_buffer 放不下怎么辦?
  • 被驅動表的關聯字段沒索引為什么要選擇使用 BNL 算法而不使用 Nested-Loop Join 呢?
  • 如何界定大表 小表
  • 關聯sql的優化的兩個核心點
  • 搞定MySQL

生猛干貨

帶你搞定MySQL實戰,輕松對應海量業務處理及高并發需求,從容應對大場面試


Demo Table

CREATE TABLE `t1` (`id` int(11) NOT NULL AUTO_INCREMENT,`a` int(11) DEFAULT NULL,`b` int(11) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_a` (`a`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;create table t2 like t1;

兩個表 t1 和 t2 , 一樣的,包括索引信息 a 字段有索引 b字段沒有索引。

數據量 t1 ,t2 如下

mysql> select count(1) from t1; +----------+ | count(1) | +----------+ | 10000 | +----------+ 1 row in setmysql> select count(1) from t2; +----------+ | count(1) | +----------+ | 100 | +----------+ 1 row in setmysql>

表關聯常見有兩種算法

嵌套循環連接 Nested-Loop Join(NLJ) 算法 (NLP)

定義

一次一行循環地從第一張表(稱為驅動表)中讀取行,在這行數據中取到關聯字段,根據關聯字段在另一張表(被驅動表)里取出滿足條件的行,然后取出兩張表的結果合集。


示例

舉個例子來說明一下

【關聯字段a有索引】

mysql> EXPLAIN select * from t1 inner join t2 on t1.a= t2.a; +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ | 1 | SIMPLE | t2 | NULL | ALL | idx_a | NULL | NULL | NULL | 100 | 100 | Using where | | 1 | SIMPLE | t1 | NULL | ref | idx_a | idx_a | 5 | artisan.t2.a | 1 | 100 | NULL | +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ 2 rows in setmysql>

從執行計劃中可以看出

  • 驅動表是 t2,被驅動表是 t1 。 從執行順序上來看,先執行的就是驅動表,所以id=1 的是t2 ,如果id相同,從上到下順序執行。 (id越大,優先級越高越先執行)

  • 使用了 NLJ算法 . 一般 join 語句中,如果執行計劃 Extra 中未出現 Using join buffer 則表示使用的 join 算法是 NLJ。


執行過程

mysql> EXPLAIN select * from t1 inner join t2 on t1.a= t2.a; +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ | 1 | SIMPLE | t2 | NULL | ALL | idx_a | NULL | NULL | NULL | 100 | 100 | Using where | | 1 | SIMPLE | t1 | NULL | ref | idx_a | idx_a | 5 | artisan.t2.a | 1 | 100 | NULL | +----+-------------+-------+------------+------+---------------+-------+---------+--------------+------+----------+-------------+ 2 rows in setmysql>

執行過程如下

  • 先從t2 驅動表里 取出一條記錄(如果有where條件,則按where條件過濾后的結果集中取出一行 )
  • 拿到t2 結果集中的一條記錄中的關聯字段 a , 去t1表中查找
  • 取出 t1 中滿足條件的行,跟 t2 中獲取到的結果合并,作為結果返回給客戶端
  • 重復上述步驟
  • 我們來算一下這個操作MySQL要讀取多少行數據

    首先讀取 t2 表的所有數據 100條記錄 ,然后遍歷這每行數據中字段 a 的值,根據 t2 表中 a 的值索引掃描 t1 表中的對應行(掃描100次 t1 表的索引(idx_a ),1次掃描可以認為最終只掃描 t1 表一行完整數據,也就是總共 t1 表也掃描了100行), 因此整個過程掃描了 200 行。 (估算的)

    如果被驅動表的關聯字段沒索引,使用NLJ算法性能會比較低 ,mysql會選擇Block Nested-Loop Join算法。


    規律

    • 優化器一般會優先選擇小表做驅動表。所以使用 inner join 時,排在前面的表并不一定就是驅動表。

    • 當使用left join時,左表是驅動表,右表是被驅動表

    • 當使用right join時,右表時驅動表,左表是被驅動表

    • 當使用join時,mysql會選擇數據量比較小的表作為驅動表,大表作為被驅動表。


    基于塊的嵌套循環連接 Block Nested-Loop Join(BNL)算法

    定義

    把驅動表的數據讀入到 join_buffer 中,然后掃描被驅動表,把被驅動表每一行取出來跟 join_buffer 中的數據做對比。


    示例

    【關聯字段b無索引】

    mysql> EXPLAIN select * from t1 inner join t2 on t1.b= t2.b; +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+ | 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL | 100 | 100 | NULL | | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 10337 | 10 | Using where; Using join buffer (Block Nested Loop) | +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+----------------------------------------------------+ 2 rows in setmysql>

    從執行計劃中可以看出

    • 驅動表是 t2,被驅動表是 t1 。 從執行順序上來看,先執行的就是驅動表,所以id=1 的是t2 ,如果id相同,從上到下順序執行。

    • 使用了BNL算法 . Extra 中 的Using join buffer (Block Nested Loop)說明該關聯查詢


    執行過程

  • 把 t2 的所有數據放入到 join_buffer 中
  • 把表 t1 中每一行取出來,跟 join_buffer 中的數據做對比
  • 返回滿足 join 條件的數據
  • 我們來算一下這個操作MySQL要讀取多少行數據

    整個過程對表 t1 和 t2 都做了一次全表掃描,因此掃描的總行數為10000(表 t1 的數據總量) + 100(表 t2 的數據總量) = 10100。

    join_buffer 里的數據是無序的,極端情況下對表 t1 中的每一行,都要做 100 次判斷,所以內存中的判斷次數是 100 * 10000= 100 萬次。


    join_buffer 放不下怎么辦?

    我們這個例子里表 t2 才 100 行,要是表 t2 是一個大表,join_buffer 放不下怎么辦呢

    join_buffer 的大小是由參數 join_buffer_size 設定的,默認值是 256k。

    mysql> show variables like '%join_buffer_size%'; +------------------+--------+ | Variable_name | Value | +------------------+--------+ | join_buffer_size | 262144 | +------------------+--------+ 1 row in setmysql>

    如果放不下表 t2 的所有數據話,策略很簡單,就是分段放

    舉個例子

    比如 t2 表有1000行記錄, join_buffer 一次只能放800行數據,那么執行過程就是先往 join_buffer 里放800行記錄,然后從 t1 表里取數據跟 join_buffer 中數據對比得到部分結果,然后清空 join_buffer ,再放入 t2 表剩余200行記錄,再次從 t1 表里取數據跟 join_buffer 中數據對比。所以就多掃了一次 t1 表。


    被驅動表的關聯字段沒索引為什么要選擇使用 BNL 算法而不使用 Nested-Loop Join 呢?

    如果上面第二條sql使用 Nested-Loop Join,那么掃描行數為 100 * 10000 = 100萬次,因為沒有索引,所以這個100萬磁盤掃描。

    雖然BNL也是100萬,但是是內存中計算 ,肯定要快

    所以,用BNL磁盤掃描次數少很多,相比于磁盤掃描,BNL的內存計算會快得多。

    因此MySQL對于被驅動表的關聯字段沒索引的關聯查詢,一般都會使用 BNL 算法。如果有索引一般選擇 NLJ 算法,有索引的情況下 NLJ 算法比 BNL算法性能更高


    如何界定大表 小表

    不是按照表中的數量來決定大表小表,而是根據參與計算的表的數量來決定大表還是小表。

    在決定哪個表做驅動表的時候,應該是兩個表按照各自的條件過濾,過濾完成之后,計算參與 join 的各個字段的總數據量,數據量小的那個表,就是“小表”,應該作為驅動表。


    關聯sql的優化的兩個核心點

    • 關聯字段加索引,讓mysql做join操作時盡量選擇NLJ算法
    • 小表驅動大表,寫多表連接sql時如果明確知道哪張表是小表可以用straight_join寫法固定連接驅動方式,省去mysql優化器自己判斷的時間.

    舉個例子

    比如:select * from t2 straight_join t1 on t2.a = t1.a; 代表指定mysql選著 t2 表作為驅動表。

    • straight_join只適用于inner join,并不適用于left join,right join。 因為left join,right join已經代表指定了表的執行順序
    • 盡可能讓優化器去判斷,因為大部分情況下mysql優化器是比人要聰明的。使用straight_join一定要慎重,因為部分情況下人為指定的執行順序并不一定會比優化引擎好。

    搞定MySQL

    總結

    以上是生活随笔為你收集整理的MySQL - Join关联查询优化 --- NLJ及BNL 算法初探的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。