一頓騷操作!我將 SQL 耗時從 30248.271s 優化到 0.001s

2020-12-26 網易

  

  場景

  我用的資料庫是mysql5.6,下面簡單的介紹下場景

  課程表

  create table Course(

  c_id int PRIMARY KEY,

  name varchar(10)

  )

  數據100條

  學生表:

  create table Student(

  id int PRIMARY KEY,

  name varchar(10)

  )

  數據70000條

  學生成績表SC

  CREATE table SC(

  sc_id int PRIMARY KEY,

  s_id int,

  c_id int,

  score int

  )

  數據70w條

  查詢目的:

  查找語文考100分的考生

  查詢語句:

  select s.* from Student s where s.s_id in (select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )

  執行時間:30248.271s

  暈,為什麼這麼慢,先來查看下查詢計劃:

  EXPLAIN

  select s.* from Student s where s.s_id in (select s_id from SC sc where sc.c_id = 0 and sc.score = 100 )
image

  發現沒有用到索引,type全是ALL,那麼首先想到的就是建立一個索引,建立索引的欄位當然是在where條件的欄位。

  先給sc表的c_id和score建個索引

  CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);

  再次執行上述查詢語句,時間為: 1.054s

  快了3w多倍,大大縮短了查詢時間,看來索引能極大程度的提高查詢效率,建索引很有必要,很多時候都忘記建

  索引了,數據量小的的時候壓根沒感覺,這優化的感覺挺爽。

  但是1s的時間還是太長了,還能進行優化嗎,仔細看執行計劃:

  image

  查看優化後的sql:

  SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
WHERE
< in_optimizer > (
`YSB`.`s`.`s_id` ,< EXISTS > (
SELECT
FROM
`YSB`.`SC` `sc`
WHERE
(
(`YSB`.`sc`.`c_id` = 0)
AND (`YSB`.`sc`.`score` = 100)
AND (
< CACHE > (`YSB`.`s`.`s_id`) = `YSB`.`sc`.`s_id`
)
)
)
)

  補充:這裡有網友問怎麼查看優化後的語句

  方法如下:

  在命令窗口執行

  
image

  有type=all

  按照我之前的想法,該sql的執行的順序應該是先執行子查詢

  select s_id from SC sc where sc.c_id = 0 and sc.score = 100

  耗時:0.001s

  得到如下結果:

  image

  然後再執行

  select s.* from Student s where s.s_id in(7,29,5000)

  耗時:0.001s

  這樣就是相當快了啊,Mysql竟然不是先執行裡層的查詢,而是將sql優化成了exists子句,並出現了EPENDENT SUBQUERY,

  mysql是先執行外層查詢,再執行裡層的查詢,這樣就要循環70007*8次。

  那麼改用連接查詢呢?

  SELECT s.* from

  Student s

  INNER JOIN SC sc

  on sc.s_id = s.s_id

  where sc.c_id=0 and sc.score=100

  這裡為了重新分析連接查詢的情況,先暫時刪除索引sc_c_id_index,sc_score_index

  執行時間是:0.057s

  效率有所提高,看看執行計劃:

  
image

  這裡有連表的情況出現,我猜想是不是要給sc表的s_id建立個索引

  CREATE index sc_s_id_index on SC(s_id);

  show index from SC

  
image

  在執行連接查詢

  時間: 1.076s,竟然時間還變長了,什麼原因?查看執行計劃:

  
image

  優化後的查詢語句為:

  SELECT
`YSB`.`s`.`s_id` AS `s_id`,
`YSB`.`s`.`name` AS `name`
FROM
`YSB`.`Student` `s`
JOIN `YSB`.`SC` `sc`
WHERE
(
(
`YSB`.`sc`.`s_id` = `YSB`.`s`.`s_id`
)
AND (`YSB`.`sc`.`score` = 100)
AND (`YSB`.`sc`.`c_id` = 0)
)

  貌似是先做的連接查詢,再進行的where條件過濾

  回到前面的執行計劃:

  
image

  這裡是先做的where條件過濾,再做連表,執行計劃還不是固定的,那麼我們先看下標準的sql執行順序:

  
image

  正常情況下是先join再進行where過濾,但是我們這裡的情況,如果先join,將會有70w條數據發送join做操,因此先執行where

  過濾是明智方案,現在為了排除mysql的查詢優化,我自己寫一條優化後的sql

  SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id

  即先執行sc表的過濾,再進行表連接,執行時間為:0.054s

  和之前沒有建s_id索引的時間差不多

  查看執行計劃:

  
image

  先提取sc再連表,這樣效率就高多了,現在的問題是提取sc的時候出現了掃描表,那麼現在可以明確需要建立相關索引

  CREATE index sc_c_id_index on SC(c_id);
CREATE index sc_score_index on SC(score);

  再執行查詢:

  SELECT
s.*
FROM
(
SELECT
*
FROM
SC sc
WHERE
sc.c_id = 0
AND sc.score = 100
) t
INNER JOIN Student s ON t.s_id = s.s_id

  執行時間為:0.001s,這個時間相當靠譜,快了50倍

  執行計劃:

  
image

  我們會看到,先提取sc,再連表,都用到了索引。

  那麼再來執行下sql

  SELECT s.* from

  Student s

  INNER JOIN SC sc

  on sc.s_id = s.s_id

  where sc.c_id=0 and sc.score=100

  執行時間0.001s

  執行計劃:

  image

  這裡是mysql進行了查詢語句優化,先執行了where過濾,再執行連接操作,且都用到了索引。

  2015-04-30日補充:最近又重新導入一些生產數據,經測試發現,前幾天優化完的sql執行效率又變低了

  調整內容為SC表的數據增長到300W,學生分數更為離散。

  先回顧下:

  show index from SC

  
image

  執行sql

  SELECT s.* from

  Student s

  INNER JOIN SC sc

  on sc.s_id = s.s_id

  where sc.c_id=81 and sc.score=84

  執行時間:0.061s,這個時間稍微慢了點

  執行計劃:

  image

  這裡用到了intersect併集操作,即兩個索引同時檢索的結果再求併集,再看欄位score和c_id的區分度,

  單從一個欄位看,區分度都不是很大,從SC表檢索,c_id=81檢索的結果是70001,score=84的結果是39425

  而c_id=81 and score=84 的結果是897,即這兩個欄位聯合起來的區分度是比較高的,因此建立聯合索引查詢效率

  將會更高,從另外一個角度看,該表的數據是300w,以後會更多,就索引存儲而言,都是不小的數目,隨著數據量的

  增加,索引就不能全部加載到內存,而是要從磁碟去讀取,這樣索引的個數越多,讀磁碟的開銷就越大,因此根據具體

  業務情況建立多列的聯合索引是必要的,那麼我們來試試吧。

  alter table SC drop index sc_c_id_index;
alter table SC drop index sc_score_index;
create index sc_c_id_score_index on SC(c_id,score);

  執行上述查詢語句,消耗時間為:0.007s,這個速度還是可以接收的

  執行計劃:

  
image

  該語句的優化暫時告一段落

  總結:

  1.mysql嵌套子查詢效率確實比較低

  2.可以將其優化成連接查詢

  3.連接表時,可以先用where條件對表進行過濾,然後做表連接

  (雖然mysql會對連表語句做優化)

  4.建立合適的索引,必要時建立多列聯合索引

  5.學會分析sql執行計劃,mysql會對sql進行優化,所以分析執行計劃很重要

  索引優化

  上面講到子查詢的優化,以及如何建立索引,而且在多個欄位索引時,分別對欄位建立了單個索引

  後面發現其實建立聯合索引效率會更高,尤其是在數據量較大,單個列區分度不高的情況下。

  單列索引

  查詢語句如下:

  select * from user_test_copy where sex = 2 and type = 2 and age = 10

  索引:

  CREATE index user_test_index_sex on user_test_copy(sex);
CREATE index user_test_index_type on user_test_copy(type);
CREATE index user_test_index_age on user_test_copy(age);

  分別對sex,type,age欄位做了索引,數據量為300w,查詢時間:0.415s

  執行計劃:

  
image_thumb3

  發現type=index_merge

  這是mysql對多個單列索引的優化,對結果集採用intersect併集操作

  多列索引

  我們可以在這3個列上建立多列索引,將表copy一份以便做測試

  create index user_test_index_sex_type_age on user_test(sex,type,age);

  查詢語句:

  select * from user_test where sex = 2 and type = 2 and age = 10

  執行時間:0.032s,快了10多倍,且多列索引的區分度越高,提高的速度也越多

  執行計劃:

  image_thumb5

  最左前綴

  多列索引還有最左前綴的特性:

  執行一下語句:

  select * from user_test where sex = 2
select * from user_test where sex = 2 and type = 2
select * from user_test where sex = 2 and age = 10

  都會使用到索引,即索引的第一個欄位sex要出現在where條件中

  索引覆蓋

  就是查詢的列都建立了索引,這樣在獲取結果集的時候不用再去磁碟獲取其它列的數據,直接返回索引數據即可

  如:

  select sex,type,age from user_test where sex = 2 and type = 2 and age = 10

  執行時間:0.003s

  要比取所有欄位快的多

  排序select * from user_test where sex = 2 and type = 2 ORDER BY user_name

  時間:0.139s

  在排序欄位上建立索引會提高排序的效率

  create index user_name_index on user_test(user_name)

  最後附上一些sql調優的總結,以後有時間再深入研究

  


  1.   列類型儘量定義成數值類型,且長度儘可能短,如主鍵和外鍵,類型欄位等等

      


  2.   建立單列索引

      


  3.   根據需要建立多列聯合索引

      

  當單個列過濾之後還有很多數據,那麼索引的效率將會比較低,即列的區分度較低,

  那麼如果在多個列上建立索引,那麼多個列的區分度就大多了,將會有顯著的效率提高。

  

  1.   根據業務場景建立覆蓋索引

      

  只查詢業務需要的欄位,如果這些欄位被索引覆蓋,將極大的提高查詢效率

  

  1.   多表連接的欄位上需要建立索引

      

  這樣可以極大的提高表連接的效率

  


  1.   where條件欄位上需要建立索引

      


  2.   排序欄位上需要建立索引

      


  3.   分組欄位上需要建立索引

      


  4.   Where條件上不要使用運算函數,以免索引失效

      

  福

相關焦點

  • 一次神奇的 sql 查詢經歷,group by 慢查詢優化記錄
    一、問題背景  現網出現慢查詢,在500萬數量級的情況下,單表查詢速度在30多秒,需要對sql進行優化,sql如下:  思路一:  後面應該加上 order by null;避免無用排序,但其實對結果耗時影響不大,還是很慢。
  • Oracle DBA之常用SQL
    有好多人或事,出現計劃外的狀況,最後那句「但,我依然愛你」 最靚虛擬環境的坑確實很多,tsm相關測試還在繼續,暫且整點sql當夜宵吧。語句select dbms_Lob.substr(a.SQL_FULLTEXT) from v$sql a where sql_id='0x7b21d4ed';select dbms_Lob.substr(s.SQL_FULLTEXT) from v$sql s,v$session se,v$process p where se.paddr=p.addr and s.sql_id
  • 女朋友都能看懂的,SQL優化乾貨
    一、什麼情況會不走索引1、模糊查詢,在欄位開頭模糊select * from teacher where name like '%老師'優化:,所以可以使用如下sql:select * from teacher where INSTR(name,'老師')>02、使用了in和not in,會全表掃描普通查詢:select *
  • Sql注入攻擊基本原理
    本文將帶你從介紹 Web 應用運行原理開始,一步一步理解 Sql 注入的由來、原理和攻擊方式。注入可以藉助資料庫的存儲過程進行提權等操作4、判斷Sql注入點4.1 判斷是否存在sql注入漏洞通常情況下,可能存在 Sql 注入漏洞的 Url 是類似這種形式 :http://xxx.xxx.xxx/abcd.php?id=XX對 Sql 注入的判斷,主要有兩個方面:判斷該帶參數的 Url 是否存在 Sql 注入?
  • mysql┃多個角度說明sql優化,讓你吊打面試官!
    ,也是重中之重,在很多大廠中會有專門的DBA來做這件事情,甚至更過分的是連應屆生的招聘崗位要求上都寫了需要懂一點sql優化,最近moon一直在寫關於mysql的文章,包括之前寫的索引相關,其實也都是為了這篇文章做個鋪墊,所以你懂了嗎,今天我將從表結構、索引、查詢語句、分庫分表這四個維度來和大家聊聊,在工作中,怎麼進行sql優化?
  • [摘要]ORACLE SQL語句優化技術分析
    (中國軟體網訊)     一.SQL語言的使用1.IN 操作符用IN寫出來的SQL的優點是比較容易寫及清晰易懂,這比較適合現代軟體開發的風格。推薦方案:在業務密集的SQL當中儘量不採用IN操作符2.NOT IN操作符此操作是強列推薦不使用的,因為它不能應用表的索引。推薦方案:用NOT EXISTS 或(外連接+判斷為空)方案代替3.<> 操作符(不等於)不等於操作符是永遠不會用到索引的,因此對它的處理只會產生全表掃描。
  • 新疆這頓「騷操作」,將成為廣東強勁的對手。
    休賽期,傳言新疆走範子銘、俞長棟,送西熱力江,轉可蘭白克,媒體人士說的一頓「騷操作」,真讓人看不懂。但明眼人都知道,這是新疆要徹底開始重建了。就在今天,傳出新疆籤約前國字號隊員於德豪,且明天就飛抵新疆與球隊匯合。
  • 帶你快速了解spark sql
    sql執行spark任務的分布式解析引擎。它能夠將用戶編寫的sql語言解析成RDD對應的分布式任務,由於spark是基於內存去處理、計算數據集,所以其執行速度非常快。spark sql對應的結構可以總結為下圖所示:DataSet,顧名思義,就是數據集的意思,它是 Spark 1.6 新引入的接口。
  • Sainsbury’s 選擇 Blue Yonder 優化供應鏈戰略
    Sainsbury’s 已選擇 Blue Yonder 端到端供應鏈平臺作為其供應鏈轉型的技術基礎。Sainsbury’s 將在統一的 AI 技術平臺上部署 Blue Yonder 解決方案,助力其端到端供應鏈戰略。為支持公司未來的供應鏈項目,Sainsbury’s 將進一步擴大當前的 Blue Yonder 解決方案陣容,納入全新的強大功能。
  • sqltoy-orm-4.16.11 發版,部分功能優化
    的十四個關鍵特點:1、最簡最直觀的sql編寫方式(不僅僅是查詢語句),採用條件參數前置處理規整法,讓sql語句部分跟客戶端保持高度一致2、sql中支持注釋(規避了對hint特性的影響,知道hint嗎?搜oracle hint),和動態更新加載,便於開發和後期維護整個過程的管理3、支持緩存翻譯和反向緩存條件檢索(通過緩存將名稱匹配成精確的key),實現sql簡化和性能大幅提升4、支持快速分頁和分頁優化功能,實現分頁最高級別的優化,同時還考慮到了cte多個with as情況下的優化支持5、支持並行查詢6、根本杜絕sql注入問題,以後不需要討論這個話題7、支持行列轉換
  • 經典SQL面試題及答案分析
    id,c_name:課程名稱,t_id:教師id):4、成績表 score(s_id:學生id,c_id:課程id,score:分數)初始化數據的sql附在文章末尾挑戰1、查詢各科成績前三名的記錄這種方式適用於編程,單純的sql實現不了,我們應該從下面這個思路去考慮------>我和04號同學學習的課程數目一樣多,且04號同學沒學過的我也沒學過,那麼我不就是和04號同學學習的課程一樣嗎?
  • 使用explain和show profile來分析SQL語句實現優化SQL語句
    SQL語句優化是建立在慢查詢分析的基礎上,通過慢查詢定位有問題的SQL語句,關於慢查詢的介紹及其分析工具,可以參考[mysql慢查詢及慢查詢日誌分析工具]一、通過explain查詢1 用法:explain sql2 作用:用於分析sql語句(1)、id:執行explain的一個編號(沒有實際意義)(2)、
  • IG管理騷操作:送出的兩位AD均已拿到S賽門票,自己卻可能無緣S10
    IG季後賽一輪遊,SN則拿到季軍和三號種子,TES更是直接奪冠加一號種子但是現如今的IG卻陷入了尷尬局面,IG在季後賽一輪遊慘遭LGD3:0,今年甚至還沒能拿到BO5的首勝,而煥烽所在的SN,卻在季後賽6:0(季軍賽3:0,冒泡賽3:0LGD0)LGD拿到三號種子。
  • NBA2K20又出騷操作:威少、土豆打中鋒,羅德曼來打得分後衛?
    近日,NBA2K20因為與現實中不符合的遊戲屬性評分,而被諸多球迷各種吐槽,但是更加騷操作的是,2K20竟然無視球迷們的意見,更是在MY TEAM模式中推出了更為過分的「位置鎖定」設置。    這樣胡來一頓的操作,讓NBA2K20的粉絲很不滿意,大家紛紛在2K20的社交帳號下留言吐槽:我去,2K想要幹嘛?他們瘋了嗎?  此前早就有NBA球星吐槽過2K20,比如說國王控球後衛福克斯,他就表示2K20就是一款垃圾遊戲,他是絕對不會參與其中的!
  • MySQL優化:定位慢查詢的兩種方法以及使用explain分析SQL
    定位慢查詢SQL在平時工作中,我想你肯定遇到過一條sql發出去了,但是等了好久才出現了返回值,這不僅僅影響了測試速度也大大降低了開發效率。所以我們有必要學習sql慢查詢定位。開啟慢查詢日誌(默認是關閉的):mysql> set global slow_query_log = on;Query OK, 0 rows affected (0.00 sec)設置慢查詢時間限制(查詢時間只要大於這個值都將記錄到慢查詢日誌中,單位:秒):mysql> set global long_query_time = 1;
  • BeetlSQL 3.0.0-M1 第一個版本發布 - OSCHINA - 中文開源技術交流...
    經過8個月的重構,BeetlSQL3裡程碑1已經完成BeetlSQL是一款資料庫訪問工具庫,廣泛應用到企業應用,網際網路項目。相比於BeetlSQL2,新版支持更多數據來源,包括支持JDBC的傳統資料庫,大數據NOSQL,以及大數據SQL查詢引擎,在易用性何擴展性也做了大幅度修改。
  • 售價220美元,XSX/S的擴展存儲卡值嗎?
    我的第一臺電腦是 1999 年買的,清華同方,64MB 內存,硬碟大概只有 6GB,排除掉系統和七七八八的工具軟體,想要裝《暗黑破壞神2》就得考慮把整個盤清空一遍。這臺電腦用了 7 年才算退役,長期的磨礪,不僅把我培養為一位空間管理大師,也將「害怕硬碟不夠用的焦慮感」刻在了靈魂裡。
  • 最後0.8s,老詹又想提前離場!再看巴特勒,太騷了
    ,甚至是直接到更衣室,球會繼續進行,直到死球;該場上球員不會給技術犯規,但是經過NBA office回看後,可能會追加罰款,甚至禁賽。然而今天比賽最後還剩0.8s,草草發了個球的詹姆斯又想提前離場了——慢鏡頭顯示
  • 快來和FIFA Online 4一起,秀出你的騷操作!
    快來和FIFA Online 4一起,秀出你的騷操作!FIFA Online 4已經上線一周年,相信大家在遊戲內已經無數次大展身手了。那麼現在,我們就為大家提供一個可以展現自己的舞臺!無論是遊戲內的精彩進球,真實球場上的精彩過人,還是利用微視FIFA專屬魔法掛件錄製的過人技巧。只要你按照下面的方法上傳視頻,就能夠參與到我們的活動中來。
  • 耗時又繁重的SQL診斷優化,以後就都交給資料庫自治服務DAS吧!
    例如如何準確地識別執行計劃中的瓶頸點,通過優化物理庫設計或SQL改寫等手段,讓資料庫優化器回歸到最佳執行計劃, 另外,由於SQL工作負載及其基礎數據龐大且不斷變化,SQL優化還是一項非常耗時繁重的任務,這些都決定了SQL優化是一項高門檻,高投入的工作。