mysql 優化步驟

1.通過show status 命令瞭解各種sql的執行效率

  SHOW STATUS提供msyql服務器的狀態信息

  一般情況下,我們只需要瞭解以”Com”開頭的指令

  show session status like ‘Com%’:顯示當前的連接的統計結果

  show global status like ‘Com%’ :顯示自數據庫上次啓動至今的統計結果

  注:默認是session級別的

  其中Com_XXX表示XXX語句所執行的次數。
重點注意:Com_select,Com_insert,Com_update,Com_delete通過這幾個參數,可以容易地瞭解到當前數據庫的應用是以插入更新爲主還是以查詢操作爲主,以及各類的SQL大致的執行比例是多少。

  另外,還有幾個參數需要注意下:

  show status like ‘Connections’// 試圖連接MySQL服務器的次數

  show status like ‘Uptime’//服務器工作的時間(單位秒)

  show status like ‘Slow_queries’//慢查詢的次數 (默認是10秒中就當做是慢查詢,如下圖所示)

  

  a) 如何查詢mysql的慢查詢時間

    Show variables like 'long_query_time';

  b) 修改mysql 慢查詢時間

    set long_query_time=2//如果查詢時間超過2秒就算作是慢查詢

2. 定位執行效率較低的SQL語句(dql出現問題的概率較dml的大)

  問題是:如何在一個項目中,找到慢查詢的select語句?

  答案:mysql支持把慢查詢語句記錄到日誌文件中。程序員需要修改php.ini的配置文件,默認情況下,慢查詢記錄是不開啓的。

  開啓慢查詢記錄的步驟:

  打開 my.ini ,找到 [mysqld] 在其下面添加

  long_query_time = 2

  log-slow-queries = D:/mysql/logs/slow.log #設置把日誌寫在那裏,可以爲空,系統會給一個缺省的文件

     例子:我們數據表中有1千萬條的數據量

  DQL語句:SELECT * FROM order_copy WHERE id=12345;

  

  查詢耗時:19s>2s,所以mysql會將該條select語句記錄到慢查詢日誌中

  SELECT * FROM order_copy WHERE id=12345的執行時間:

  添加索引前:19s

  添加索引後:0.08s

3.通過explain分析低效率的SQL語句的執行情況

  使用explain分析該dql語句:

EXPLAIN SELECT * FROM order_copy WHERE id=12345
會產生如下信息:
select_type:表示查詢的類型。
table:輸出結果集的表
type:表示表的連接類型(system和const爲佳)
possible_keys:表示查詢時,可能使用的索引
key:表示實際使用的索引
key_len:索引字段的長度
rows:掃描的行數
Extra:執行情況的描述和說明

注意:要儘量避免讓type的結果爲all,extra的結果爲:using filesort

4.確定問題並採取相應的優化措施

  • 常用的優化措施是添加索引。添加索引,我們不用加內存,不用改程序,不用調sql,只要執行個正確的’create index’,查詢速度就可能提高百倍千倍。但是天下沒有免費的午餐,查詢速度的提高是以插入、更新、刪除的速度爲代價的,這些寫操作,增加了大量的I/O。

例如:給字段id添加索引:

ALTER TABLE order_copy ADD PRIMARY KEY(id)

給1千萬的數據添加primary key 需要耗時: 428秒(7分鐘)

EXPLAIN SELECT * FROM order_copy WHERE id=12345

正是因爲給id添加了索引,才使得rows的結果爲1

但是索引並不是可以隨便添加的,以下幾種情況需牢記在心:

  • 較頻繁的作爲查詢條件字段應該創建索引

    select * from order_copy where id = $id

  • 唯一性太差的字段不適合單獨創建索引,即使頻繁作爲查詢條件

   select * from order_copy where sex=’女’

  • 更新非常頻繁的字段不適合創建索引

    select * from order_copy where order_state=’未付款’

  • 不會出現在WHERE子句中字段不該創建索引

索引的類型:

  • PRIMARY 索引      => 在主鍵上自動創建
  • INDEX 索引          => 就是普通索引
  • UNIQUE 索引        => 相當於INDEX + Unique
  • FULLTEXT            => 只在MYISAM 存儲引擎支持, 目的是全文索引,在內容系統中用的多, 在全英文網站用多(英文詞獨立). 中文數據不常用,意義不大 國內全文索引通常 使用 sphinx 來完成.

索引的使用

  • 建立索引 create [UNIQUE|FULLTEXT]  index index_name on tbl_name (col_name [(length)] [ASC | DESC] , …..);
    alter table table_name ADD INDEX [index_name] (index_col_name,...)

    添加主鍵(索引) ALTER TABLE 表名 ADD PRIMARY KEY(列名,..); 聯合主鍵

  • 刪除索引 DROP INDEX index_name ON tbl_name;
    alter table table_name drop index index_name;
  • 刪除主鍵(索引)比較特別: alter table t_b drop primary key;
  • 查詢索引(均可) show index from table_name;
    show keys from table_name;

  • desc table_Name;



優化目標

  1.減少 IO 次數

  IO永遠是數據庫最容易瓶頸的地方,這是由數據庫的職責所決定的,大部分數據庫操作中超過90%的時間都是 IO 操作所佔用的,減少 IO 次數是 SQL 優化中需要第一優先考慮,當然,也是收效最明顯的優化手段。

  2.降低 CPU 計算

  除了 IO 瓶頸之外,SQL優化中需要考慮的就是 CPU 運算量的優化了。order by, group by,distinct … 都是消耗 CPU 的大戶(這些操作基本上都是 CPU 處理內存中的數據比較運算)。當我們的 IO 優化做到一定階段之後,降低 CPU 計算也就成爲了我們 SQL 優化的重要目標

 

優化方法

  改變 SQL 執行計劃

  明確了優化目標之後,我們需要確定達到我們目標的方法。對於 SQL 語句來說,達到上述2個目標的方法其實只有一個,那就是改變 SQL 的執行計劃,讓他儘量“少走彎路”,儘量通過各種“捷徑”來找到我們需要的數據,以達到 “減少 IO 次數” 和 “降低 CPU 計算” 的目標

 

常見誤區

 

1.count(1)和count(primary_key) 優於 count(*)

  很多人爲了統計記錄條數,就使用 count(1) 和 count(primary_key) 而不是 count(*) ,他們認爲這樣性能更好,其實這是一個誤區。對於有些場景,這樣做可能性能會更差,應爲數據庫對 count(*) 計數操作做了一些特別的優化。

 

2.count(column) 和 count(*) 是一樣的

  這個誤區甚至在很多的資深工程師或者是 DBA 中都普遍存在,很多人都會認爲這是理所當然的。實際上,count(column) 和 count(*) 是一個完全不一樣的操作,所代表的意義也完全不一樣。

  count(column) 是表示結果集中有多少個column字段不爲空的記錄

  count(*) 是表示整個結果集有多少條記錄

 

3.select a,b from … 比 select a,b,c from … 可以讓數據庫訪問更少的數據量

  這個誤區主要存在於大量的開發人員中,主要原因是對數據庫的存儲原理不是太瞭解。

  實際上,大多數關係型數據庫都是按照行(row)的方式存儲,而數據存取操作都是以一個固定大小的IO單元(被稱作 block 或者 page)爲單位,一般爲4KB,8KB… 大多數時候,每個IO單元中存儲了多行,每行都是存儲了該行的所有字段(lob等特殊類型字段除外)。

  所以,我們是取一個字段還是多個字段,實際上數據庫在表中需要訪問的數據量其實是一樣的。

  當然,也有例外情況,那就是我們的這個查詢在索引中就可以完成,也就是說當只取 a,b兩個字段的時候,不需要回表,而c這個字段不在使用的索引中,需要回表取得其數據。在這樣的情況下,二者的IO量會有較大差異。

 

4.order by 一定需要排序操作

  我們知道索引數據實際上是有序的,如果我們的需要的數據和某個索引的順序一致,而且我們的查詢又通過這個索引來執行,那麼數據庫一般會省略排序操作,而直接將數據返回,因爲數據庫知道數據已經滿足我們的排序需求了。

  實際上,利用索引來優化有排序需求的 SQL,是一個非常重要的優化手段

  延伸閱讀:MySQL ORDER BY 的實現分析,MySQL 中 GROUP BY 基本實現原理以及 MySQL DISTINCT 的基本實現原理這3篇文章中有更爲深入的分析,尤其是第一篇

 

5.執行計劃中有 filesort 就會進行磁盤文件排序

  有這個誤區其實並不能怪我們,而是因爲 MySQL 開發者在用詞方面的問題。filesort 是我們在使用 explain 命令查看一條 SQL 的執行計劃的時候可能會看到在 “Extra” 一列顯示的信息。

  實際上,只要一條 SQL 語句需要進行排序操作,都會顯示“Using filesort”,這並不表示就會有文件排序操作。

 

基本原則

1.儘量少 join

  MySQL 的優勢在於簡單,但這在某些方面其實也是其劣勢。MySQL 優化器效率高,但是由於其統計信息的量有限,優化器工作過程出現偏差的可能性也就更多。對於複雜的多表 Join,一方面由於其優化器受限,再者在 Join 這方面所下的功夫還不夠,所以性能表現離 Oracle 等關係型數據庫前輩還是有一定距離。但如果是簡單的單表查詢,這一差距就會極小甚至在有些場景下要優於這些數據庫前輩。

 

2.儘量少排序

  排序操作會消耗較多的 CPU 資源,所以減少排序可以在緩存命中率高等 IO 能力足夠的場景下會較大影響 SQL 的響應時間。

  對於MySQL來說,減少排序有多種辦法,比如:

  上面誤區中提到的通過利用索引來排序的方式進行優化

  減少參與排序的記錄條數

  非必要不對數據進行排序

  …

 

3.儘量避免 select *

  很多人看到這一點後覺得比較難理解,上面不是在誤區中剛剛說 select 子句中字段的多少並不會影響到讀取的數據嗎?

  是的,大多數時候並不會影響到 IO 量,但是當我們還存在 order by 操作的時候,select 子句中的字段多少會在很大程度上影響到我們的排序效率,這一點可以通過我之前一篇介紹 MySQL ORDER BY 的實現分析的文章中有較爲詳細的介紹。

  此外,上面誤區中不是也說了,只是大多數時候是不會影響到 IO 量,當我們的查詢結果僅僅只需要在索引中就能找到的時候,還是會極大減少 IO 量的。

 

4.儘量用 join 代替子查詢

  雖然 Join 性能並不佳,但是和 MySQL 的子查詢比起來還是有非常大的性能優勢。MySQL 的子查詢執行計劃一直存在較大的問題,雖然這個問題已經存在多年,但是到目前已經發布的所有穩定版本中都普遍存在,一直沒有太大改善。雖然官方也在很早就承認這一問題,並且承諾儘快解決,但是至少到目前爲止我們還沒有看到哪一個版本較好的解決了這一問題。

 

5.儘量少 or

  當 where 子句中存在多個條件以“或”並存的時候,MySQL 的優化器並沒有很好的解決其執行計劃優化問題,再加上 MySQL 特有的 SQL 與 Storage 分層架構方式,造成了其性能比較低下,很多時候使用 union all 或者是union(必要的時候)的方式來代替“or”會得到更好的效果。

 

6.儘量用 union all 代替 union

  union 和 union all 的差異主要是前者需要將兩個(或者多個)結果集合並後再進行唯一性過濾操作,這就會涉及到排序,增加大量的 CPU 運算,加大資源消耗及延遲。所以當我們可以確認不可能出現重複結果集或者不在乎重複結果集的時候,儘量使用 union all 而不是 union。

 

7.儘量早過濾

  這一優化策略其實最常見於索引的優化設計中(將過濾性更好的字段放得更靠前)。

  在 SQL 編寫中同樣可以使用這一原則來優化一些 Join 的 SQL。比如我們在多個表進行分頁數據查詢的時候,我們最好是能夠在一個表上先過濾好數據分好頁,然後再用分好頁的結果集與另外的表 Join,這樣可以儘可能多的減少不必要的 IO 操作,大大節省 IO 操作所消耗的時間。

 

8.避免類型轉換

  這裏所說的“類型轉換”是指 where 子句中出現 column 字段的類型和傳入的參數類型不一致的時候發生的類型轉換:

  人爲在column_name 上通過轉換函數進行轉換

  直接導致 MySQL(實際上其他數據庫也會有同樣的問題)無法使用索引,如果非要轉換,應該在傳入的參數上進行轉換

  由數據庫自己進行轉換

  如果我們傳入的數據類型和字段類型不一致,同時我們又沒有做任何類型轉換處理,MySQL 可能會自己對我們的數據進行類型轉換操作,也可能不進行處理而交由存儲引擎去處理,這樣一來,就會出現索引無法使用的情況而造成執行計劃問題。

 

9.優先優化高併發的 SQL,而不是執行頻率低某些“大”SQL

  對於破壞性來說,高併發的 SQL 總是會比低頻率的來得大,因爲高併發的 SQL 一旦出現問題,甚至不會給我們任何喘息的機會就會將系統壓跨。而對於一些雖然需要消耗大量 IO 而且響應很慢的 SQL,由於頻率低,即使遇到,最多就是讓整個系統響應慢一點,但至少可能撐一會兒,讓我們有緩衝的機會。

 

10.從全局出發優化,而不是片面調整

  SQL 優化不能是單獨針對某一個進行,而應充分考慮系統中所有的 SQL,尤其是在通過調整索引優化 SQL 的執行計劃的時候,千萬不能顧此失彼,因小失大。

 

11.儘可能對每一條運行在數據庫中的SQL進行 explain

  優化 SQL,需要做到心中有數,知道 SQL 的執行計劃才能判斷是否有優化餘地,才能判斷是否存在執行計劃問題。在對數據庫中運行的 SQL 進行了一段時間的優化之後,很明顯的問題 SQL 可能已經很少了,大多都需要去發掘,這時候就需要進行大量的 explain 操作收集執行計劃,並判斷是否需要進行優化。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章