52 條 SQL 語句性能優化策略(建議收藏)

做者 | JiekeXu
html

來源 | JiekeXu之路(ID: JiekeXu_IT)
mysql

轉載請聯繫受權 | (微信ID:xxq1426321293)web

你們好,我是 JiekeXu,很高興又和你們見面了,今天分享一篇轉載文章 52 條 SQL 語句性能優化策略(建議收藏) 歡迎點擊上方藍字關注我,標星或置頂,更多幹貨第一時間到達!

本文會提到 52 條 SQL 語句性能優化策略,建議收藏下來,慢慢看,無論你是開發、運維、仍是 DBA,本文都會對你有所幫助,文中主要是以 MySQL 爲主,但 SQL 無論是哪一種數據庫基本上都適用,固然有些不敢苟同的優化建議我我的也有一點兒不一樣建議,能夠一塊兒看看。sql


一、對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 WHERE 及 ORDER BY 涉及的列上創建索引。數據庫


二、應儘可能避免在 WHERE 子句中對字段進行 NULL 值判斷,建立表時 NULL 是默認值,但大多數時候應該使用 NOT NULL,或者使用一個特殊的值,如 0,-1 做爲默認值。緩存


三、應儘可能避免在 WHERE 子句中使用 != 或 <> 操做符。MySQL 只有對如下操做符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些時候的 LIKE。性能優化


四、應儘可能避免在 WHERE 子句中使用 OR 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,可使用 UNION ALL 合併查詢:服務器

select id from t where num=10 union all select id from t where num=20;微信


五、IN 和 NOT IN 也要慎用,不然會致使全表掃描。對於連續的數值,能用 BETWEEN 就不要用 IN:網絡

select id from t where num between 1 and 3;


六、下面的查詢也將致使全表掃描:

select id from t where name like‘%abc%’; 

或者 

select id from t where name like‘%abc’;

若要提升效率,能夠考慮全文檢索。而

select id from t where name like‘abc%’;

纔用到索引。


七、若是在 WHERE 子句中使用參數,也會致使全表掃描。


八、應儘可能避免在 WHERE 子句中對字段進行表達式操做,應儘可能避免在 WHERE 子句中對字段進行函數操做。


九、不少時候用 EXISTS 代替 IN 是一個好的選擇:

select num from a where num in(select num from b);

用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num);

--注:固然這裏我不敢苟同,in 和 exists 沒有孰優孰劣,同等狀況下仍是須要看執行計劃,執行計劃才具備參考性,2020 DTC 嘉年華羅老師《SQL等價改寫核心思想》裏有說明「不要脫離執行計劃和數據量來評價 in 和 exists 誰快誰慢」。《SQL等價改寫核心思想》 PDF 可在公衆號【JiekeXu之路】後臺回覆「SQL等價改寫核心思想」得到。

十、索引當然能夠提升相應的 SELECT 的效率,但同時也下降了 INSERT 及 UPDATE 的效率。由於 INSERT 或 UPDATE 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過 6 個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

--注:這裏我不敢苟同,也說一句吧,在 Oracle 裏一個表的索引數最好不要超過 5 個,只能仁者見仁仁智者見智無非對錯,不過 索引高度也不要超過 4,對於太多的索引應該要進行索引監控,對不使用的索引及時清理。


十一、應儘量的避免更新 clustered 索引數據列, 由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。


十二、儘可能使用數字型字段,若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。


1三、儘量的使用 varchar, nvarchar 代替 char, nchar。由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。


1四、最好不要使用返回全部:select * from t ,用具體的字段列表代替 「*」,不要返回用不到的任何字段。


1五、儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。


1六、使用表的別名(Alias):當在 SQL 語句中鏈接多個表時,請使用表的別名並把別名前綴於每一個 Column 上。這樣一來,就能夠減小解析的時間並減小那些由 Column 歧義引發的語法錯誤。


1七、使用「臨時表」暫存中間結果 :


簡化 SQL 語句的重要方法就是採用臨時表暫存中間結果。可是臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在 tempdb 中了,這能夠避免程序中屢次掃描主表,也大大減小了程序執行中「共享鎖」阻塞「更新鎖」,減小了阻塞,提升了併發性能。


1八、一些 SQL 查詢語句應加上 nolock,讀、寫是會相互阻塞的,爲了提升併發性能。對於一些查詢,能夠加上 nolock,這樣讀的時候能夠容許寫,但缺點是可能讀到未提交的髒數據。


使用 nolock 有3條原則:


  • 查詢的結果用於「插、刪、改」的不能加 nolock;

  • 查詢的表屬於頻繁發生頁分裂的,慎用 nolock ;

  • 使用臨時表同樣能夠保存「數據前影」,起到相似 Oracle 的 undo 表空間的功能,能採用臨時表提升併發性能的,不要用 nolock。


1九、常見的簡化規則以下:


MySQL 中不要有超過 5 個以上的錶鏈接(JOIN),考慮使用臨時表或表變量存放中間結果。少用子查詢,視圖嵌套不要過深,通常視圖嵌套不要超過 2 個爲宜。


20、將須要查詢的結果預先計算好放在表中,查詢的時候再 Select。這在 SQL7.0 之前是最重要的手段,例如醫院的住院費計算。


2一、用 OR 的字句能夠分解成多個查詢,而且經過 UNION 鏈接多個查詢。他們的速度只同是否使用索引有關,若是查詢須要用到聯合索引,用 UNION all 執行的效率更高。多個 OR 的字句沒有用到索引,改寫成 UNION 的形式再試圖與索引匹配。一個關鍵的問題是否用到索引。


2二、在 IN 後面值的列表中,將出現最頻繁的值放在最前面,出現得最少的放在最後面,減小判斷的次數。


2三、儘可能將數據的處理工做放在服務器上,減小網絡的開銷,如使用存儲過程。存儲過程是編譯好、優化過、而且被組織到一個執行規劃裏、且存儲在數據庫中的 SQL 語句,是控制流語言的集合,速度固然快。反覆執行的動態 SQL,可使用臨時存儲過程,該過程(臨時表)被放在 Tempdb 中。


2四、當服務器的內存夠多時,配製線程數量 = 最大鏈接數+5,這樣能發揮最大的效率;不然使用配製線程數量< 最大鏈接數,啓用 SQL SERVER 的線程池來解決,若是仍是數量 = 最大鏈接數+5,嚴重的損害服務器的性能。


2五、查詢的關聯同寫的順序 :

select a.personMemberID, * from chineseresume a,personmember bwhere personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681'; (A = B, B = '號碼') 
select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' and b.referenceid = 'JCNPRH39681'; (A = B, B = '號碼', A = '號碼') select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681'; (B = '號碼', A = '號碼')


2六、儘可能使用 EXISTS 代替 select count(1) 來判斷是否存在記錄。count 函數只有在統計表中全部行數時使用,並且 count(1) 比 count(*) 更有效率。


2七、儘可能使用 「>=」,不要使用 「>」。


2八、索引的使用規範:


  • 索引的建立要與應用結合考慮,建議大的 OLTP 表不要超過 6 個索引;

  • 儘量的使用索引字段做爲查詢條件,尤爲是聚簇索引,必要時能夠經過 index index_name 來強制指定索引;

  • 避免對大表查詢時進行 table scan,必要時考慮新建索引;

  • 在使用索引字段做爲條件時,若是該索引是聯合索引,那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用;

  • 要注意索引的維護,週期性重建索引,從新編譯存儲過程。  


2九、下列 SQL 條件語句中的列都建有恰當的索引,但執行速度卻很是慢:

SELECT * FROM record WHERE substrINg(card_no, 14) = '5378';   --13秒 SELECT * FROM record WHERE amount/30 < 1000--11秒 SELECT * FROM record WHERE convert(char(10), date112) = '19991201'--10秒


分析: 

WHERE 子句中對列的任何操做結果都是在 SQL 運行時逐列計算獲得的,所以它不得不進行表搜索,而沒有使用該列上面的索引。


若是這些結果在查詢編譯時就能獲得,那麼就能夠被 SQL 優化器優化,使用索引,避免表搜索,所以將 SQL 重寫成下面這樣:

SELECT * FROM record WHERE card_no like '5378%' -- < 1秒 SELECT * FROM record WHERE amount < 1000*30 -- < 1秒 SELECT * FROM record WHERE date = '1999/12/01' -- < 1秒


30、當有一批處理的插入或更新時,用批量插入或批量更新,毫不會一條條記錄的去更新。


3一、在全部的存儲過程當中,可以用 SQL 語句的,我毫不會用循環去實現。

例如:列出上個月的每一天,我會用 connect by 去遞歸查詢一下,毫不會去用循環從上個月第一天到最後一天。


3二、選擇最有效率的表名順序(只在基於規則的優化器中有效): 


Oracle 的解析器按照從右到左的順序處理 FROM 子句中的表名,FROM 子句中寫在最後的表(基礎表 driving table)將被最早處理,在 FROM 子句中包含多個表的狀況下,你必須選擇記錄條數最少的表做爲基礎表。


若是有 3 個以上的錶鏈接查詢,那就須要選擇交叉表(intersection table)做爲基礎表,交叉表是指那個被其餘表所引用的表。


3三、提升 GROUP BY 語句的效率,能夠經過將不須要的記錄在 GROUP BY 以前過濾掉。下面兩個查詢返回相同結果,但第二個明顯就快了許多。 


低效:

SELECT JOB, AVG(SAL) FROM EMP GROUP BY JOB HAVING JOB = 'PRESIDENT'OR JOB = 'MANAGER';

高效:

SELECT JOB, AVG(SAL) FROM EMPWHERE JOB = 'PRESIDENT'OR JOB = 'MANAGER'GROUP BY JOB;


3四、SQL 語句用大寫,由於 Oracle 老是先解析 SQL 語句,把小寫的字母轉換成大寫的再執行。

3五、別名的使用,別名是大型數據庫的應用技巧,就是表名、列名在查詢中以一個字母爲別名,查詢速度要比建鏈接錶快 1.5 倍。


3六、避免死鎖,在你的存儲過程和觸發器中訪問同一個表時老是以相同的順序;事務應經可能地縮短,在一個事務中應儘量減小涉及到的數據量;永遠不要在事務中等待用戶輸入。


3七、避免使用臨時表,除非卻有須要,不然應儘可能避免使用臨時表,相反,可使用表變量代替。大多數時候(99%),表變量駐紮在內存中,所以速度比臨時表更快,臨時表駐紮在 TempDb 數據庫中,所以臨時表上的操做須要跨數據庫通訊,速度天然慢。


3八、最好不要使用觸發器:


  • 觸發一個觸發器,執行一個觸發器事件自己就是一個耗費資源的過程;

  • 若是可以使用約束實現的,儘可能不要使用觸發器;

  • 不要爲不一樣的觸發事件(Insert、Update 和 Delete)使用相同的觸發器;

  • 不要在觸發器中使用事務型代碼。


3九、索引建立規則: 


  • 表的主鍵、外鍵必須有索引; 

  • 數據量超過 300 的表應該有索引; 

  • 常常與其餘表進行鏈接的表,在鏈接字段上應該創建索引; 

  • 常常出如今 WHERE 子句中的字段,特別是大表的字段,應該創建索引; 

  • 索引應該建在選擇性高的字段上; 

  • 索引應該建在小字段上,對於大的文本字段甚至超長字段,不要建索引; 

  • 複合索引的創建須要進行仔細分析,儘可能考慮用單字段索引代替; 

  • 正確選擇複合索引中的主列字段,通常是選擇性較好的字段; 

  • 複合索引的幾個字段是否常常同時以 AND 方式出如今 WHERE 子句中?單字段查詢是否極少甚至沒有?若是是,則能夠創建複合索引;不然考慮單字段索引; 

  • 若是複合索引中包含的字段常常單獨出如今 WHERE 子句中,則分解爲多個單字段索引; 

  • 若是複合索引所包含的字段超過 3 個,那麼仔細考慮其必要性,考慮減小複合的字段; 

  • 若是既有單字段索引,又有這幾個字段上的複合索引,通常能夠刪除複合索引; 

  • 頻繁進行數據操做的表,不要創建太多的索引; 

  • 刪除無用的索引,避免對執行計劃形成負面影響; 

  • 表上創建的每一個索引都會增長存儲開銷,索引對於插入、刪除、更新操做也會增長處理上的開銷。另外,過多的複合索引,在有單字段索引的狀況下,通常都是沒有存在價值的;相反,還會下降數據增長刪除時的性能,特別是對頻繁更新的表來講,負面影響更大。 

  • 儘可能不要對數據庫中某個含有大量重複的值的字段創建索引。


40、MySQL 查詢優化總結:


使用慢查詢日誌去發現慢查詢,使用執行計劃去判斷查詢是否正常運行,老是去測試你的查詢看看是否他們運行在最佳狀態下。


長此以往性能總會變化,避免在整個表上使用 count(*),它可能鎖住整張表,使查詢保持一致以便後續類似的查詢可使用查詢緩存,在適當的情形下使用 GROUP BY 而不是 DISTINCT,在 WHERE、GROUP BY 和 ORDER BY 子句中使用有索引的列,保持索引簡單,不在多個索引中包含同一個列。


有時候 MySQL 會使用錯誤的索引,對於這種狀況使用 USE INDEX,檢查使用 SQL_MODE=STRICT 的問題,對於記錄數小於5的索引字段,在 UNION 的時候使用LIMIT不是是用OR。 


爲了不在更新前 SELECT,使用 INSERT ON DUPLICATE KEY 或者 INSERT IGNORE;不要用 UPDATE 去實現,不要使用 MAX;使用索引字段和 ORDER BY子句 LIMIT M,N 實際上能夠減緩查詢在某些狀況下,有節制地使用,在 WHERE 子句中使用 UNION 代替子查詢,在從新啓動的 MySQL,記得來溫暖你的數據庫,以確保數據在內存和查詢速度快,考慮持久鏈接,而不是多個鏈接,以減小開銷。


基準查詢,包括使用服務器上的負載,有時一個簡單的查詢能夠影響其餘查詢,當負載增長在服務器上,使用 SHOW PROCESSLIST 查看慢的和有問題的查詢,在開發環境中產生的鏡像數據中測試的全部可疑的查詢。


4一、MySQL 備份過程:


  • 從二級複製服務器上進行備份;

  • 在進行備份期間中止複製,以免在數據依賴和外鍵約束上出現不一致;

  • 完全中止 MySQL,從數據庫文件進行備份;

  • 若是使用 MySQL dump 進行備份,請同時備份二進制日誌文件 – 確保複製沒有中斷;

  • 不要信任 LVM 快照,這極可能產生數據不一致,未來會給你帶來麻煩;

  • 爲了更容易進行單表恢復,以表爲單位導出數據——若是數據是與其餘表隔離的。 

  • 當使用 mysqldump 時請使用 –opt;

  • 在備份以前檢查和優化表;

  • 爲了更快的進行導入,在導入時臨時禁用外鍵約束。;

  • 爲了更快的進行導入,在導入時臨時禁用惟一性檢測;

  • 在每一次備份後計算數據庫,表以及索引的尺寸,以便更夠監控數據尺寸的增加;

  • 經過自動調度腳本監控複製實例的錯誤和延遲;

  • 按期執行備份。


4二、查詢緩衝並不自動處理空格,所以,在寫 SQL 語句時,應儘可能減小空格的使用,尤爲是在 SQL 首和尾的空格(由於查詢緩衝並不自動截取首尾空格)。


4三、member 用 mid 作標準進行分表方便查詢麼?通常的業務需求中基本上都是以 username 爲查詢依據,正常應當是 username 作 hash 取模來分表。


而分表的話 MySQL 的 partition 功能就是幹這個的,對代碼是透明的;在代碼層面去實現貌似是不合理的。


4四、咱們應該爲數據庫裏的每張表都設置一個 ID 作爲其主鍵,並且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設置上自動增長的 AUTO_INCREMENT 標誌。


4五、在全部的存儲過程和觸發器的開始處設置 SET NOCOUNT ON,在結束時設置 SET NOCOUNT OFF。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。


4六、MySQL 查詢能夠啓用高速查詢緩存。這是提升數據庫性能的有效MySQL優化方法之一。當同一個查詢被執行屢次時,從緩存中提取數據和直接從數據庫中返回數據快不少。


4七、EXPLAIN SELECT 查詢用來跟蹤查看效果:


使用 EXPLAIN 關鍵字可讓你知道 MySQL 是如何處理你的 SQL 語句的。這能夠幫你分析你的查詢語句或是表結構的性能瓶頸。EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的。


4八、當只要一行數據時使用 LIMIT 1 :


當你查詢表的有些時候,你已經知道結果只會有一條結果,但由於你可能須要去 fetch 遊標,或是你也許會去檢查返回的記錄數。


在這種狀況下,加上 LIMIT 1 能夠增長性能。這樣一來,MySQL 數據庫引擎會在找到一條數據後中止搜索,而不是繼續日後查少下一條符合記錄的數據。


4九、選擇表合適存儲引擎: 


  • myisam:應用時以讀和插入操做爲主,只有少許的更新和刪除,而且對事務的完整性,併發性要求不是很高的。 

  • InnoDB:事務處理,以及併發條件下要求數據的一致性。除了插入和查詢外,包括不少的更新和刪除。(InnoDB 有效地下降刪除和更新致使的鎖定)。

    對於支持事務的 InnoDB類 型的表來講,影響速度的主要緣由是 AUTOCOMMIT 默認設置是打開的,並且程序沒有顯式調用 BEGIN 開始事務,致使每插入一條都自動提交,嚴重影響了速度。能夠在執行 SQL 前調用 begin,多條 SQL 造成一個事物(即便 autocommit 打開也能夠),將大大提升性能。


50、優化表的數據類型,選擇合適的數據類型: 


原則:更小一般更好,簡單就好,全部字段都得有默認值,儘可能避免 NULL。 


例如:數據庫表設計時候更小的佔磁盤空間儘量使用更小的整數類型。(mediumint 就比 int 更合適) 


好比時間字段:datetime 和 timestamp。datetime 佔用8個字節,timestamp 佔用4個字節,只用了一半。而 timestamp 表示的範圍是 1970—2037 適合作更新時間。


MySQL能夠很好的支持大數據量的存取,可是通常說來,數據庫中的表越小,在它上面執行的查詢也就會越快。 所以,在建立表的時候,爲了得到更好的性能,咱們能夠將表中字段的寬度設得儘量小。


例如:在定義郵政編碼這個字段時,若是將其設置爲 CHAR(255),顯然給數據庫增長了沒必要要的空間。甚至使用VARCHAR 這種類型也是多餘的,由於 CHAR(6) 就能夠很好的完成任務了。


一樣的,若是能夠的話,咱們應該使用 MEDIUMINT 而不是 BIGIN 來定義整型字段,應該儘可能把字段設置爲 NOT NULL,這樣在未來執行查詢的時候,數據庫不用去比較 NULL 值。 


對於某些文本字段,例如「省份」或者「性別」,咱們能夠將它們定義爲 ENUM 類型。由於在 MySQL 中,ENUM 類型被看成數值型數據來處理,而數值型數據被處理起來的速度要比文本類型快得多。這樣,咱們又能夠提升數據庫的性能。


5一、字符串數據類型:char, varchar, text 選擇區別。


5二、任何對列的操做都將致使表掃描,它包括數據庫函數、計算表達式等等,查詢時要儘量將操做移至等號右邊。

本文轉自:SimpleWu, 連接:cnblogs.com/SimpleWu/p/9929043.html 略有修改,文中 PPT 來自羅老師 2020 嘉年華技術分享。


以上,學習之。今天的分享就到這裏了,若是本文對您有一丁點兒幫助,請多支持「在看」與轉發,不求小費了哪怕是一個小小的贊,您的鼓勵都將是我熬夜寫文章最大的動力,讓我有一直寫下去的動力,最後一塊兒加油,奧利給!

————————————————————————————
公衆號:JiekeXu之路
墨天輪:https://www.modb.pro/u/4347
CSDN :https://blog.csdn.net/JiekeXu
騰訊雲:https://cloud.tencent.com/developer/user/5645107
————————————————————————————



Oracle 12c 及以上版本補丁更新說明及下載方法(收藏版)

Oracle 21C 新特性:數據泵相關新特性彙總

使用數據泵導出時遇到 ORA-27054 錯誤解決辦法

案例|RAC 添加表空間誤將數據文件放本地處理辦法

11g RAC 在線存儲遷移實現 OCR 磁盤組完美替換

震驚:Oracle 11gR2 RAC ADG 並無高可用

如何經過 Shell 監控異常等待事件和活躍會話 

個人 OCM 之路|書寫無悔青春追夢永不止步

Oracle 19c 之多租戶 PDB 鏈接與訪問(三)

Oracle 12C 最新補丁下載與安裝操做指北

DBA 經常使用的軟件工具備哪些(分享篇)?

深刻了解 Oracle Flex ASM 及其優勢

Oracle 11g 臨時表空間管理

Oracle 每日一題系列合集


本文分享自微信公衆號 - JiekeXu之路(JiekeXu_IT)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索