IO永遠是數據庫最容易瓶頸的地方,這是由數據庫的職責所決定的,大部分數據庫操做中超過90%的時間都是 IO 操做所佔用的,減小 IO 次數是 SQL 優化中須要第一優先考慮,固然,也是收效最明顯的優化手段。mysql
除了 IO 瓶頸以外,SQL優化中須要考慮的就是 CPU 運算量的優化了。order by, group by,distinct … 都是消耗 CPU 的大戶(這些操做基本上都是 CPU 處理內存中的數據比較運算)。當咱們的 IO 優化作到必定階段以後,下降 CPU 計算也就成爲了咱們 SQL 優化的重要目標。sql
明確了優化目標以後,咱們須要肯定達到咱們目標的方法。對於 SQL 語句來講,達到上述2個目標的方法其實只有一個,那就是改變 SQL 的執行計劃,讓他儘可能「少走彎路」,儘可能經過各類「捷徑」來找到咱們須要的數據,以達到 「減小 IO 次數」 和 「下降 CPU 計算」 的目標。數據庫
explain sql;緩存
對於徹底相同的sql,使用已經存在的執行計劃,從而跳過解析和生成執行計劃的過程。服務器
應用場景:不常常變的表,服務器收到該表的大量相同查詢。查詢必須是徹底相同的(逐字節相同)纔可以被認爲是相同的併發
Mysql 判斷是否命中緩存的辦法很簡單,首先會將要緩存的結果放在引用表中,而後使用查詢語句,數據庫名稱,客戶端協議的版本等因素算出一個hash值,這個hash值與引用表中的結果相關聯。若是在執行查詢時,根據一些相關的條件算出的hash值能與引用表中的數據相關聯,則表示查詢命中。函數
緩存機制失效高併發
額外的消耗優化
區分in和existspa
select * from 表A where id in (select id from 表B)
這句至關於
select * from 表A where exists(select * from 表B where 表B.id=表A.id)
exists:對於表A的每一條數據,都執行select * from 表B where 表B.id=表A.id的存在性判斷,若是表B中存在表A當前行相同的id,則exists爲真,該行顯示,不然不顯示。
IN適合於外表大而內表小的狀況;EXISTS適合於外表小而內表大的狀況。
儘可能用join代替子查詢
MySQL須要爲內層查詢語句的查詢結果創建一個臨時表。而後外層查詢語句在臨時表中查詢記錄。查詢完畢後,MySQL須要插銷這些臨時表。因此在MySQL中可使用鏈接查詢來代替子查詢。鏈接查詢不須要創建臨時表,其速度比子查詢要快。
儘可能少排序
排序操做會消耗較多的 CPU 資源,因此減小排序能夠在緩存命中率高等 IO 能力足夠的場景下會較大影響 SQL 的響應時間。
減小參與排序的記錄條數
非必要不對數據進行排序
儘可能避免select *
儘可能少or
不少時候使用 union all 或者是union(必要的時候)的方式來代替「or」會獲得更好的效果。
儘可能用 union all 代替 union
union 和 union all 的差別主要是前者須要將兩個(或者多個)結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的 CPU 運算,加大資源消耗及延遲。因此當咱們能夠確認不可能出現重複結果集或者不在意重複結果集的時候,儘可能使用 union all 而不是 union。
儘可能早過濾
在 SQL 編寫中一樣可使用這一原則來優化一些 Join 的 SQL。好比咱們在多個表進行分頁數據查詢的時候,咱們最好是可以在一個表上先過濾好數據分好頁,而後再用分好頁的結果集與另外的表 Join,這樣能夠儘量多的減小沒必要要的 IO 操做,大大節省 IO 操做所消耗的時間。
優先優化高併發的 SQL,而不是執行頻率低某些「大」SQL
對於破壞性來講,高併發的 SQL 老是會比低頻率的來得大,由於高併發的 SQL 一旦出現問題,甚至不會給咱們任何喘息的機會就會將系統壓跨。而對於一些雖然須要消耗大量 IO 並且響應很慢的 SQL,因爲頻率低,即便遇到,最多就是讓整個系統響應慢一點,但至少可能撐一下子,讓咱們有緩衝的機會。
儘可能避免where子句中對字段進行null值的判斷
會致使引擎放棄索引,進而進行全表掃描。
儘可能不要給數據庫留null值,儘量地使用not null填充數據庫。能夠爲每一個null型的字段設置一個和null對應的實際內容表述。
避免在where中使用!=, >, <操做符
不然引擎放棄使用索引,進行全表掃描
經常使用查詢字段建索引
in和not in關鍵詞慎用,容易致使全表掃面
避免在where子句中對字段進行表達式操做或進行函數操做
會致使引擎放棄使用索引
選擇最有效率的表名順序(只在基於規則的優化器中有效)
Oracle的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)將被最早處理,在FROM子句中包含多個表的狀況下,你必須選擇記錄條數最少的表做爲基礎表。若是有3個以上的錶鏈接查詢, 那就須要選擇交叉表(intersection table)做爲基礎表, 交叉表是指那個被其餘表所引用的表。
WHERE子句中的鏈接順序
Oracle採用自下而上的順序解析WHERE子句,根據這個原理,表之間的鏈接必須寫在其餘WHERE條件以前, 那些能夠過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾。