1. 程式人生 > 資料庫 >面試官:怎麼優化 SQL?

面試官:怎麼優化 SQL?

在面試的環節中,面試官問到:你是如何設計你的表結構的,畫一下E-R圖?接著又繼續深挖,如果有慢查詢,你是如何優化你的sql的?

今天,我就來和大家講講要怎麼回答這道問題。首先,我們要穩住不要慌,自己是自己親手做的專案,第一個問題應該都不大,第二個問題就需要在面試之前做好充分的準備啦…

在回答問題之前先要了解查詢的流程:查詢是由一系列的子任務組成的,包括從客戶端,到伺服器,然後在伺服器上進行解析,生成執行計劃,執行,並返回結果給客戶端。其中“執行”可以認為是整個生命週期中最重要的階段,這其中包括了大量為了檢索資料到儲存引擎的呼叫以及呼叫後的資料處理,包括排序、分組。為了完成這些任務,查詢需要在不同的地方花費時間,包括網路,CPU計算,生成統計資訊和執行計劃、鎖等待操作。進行一些不必要的額外操作時或者某些重複執行某些額外操作會消耗大量的時間。

查詢效能低下最基本的原因是訪問的資料太多。某些查詢可能不可避免地需要篩選大量的資料,大部分效能低下的查詢都可以通過減少訪問的資料量的方式進行優化。對於低效的查詢,可以通過以下兩個步驟來分析:

  • 確認應用程式是否在檢索大量超過需要的資料。

  • 確認MySQL伺服器是否在分析大量超過需要的資料行。

上面的都是理論,在實踐中,MySQL的優化主要涉及SQL語句及索引的優化、資料表結構的優化這三個方面。

 

SQL語句的優化:

1、少用子查詢

儘量少用子查詢,因為子查詢會產生臨時表;除非像count(*)臨時表很小的。

2、少用SELECT *

每次看到SELECT *都需要用懷疑的眼光審視,是否真的需要返回全部的列?取出全部的列,會讓優化器無法完成索引覆蓋掃描這類優化,還會為伺服器帶來額外的I/O、記憶體和CPU的消耗。

3、查詢必要的記錄

一個常見的錯誤是常常會誤以為MySQL只會返回需要的資料,實際上MySQL卻是先返回全部結果集再進行計算,建議在查詢後面加上LIMIT。

4、不要重複查詢相同的資料

不斷執行相同的查詢,然後每次都會返回完全相同的資料。可以採用的方案是初次查詢的時候將這個資料快取起來,需要的時候從快取中取出,這樣效能顯然會更好。

5、COUNT查詢優化

COUNT()聚合函式的作用:統計某一個列值的數量,也可以統計行數。需要注意的是統計列值時要求列值是非空的(不統計NULL),COUNT()查詢儘可能少的行。

舉個例子:如果我們直接查 id>100 的記錄,涉及到的有兩千多萬行記錄掃描。但是由於COUNT()特性,我們可以用 count() - (id<100)的做法,這樣掃描的行就只有100行了。

6、Where子句中,where表之間的連線必須寫在其他Where條件之前,那些可以過濾掉最大數量記錄的條件必須寫在Where子句的末尾.HAVING最後。

7、用EXISTS替代IN、用NOT EXISTS替代NOT IN。

8、避免在索引列上使用計算。

9、避免在索引列上使用IS NULL和IS NOT NULL。

10、對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

11、應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描。

12、應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描。

 

索引優化

1、關聯查詢優化

確保ON 或則USING 子句的列上有索引。建立索引時就要考慮關聯的順序,當表A和表B用列c關聯的時候,如果優化器關聯順序是B、A,就只需要在表A上建立索引,沒用的索引會佔用儲存。

2、GROUP BY 和 DISTINCT優化

GROUP BY 和 DISTINCT的優化最有效的就是使用索引。所有對於分組的列一定要建立索引。比如:

select product, count(*) from orders group by product;

這樣的一個查詢,對product要建立索引。

3、LIMIT分頁優化

進行分頁操作時,通常都會通過偏移量來查詢某些資料。然後再加上解釋的order by,效能一般都不錯。對於order by的列 一定要加上索引。但是對於limit 10000,10 這樣檢索目標10條記錄必須先先查詢前面的10000條記錄。代價很高,這種時候優化最簡單辦法就是使用覆蓋索引。

注意索引失效的情況,

1)以“%”開頭的LIKE語句,模糊匹配

2)OR語句前後沒有同時使用索引

3)資料型別出現隱式轉化(如varchar不加單引號的話可能會自動轉換為int型)

https://www.houdianzi.com

資料庫優化

選擇優化資料型別的幾條建議:

  • 更小的通常更好,儘量使用可以正確儲存資料的最小資料型別,因為佔用更少的磁碟、記憶體和CPU快取。

  • 簡單最好,選擇整數而不是字串,選擇MySQL內建的型別而不是字串來儲存時間和日期,使用整數來儲存IP地址。

  • 儘量避免NULL,很多表都包含可為NULL的列,這是因為NULL是列的預設值,需要指定列為NOT NULL。

  • 整數型別資料一般用int,對於布林型別的資料用tinyint,但是整數計算一般是使用64位的BIGINT整數。

  • 在需要對小數進行精確計算時,比如說儲存財務資料才使用DECIMAL(浮點儲存的float和double型別計算不精確),但是DECIMAL計算的代價很高,可以考慮使用BIGINT代替DECIMAL,將小數的位數乘以相應的倍數即可。

  • varchar和char

    當需要儲存可變長的字串用varchar,比使用char儲存更節省空間,varchar使用1或者2個額外的位元組來記錄長度。至於用char來儲存適用於下列幾種情況,一是需要儲存很短的字串時(儲存只有Y和N的值時),二是所有的值接近固定長度(儲存MD5值),三是經常需要變更的值。

  • BIT

    在MySQL5.0之前,BIT是TINYINT的同義詞,在MySQL5.0以及更新的版本,是一個完全不同的資料型別。BIT型別的新行為:(1)可以使用BIT列在一列中儲存一個或者多個true/false值。MySQL把BIT當做字串型別,而不是數字型別。當檢索BIT(1)的值時,結果是一個包含二進位制0或者1的字串,而不是ASCII的“0”或“1”。

  • SET

    如果需要儲存很多的true/false值,可以考慮合併這些列到一個SET資料型別,它在MySQL內部是一系列打包的位的集合來表示的。

  • 使用列舉代替常用的字串型別,因為MySQL在儲存列舉時非常緊湊,MySQL把每個列舉的值儲存為整數,並且在表的.firm檔案中儲存“數字-字串”對映關係的“查詢表”。

  • DATATIME儲存的範圍更廣,儲存的值從1001年到9999年,精確到秒,與時區無關,使用8個位元組的儲存空間,使用一種可排序、無歧義的格式顯示時間,TIMESTAMP型別儲存了從1970年1月1日午夜以來的秒數,使用4個位元組的儲存空間,只能表示從1970年到2038年,依賴於時區,空間效率更高,推薦使用TIMESTAMP

  • 對於BOLB和TEXT型別他們都是為了儲存很大的資料而設計的字串,分別採用二進位制和字串方式儲存。

  • 不能有太多的列

  • 單個查詢最好在12個表以內做關聯

  • 當遇到未知值的時候不要害怕使用NULL

  • 在實際的應用中需要混用正規化和反正規化,使用部分正規化化的schema、快取表、以及其他的技巧,最常見的反正規化化資料的方法是複製或者快取,在不同的表中儲存相同的特定列。

  • 修改.frm檔案來加快ALTER TABLE 操作的速度

  • 選取最適用的欄位屬性,儘可能減少定義欄位寬度,儘量把欄位設定NOTNULL,例如’省份’、’性別’最好適用ENUM

  • 使用連線(JOIN)來代替子查詢

  • 用聯合(UNION)來代替手動建立的臨時表

  • 鎖定表、優化事務處理