Mysql數據庫的join算法介紹,優美的執行優化

數據庫的連接

前幾天,小夥伴們在羣裏面討論進行優化join語句,你們都很積極的發言討論,結論是圍繞索引與大小表關係來進行操做,重要的是業務進行綁定。算法

部份內容來源於極客時間的Mysql實戰45講。sql

在Mysql的數據庫中,咱們知道join連接主要使用的有大體三種狀況。數據庫

  • inner join:內鏈接
  • left joinL:左連接
  • right join:右連接

那這些join咱們須要怎麼使用呢?而且可使用的很好,須要咱們在數據庫裏面嘗試下。微信

數據準備

該數據表來源於網絡。網絡

-- 建立測試數據庫
CREATE DATABASE join_test CHARSET UTF8;

-- 人員信息表
CREATE TABLE `Persons` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `LastName` char(16) NOT NULL DEFAULT '',
  `FirstName` char(16) NOT NULL DEFAULT '',
  `Address` varchar(128) NOT NULL DEFAULT '',
  `City` varchar(128) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 訂單表
CREATE TABLE `Orders` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `OrderNo` int(11) NOT NULL DEFAULT '0',
  `Pid` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `Persons` (`LastName`, `FirstName`, `Address`, `City`)
VALUES
('Adams', 'John', 'Oxford Street', 'London'),
('Bush', 'George', 'Fifth Avenue', 'New York'),
('Carter', 'Thomas', 'Changan Street', 'Beijing');

INSERT INTO `Orders` (`OrderNo`, `Pid`)
VALUES (77895, 3), (44678, 3), (22456, 1), (24562, 1), (34764, 65);
複製代碼

建立了兩個字段的關聯關係,而且關聯關係這裏沒有使用索引字段。oop

join中使用的 算法

使用的算法有幾種,一個是Index Nested-Loop join,另外就是Block Nested-Loop Join.性能

關聯表Peoples,與Order表測試

Block Nested-Loop Join

explain select  p.* from Persons p
INNER JOIN Orders  o on p.id = o.Pid

複製代碼

執行結果圖: 優化

2019-03-25-23-36-54

從圖上能夠看出驅動表是Peoples,被驅動表是Order,因爲咱們的關聯關係中,被驅動表沒有索引,因此在執行關聯的時候第二張表要全盤掃描。ui

那執行流程是怎樣的呢?

  1. 將驅動表裏面須要讀取的數據放入到內存中(useing join buffer)。
  2. 從被驅動表中取出來一行內容,與內存中的數據進行匹配,符合結果的結果集取出來。
  3. 循環執行被驅動表中的全部數據,
  4. 被驅動表執行完畢後,在執行驅動表的下一條數據。
  5. 2-4,直到驅動表的數據執行完畢,結束。

結果執行的數量是笛卡爾積,進行乘法。

若是join buffer 裏面的數據放不下怎麼辦?

就先取出來一部分驅動表裏面的數據,進行與第二個表對比,循環執行,對比結束後清空buffer中的內容,再處理。

從上面能夠看出,當被驅動表上沒有使用索引的時候會涉及全盤掃描,而且是兩個表都全盤掃描,雖然第一個表內容讀取到內存中能夠加快數據的讀取,可是全盤掃描對於性能屬於一個損耗。

因此咱們須要儘量的創建索引

Index Nested-Loop

那麼若是咱們創建索引了呢?

增長索引  索引的名字 與索引的列
CREATE INDEX Pid ON Orders (Pid)

EXPLAIN select  p.* from Persons p
STRAIGHT_JOIN Orders o on  p.id = o.Pid;
複製代碼

由於咱們在被驅動的表上增長了索引,因此當咱們須要的是Persons表中的數據時候,能夠利用到索引,執行結果以下。

2019-03-26-00-21-33

當兩個表關聯的時候,咱們的People表,還有Order表。選擇People選擇爲驅動表,Order爲被動表,使用On關聯的時候Order 字段上有索引,那麼就會使用該執行算法語句。

算法內容以下:

  1. 取出驅動表的裏面的一條數據
  2. 拿着這條數據去表二中查找到合適的結果集,返回。
  3. 重複以上循環。

能夠看到是使用的循環驅動表中的數據而後去被驅動表中查找,利用索引,減小第二個循環的次數。這樣就能加快速度。

兩個算法總結

從上面能夠看出,在選擇使用join的時候,必定要避免sql語句將關聯的第二個上使用join語句,咱們能夠每次將本身執行的語句加上explain簡單的看下sql執行計劃,在優化咱們的sql語句。

還有做爲驅動表的數據儘量少,循環的數據就不多了。

這就有咱們前面所說的小表做爲驅動表,大表加索引。這個概念。

Join

上面咱們說了使用的兩個算法,那麼咱們在執行過程當中會遇到哪些呢?

Inner join

重點強調,咱們的語句都每次使用explain來查看輸出

inner join 與join語句執行結果是一致的,因此在看執行結果,咱們沒必要要關注某個點。

在使用inner join 的時候,以哪一個左表仍是右表做爲依賴表都是存在可能的,因此咱們可使用straight_join來強制使用某個表做爲依賴表,而且在使用inner join語句的時候該straight_join 也是一個優化的方式。

強制採用某個表做爲依賴表。

// 注意當咱們使用join語句,須要查詢第二個表數據的時候,若是咱們的where 條件中沒有增長 篩選條件可能會致使使用Block Nested-Loop Join
EXPLAIN select  p.id,o.OrderNo from Persons p
STRAIGHT_JOIN Orders o on  p.id = o.Pid;
複製代碼

那麼這種狀況下怎麼優化的呢?

2019-03-26-00-37-04

從圖上能夠看出來,咱們的表二沒有走索引,致使咱們數據進行全盤的掃描。

在每個數據庫的表上,咱們都瞭解會有主鍵索引,那麼咱們是否能夠根據主鍵索引來排除呢?

咱們經過sql來看。

當OrderNo上沒有索引的時候
EXPLAIN select  p.id,o.OrderNo from Persons p
STRAIGHT_JOIN Orders o on  p.id = o.Pid
where o.OrderNo > 30000

複製代碼

2019-03-26-00-42-28

走的是全盤掃描,若是咱們在OrderNo上加上索引呢?

增長普通索引
CREATE INDEX Pid ON Orders (Pid)

//在我建立索引的時候,有時候條件語句是能夠用上索引的,有的時候是用不上的。因爲數據量過小的緣由致使部分索引使用補上的狀況。在這裏根據不一樣的字段內容是能使用上索引的。

EXPLAIN select  p.id,o.OrderNo from Persons p
STRAIGHT_JOIN Orders o on  p.id = o.Pid
where o.Pid > 3

複製代碼

能使用索引
不能使用索引
數據分佈

因此咱們在使用語句的時候須要多多關注索引的使用,關於Tree索引,咱們下次再聊。

總結

從上面能夠看到,使用索引能幫助咱們提升不少,可是寫入的SQL中能不能執行使用索引,還跟語句的構成有關。

儘可能在寫出來的sql都須要執行下explain 檢查下執行情況,知道sql的執行結果,這樣咱們能真正的寫出來好的join語句。

關於使用join,建議使用left join或者right join 提升效率。具體分析下次再聊。

·END·

路雖遠,行則必至

本文原發於 同名微信公衆號「胖琪的升級之路」,回覆「1024」你懂得,給個讚唄。

微信ID:YoungRUIQ

公衆號
相關文章
相關標籤/搜索