當面試官問:怎麼優化sql?

今天,我就來和你們講講要怎麼回答這道問題。首先,咱們要穩住不要慌,本身是本身親手作的項目,第一個問題應該都不大,第二個問題就須要在面試以前作好充分的準備啦…面試

在回答問題以前先要了解查詢的流程:查詢是由一系列的子任務組成的,包括從客戶端,到服務器,而後在服務器上進行解析,生成執行計劃,執行,並返回結果給客戶端。其中「執行」能夠認爲是整個生命週期中最重要的階段,這其中包括了大量爲了檢索數據到存儲引擎的調用以及調用後的數據處理,包括排序、分組。爲了完成這些任務,查詢須要在不一樣的地方花費時間,包括網絡,CPU計算,生成統計信息和執行計劃、鎖等待操做。進行一些沒必要要的額外操做時或者某些重複執行某些額外操做會消耗大量的時間。數據庫

查詢性能低下最基本的緣由是訪問的數據太多。某些查詢可能不可避免地須要篩選大量的數據,大部分性能低下的查詢均可以經過減小訪問的數據量的方式進行優化。對於低效的查詢,能夠經過如下兩個步驟來分析:緩存

  • 確認應用程序是否在檢索大量超過須要的數據。
  • 確認MySQL服務器是否在分析大量超過須要的數據行。

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

SQL語句的優化:

一、少用子查詢
儘可能少用子查詢,由於子查詢會產生臨時表;除非像count(*)臨時表很小的。網絡

二、少用SELECT *
每次看到SELECT *都須要用懷疑的眼光審視,是否真的須要返回所有的列?取出所有的列,會讓優化器沒法完成索引覆蓋掃描這類優化,還會爲服務器帶來額外的I/O、內存和CPU的消耗。函數

三、查詢必要的記錄
一個常見的錯誤是經常會誤覺得MySQL只會返回須要的數據,實際上MySQL倒是先返回所有結果集再進行計算,建議在查詢後面加上LIMIT。性能

四、不要重複查詢相同的數據
不斷執行相同的查詢,而後每次都會返回徹底相同的數據。能夠採用的方案是初次查詢的時候將這個數據緩存起來,須要的時候從緩存中取出,這樣性能顯然會更好。優化

五、COUNT查詢優化
COUNT()聚合函數的做用:統計某一個列值的數量,也能夠統計行數。須要注意的是統計列值時要求列值是非空的(不統計NULL),COUNT()查詢儘量少的行。設計

舉個例子:若是咱們直接查 id>100 的記錄,涉及到的有兩千多萬行記錄掃描。可是因爲COUNT()特性,咱們能夠用 count() - (id<100)的作法,這樣掃描的行就只有100行了。排序

六、Where子句中,where表之間的鏈接必須寫在其餘Where條件以前,那些能夠過濾掉最大數量記錄的條件必須寫在Where子句的末尾.HAVING最後。

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

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

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

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

十一、應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描。

十二、應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。

索引優化

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

二、GROUP BY 和 DISTINCT優化
GROUP BY 和 DISTINCT的優化最有效的就是使用索引。全部對於分組的列必定要創建索引。好比:

select product, count(*) from orders group by product;
這樣的一個查詢,對product要創建索引。

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

注意索引失效的狀況,
1)以「%」開頭的LIKE語句,模糊匹配
2)OR語句先後沒有同時使用索引
3)數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換爲int型)

數據庫優化

選擇優化數據類型的幾條建議:

  • 更小的一般更好,儘可能使用能夠正確存儲數據的最小數據類型,由於佔用更少的磁盤、內存和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)來代替手動建立的臨時表
  • 鎖定表、優化事務處理
相關文章
相關標籤/搜索