<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          神奇的SQL之聯(lián)表細(xì)節(jié):MySQL JOIN的執(zhí)行過程(二)

          共 8673字,需瀏覽 18分鐘

           ·

          2021-09-13 15:34

          點(diǎn)擊關(guān)注上方“SQL數(shù)據(jù)庫(kù)開發(fā)”,

          設(shè)為“置頂或星標(biāo)”,第一時(shí)間送達(dá)干貨
          SQL專欄
          SQL基礎(chǔ)知識(shí)第二版
          SQL高級(jí)知識(shí)第二版

          神奇的SQL之聯(lián)表細(xì)節(jié):MySQL JOIN的執(zhí)行過程(一)中,我們講到了 JOIN 的部分內(nèi)容,像:驅(qū)動(dòng)表、JOIN 大致流程等。還沒看的小伙伴趕緊去補(bǔ)課!下面是這篇的主要內(nèi)容:


          • BKA(Batched Key Access)

          • ON 和 WHERE


          環(huán)境準(zhǔn)備


          • 數(shù)據(jù)庫(kù):MySQL 5.7.1

          • 存儲(chǔ)引擎:InnoDB

          • 建表和初始化數(shù)據(jù)


          -- 查看版本和存儲(chǔ)引擎SELECT VERSION();
          SHOW ENGINES;
          SHOW VARIABLES LIKE '%storage_engine%';-- 表創(chuàng)建與數(shù)據(jù)初始化DROP TABLE IF EXISTS tbl_user;CREATE TABLE tbl_user (
            id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
            sex TINYINT(1) NOT NULL COMMENT '性別, 1:男,0:女',
            create_time datetime NOT NULL COMMENT '創(chuàng)建時(shí)間',
            update_time datetime NOT NULL COMMENT '更新時(shí)間',
              remark VARCHAR(255) NOT NULL DEFAULT '' COMMENT '備注', PRIMARY KEY (id)
          ) COMMENT='用戶表';DROP TABLE IF EXISTS tbl_user_login_log;CREATE TABLE tbl_user_login_log (
            id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵', user_name VARCHAR(50) NOT NULL COMMENT '用戶名',
            ip VARCHAR(15) NOT NULL COMMENT '登錄IP',
            client TINYINT(1) NOT NULL COMMENT '登錄端, 1:android, 2:ios, 3:PC, 4:H5',
            create_time datetime NOT NULL COMMENT '創(chuàng)建時(shí)間', PRIMARY KEY (id)
          ) COMMENT='登錄日志';INSERT INTO tbl_user(user_name,sex,create_time,update_time,remark) VALUES('何天香',1,NOW(), NOW(),'朗眉星目,一表人材'),
          ('薛沉香',0,NOW(), NOW(),'天星樓的總樓主薛搖紅的女兒,也是天星樓的少總樓主,體態(tài)豐盈,烏發(fā)飄逸,指若春蔥,袖臂如玉,風(fēng)姿卓然,高貴典雅,人稱“天星絕香”的武林第一大美女'),
          ('慕容蘭娟',0,NOW(), NOW(),'武林東南西北四大世家之北世家慕容長(zhǎng)明的獨(dú)生女兒,生得玲瓏剔透,粉雕玉琢,脾氣卻是剛烈無(wú)比,又喜著火紅,所以人送綽號(hào)“火鳳凰”,是除天星樓薛沉香之外的武林第二大美女'),
          ('萇婷',0,NOW(), NOW(),'當(dāng)今皇上最寵愛的侄女,北王府的郡主,腰肢纖細(xì),遍體羅綺,眉若墨畫,唇點(diǎn)櫻紅;雖無(wú)沉香之雅重,蘭娟之熱烈,卻別現(xiàn)出一種空靈'),
          ('柳含姻',0,NOW(), NOW(),'武林四絕之一的添愁仙子董婉婉的徒弟,體態(tài)窈窕,姿容秀麗,真?zhèn)€是秋水為神玉為骨,芙蓉如面柳如腰,眉若墨畫,唇若點(diǎn)櫻,不弱西子半分,更勝玉環(huán)一籌; 搖紅樓、聽雨軒,琵琶一曲值千金!'),
          ('李凝雪',0,NOW(), NOW(),'李相國(guó)的女兒,神采奕奕,英姿颯爽,愛憎分明'),
          ('周遺夢(mèng)',0,NOW(), NOW(),'音神傳人,湘妃竹琴的擁有者,云髻高盤,穿了一身黑色蟬翼紗衫,愈覺得冰肌玉骨,粉面櫻唇,格外嬌艷動(dòng)人'),
          ('葉留痕',0,NOW(), NOW(),'圣域圣女,膚白如雪,白衣飄飄,宛如仙女一般,微笑中帶著說不出的柔和之美'),
          ('郭疏影',0,NOW(), NOW(),'揚(yáng)灰右使的徒弟,秀發(fā)細(xì)眉,玉肌豐滑,嬌潤(rùn)脫俗'),
          ('鐘鈞天',0,NOW(), NOW(),'天界,玄天九部 - 鈞天部的部主,超凡脫俗,仙氣逼人'),
          ('王雁云',0,NOW(), NOW(),'塵緣山莊二小姐,刁蠻任性'),
          ('許侍霜',0,NOW(), NOW(),'藥王谷谷主女兒,醫(yī)術(shù)高明'),
          ('馮黯凝',0,NOW(), NOW(),'桃花門門主,嬌艷如火,千嬌百媚');INSERT INTO tbl_user_login_log(user_name, ip, client, create_time) VALUES('薛沉香', '10.53.56.78',2, '2019-10-12 12:23:45'),
          ('萇婷', '10.53.56.78',2, '2019-10-12 22:23:45'),
          ('慕容蘭娟', '10.53.56.12',1, '2018-08-12 22:23:45'),
          ('何天香', '10.53.56.12',1, '2019-10-19 10:23:45'),
          ('柳含姻', '198.11.132.198',2, '2018-05-12 22:23:45'),
          ('馮黯凝', '198.11.132.198',2, '2018-11-11 22:23:45'),
          ('周遺夢(mèng)', '198.11.132.198',2, '2019-06-18 22:23:45'),
          ('郭疏影', '220.181.38.148',3, '2019-10-21 09:45:56'),
          ('薛沉香', '220.181.38.148',3, '2019-10-26 22:23:45'),
          ('萇婷', '104.69.160.60',4, '2019-10-12 10:23:45'),
          ('王雁云', '104.69.160.61',4, '2019-10-16 20:23:45'),
          ('李凝雪', '104.69.160.62',4, '2019-10-17 20:23:45'),
          ('許侍霜', '104.69.160.63',4, '2019-10-18 20:23:45'),
          ('葉留痕', '104.69.160.64',4, '2019-10-19 20:23:45'),
          ('王雁云', '104.69.160.65',4, '2019-10-20 20:23:45'),
          ('葉留痕', '104.69.160.66',4, '2019-10-21 20:23:45');SELECT * FROM tbl_user;SELECT * FROM tbl_user_login_log;DROP TABLE IF EXISTS tbl_range_access;CREATE TABLE tbl_range_access (
            id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主鍵',
            a INT(11) NOT NULL COMMENT '測(cè)試索引',
              name VARCHAR(50) NOT NULL COMMENT '姓名',
              age TINYINT(3) NOT NULL COMMENT '年齡', PRIMARY KEY (id), INDEX i_a(a)
          ) COMMENT='mrr測(cè)試';INSERT INTO tbl_range_access(a,name,age) VALUES(5,'123654', 23),
          (8, 'asdf',20),
          (1,'lljl',19),
          (4, '98459',64),
          (7,'zhangsan', 45),
          (9,'lisi',46),
          (2,'zhaoqian',25),
          (6,'hello', 23),
          (3,'world',100),
          (10,'666',66),
          (88, '888',88);SELECT * FROM tbl_range_access;






          表 tbl_range_access 的數(shù)據(jù)要多一點(diǎn),像上面示例只有 11 條記錄,那么即使 a 字段上有索引, SELECT * FROM tbl_range_access WHERE a BETWEEN 4 AND 9; 也不會(huì)走索引,執(zhí)行計(jì)劃如下:



          數(shù)據(jù)太少,優(yōu)化器覺得走索引,然后回表查詢數(shù)據(jù),還不如直接走聚簇索引全表查詢來(lái)的快,所以沒有選擇走索引 i_a 


          既然數(shù)據(jù)太少,我們就多造點(diǎn)數(shù)據(jù),運(yùn)行 data-init 下的 RangeAccessTest.java 中的 batchAddData 方法就好,輕輕松松 10W 到手! 此時(shí)執(zhí)行計(jì)劃如下



          MRR


          講 BKA 之前了,我們不得不先看下 MRR,它是 BKA 的重要支柱。


          全稱 Multi-Range Read ,是對(duì)多行 IO 查詢進(jìn)行優(yōu)化的一種策略,詳情可看 MySQL 的 mrr-optimization 或者 MariaDB 的 Multi Range Read Optimization(MySQL 和 MariaDB 是什么關(guān)系? 呃,這么說吧,他們是一個(gè)爹的兒子)。簡(jiǎn)單點(diǎn)來(lái)說,MRR 是優(yōu)化器將隨機(jī) IO 轉(zhuǎn)化為順序 IO 以降低查詢過程中 IO 開銷的一種手段


          • 什么是讀盤與落盤(IO)?

          當(dāng)前絕大多少情況下,MySQL 的數(shù)據(jù)是存在機(jī)械硬盤(SATA 盤)上的,極少數(shù)情況下是存在固態(tài)硬盤(SSD)上的;讀盤指的是從磁盤讀取數(shù)據(jù)的過程,落盤指的是從內(nèi)存持久化到磁盤的過程


          • 為什么順序讀盤比隨機(jī)讀盤快?

          這不是絕對(duì)的,多數(shù)情況下是這樣的;至于為什么,這涉及到機(jī)械硬盤的硬件知識(shí)了,包括其組織結(jié)構(gòu),以及磁盤的讀盤過程,另外還需要了解 MySQL 數(shù)據(jù)的落盤與讀盤(頁(yè)為單位),內(nèi)容太多,就不在本篇講了。


          1. 使用場(chǎng)景


          不是任何情況下 MySQL 都會(huì)使用 MRR 的,只是在某些情況下會(huì)用 MRR 來(lái)進(jìn)行優(yōu)化。


          摘自 Multi Range Read Optimization


          MySQL 中的 NDB 也會(huì)用到 MRR,一般而言,我們無(wú)需關(guān)注,我們只關(guān)注上圖中的情況就行了。理論之后來(lái)點(diǎn)案例,完美!


          2. range access


          表 tbl_range_access 的 a 字段上我們已經(jīng)建了索引 i_a ,我們來(lái)個(gè)范圍查詢,看下執(zhí)行計(jì)劃 EXPLAIN SELECT * FROM tbl_range_access WHERE a BETWEEN 4 AND 9;  如下



          此時(shí)沒有用到 MRR,執(zhí)行此查詢時(shí),磁盤 IO 訪問模式將遵循下圖中的紅線



          因?yàn)槭?nbsp;SELECT * ,所以通過索引 i_a 先找到主鍵 ID,然后通過主鍵 ID 回表(從聚簇索引)查詢完整記錄;a 在索引 i_a 中是有序的,但不保證主鍵在 i_a 中也是有序的(關(guān)于 MySQL 的索引,推薦大家去看:MySQL的索引),這就導(dǎo)致回表的過程是隨機(jī) IO 


          為什么 MySQL 沒有采用 MRR 來(lái)保證回表的過程是順序 IO 呢?


          mrr-optimization 中有這么一段話:

          Two optimizer_switch system variable flags provide an interface to the use of MRR optimization. The mrr flag controls whether MRR is enabled. If mrr is enabled (on), the mrr_cost_based flag controls whether the optimizer attempts to make a cost-based choice between using and not using MRR (on) or uses MRR whenever possible (off). By default, mrr is on and mrr_cost_based is on


          mrr 和 mrr_cost_based 的默認(rèn)值是 on ;我簡(jiǎn)單畫個(gè)圖,大家就明白這兩個(gè)開關(guān)的作用了



          上面的示例之所以沒使用 MRR,是優(yōu)化器覺得使用 MRR 反而提升了成本,還不如不使用。


          我們強(qiáng)制優(yōu)化器使用 MRR:


          -- 查看所有開關(guān)及其默認(rèn)值 
          SELECT @@optimizer_switch;

          -- mrr_cost_based設(shè)置成off,強(qiáng)制優(yōu)化器使用
          mrr SET optimizer_switch='mrr_cost_based=off';


          我們?cè)賮?lái)看看執(zhí)行計(jì)劃是什么樣的



          此時(shí)用到 MRR,執(zhí)行此查詢時(shí),磁盤 IO 訪問模式將遵循下圖中的紅線



          此時(shí)回表查詢的主鍵是有序的,會(huì)采用順序 IO 來(lái)讀取數(shù)據(jù),從而提高查詢效率。


          MySQL 中有個(gè) rowids_buffer,用來(lái)緩存從索引 i_a 中查詢到的數(shù)據(jù)記錄(包含字段 a 和主鍵 ID),緩存滿了或者索引查完了,再對(duì)緩存中記錄按照主鍵 id 進(jìn)行排序,再用排序后的主鍵 id 進(jìn)行回表,使得回表查詢的過程是順序 IO


          是不是感覺 MRR 有點(diǎn)像二級(jí)索引與主鍵的 JOIN 操作,有這感覺就對(duì)了,后面的 BKA 也就好理解了


          BKA


          BKA 全稱是:Batched Key Access ,是對(duì)INL優(yōu)化后的一種聯(lián)表算法,類似與 BNL 對(duì) SNL 的優(yōu)化,但又有些不同,具體我們往下看


          先在表 tbl_user 新增一個(gè)索引 ALTER TABLE tbl_user ADD index i_aaa(user_name); ,此時(shí)查看執(zhí)行計(jì)劃 EXPLAIN SELECT * FROM tbl_user_login_log tl LEFT JOINtbl_user tu ON tl.user_name = tu.user_name; 如下圖



          此時(shí)的聯(lián)表算法就是 INL,因?yàn)?/span>表 tbl_user_login_log 的 user_name 是無(wú)索引的,那么從表 tbl_user_login_log 取出的 user_name 的值就是無(wú)序的,再去關(guān)聯(lián) tbl_user ,就會(huì)隨機(jī)匹配索引 i_aaa ,類似下圖



          是不是有點(diǎn)類似于前面講過的回表隨機(jī) IO ?


          BKA 功能默認(rèn)是關(guān)閉的( batched_key_access=off ),開啟它 

          SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';


          我們?cè)賮?lái)看執(zhí)行計(jì)劃



          從tbl_user_login_log 查詢到的 user_name 的值先放到 join buffer,當(dāng) join buffer 滿了或者數(shù)據(jù)查完了,再對(duì) join buffer 里面的值進(jìn)行排序,然后再去關(guān)聯(lián) tbl_user ,此時(shí)就會(huì)順序匹配索引 i_aaa ,類似下圖



          如果需要回表,那么 MySQL 會(huì)按之前講到過的回表流程再優(yōu)化一次


          默認(rèn)值的思考


          MRR 相關(guān)的 3 個(gè)開關(guān)的默認(rèn)值是這樣的 mrr=on,mrr_cost_based=on,batched_key_access=off 


          • mrr=on 表示 mrr 功能是開啟的,開啟并不代表一定會(huì)使用,但不開啟則一定享受不到 mrr 帶來(lái)的優(yōu)化

          • mrr_cost_based=on 表示優(yōu)化器會(huì)基于成本考慮來(lái)決定是否使用 mrr,使用 mrr 反而使成本變高,那為什么使用 mrr ?只有 mrr 確實(shí)是帶來(lái)了效率上的提升,那么使用它才有意義,但是成本的計(jì)算又是優(yōu)化器來(lái)完成的,而且是一個(gè)比較復(fù)雜的過程,一定能保證優(yōu)化器的成本計(jì)算是準(zhǔn)確的嗎?100%準(zhǔn)確肯定不敢保證,但經(jīng)過這么多年的沉淀,絕大多數(shù)情況下,優(yōu)化器的成本計(jì)算是準(zhǔn)確的,所以 mrr_cost_based 建議就采用默認(rèn)值 on ,由優(yōu)化器來(lái)決定是否采用 mrr


          • batched_key_access=off 表示默認(rèn)不啟用 BKA,說實(shí)話,我沒太理解這么做的意圖;既然是否使用 mrr 交由優(yōu)化器來(lái)決定了,沒什么不把是否使用 BKA 也交由優(yōu)化器來(lái)決定?我能猜到的可能原因之一是基本用不到 ,為什么這么說? 我們回想下 BKA 會(huì)在什么情況下使用: 驅(qū)動(dòng)表在關(guān)聯(lián)的字段上無(wú)索引,而被驅(qū)動(dòng)表在關(guān)聯(lián)的字段上有索引 ,而如果驅(qū)動(dòng)表在關(guān)聯(lián)的字段上有索引了,還有必要進(jìn)行緩存、排序、再關(guān)聯(lián)被驅(qū)動(dòng)表嗎 ? 很顯然不必了,因?yàn)樗饕淖侄伪緛?lái)就是有序的了;而實(shí)際應(yīng)用中,關(guān)聯(lián)的字段,不管是驅(qū)動(dòng)表還是被驅(qū)動(dòng)表,往往是同時(shí)存在索引的,而不是一個(gè)存在索引而另一個(gè)不存在索引。這只是我個(gè)人的猜想,望知道的大神能解惑下,小弟不勝感激!


          總結(jié)


          • mrr 帶來(lái)的性能上的提升就是將隨機(jī) IO 優(yōu)化成 順序 IO,從而提高查詢效率

          • mrr 的使用場(chǎng)景比較有限, range access 和基于 req、eq_ref access 的 BKA,至于其他不適用的場(chǎng)景,我們可以結(jié)合 mrr 的特性分析出原因


          • mrr 相關(guān)的 3 個(gè)開關(guān)的默認(rèn)值不建議改動(dòng),這可是 MySQL 這么多年的經(jīng)驗(yàn)總結(jié)


          作者:青石路

          來(lái)源:博客園

          本文為轉(zhuǎn)發(fā)分享,轉(zhuǎn)載請(qǐng)聯(lián)系原作者授權(quán)


          最后給大家分享我寫的SQL兩件套:《SQL基礎(chǔ)知識(shí)第二版》《SQL高級(jí)知識(shí)第二版》的PDF電子版。里面有各個(gè)語(yǔ)法的解釋、大量的實(shí)例講解和批注等等,非常通俗易懂,方便大家跟著一起來(lái)實(shí)操。


          有需要的讀者可以下載學(xué)習(xí),在下面的公眾號(hào)「數(shù)據(jù)前線」(非本號(hào))后臺(tái)回復(fù)關(guān)鍵字:SQL,就行

          數(shù)據(jù)前線


          后臺(tái)回復(fù)關(guān)鍵字:1024,獲取一份精心整理的技術(shù)干貨

          后臺(tái)回復(fù)關(guān)鍵字:進(jìn)群,帶你進(jìn)入高手如云的交流群。


          推薦閱讀

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美成人免费电影 | 日本久久一级片 | 丰满肥臀无码一区二区三区 | 免费黄色视频观看 | 日本黄色视频在线观看 |