6.1 sql優化步驟
1、通過show status 瞭解各種SQL的執行頻率
show status like 'Com_%'; 顯示當前session中所有統計參數的值
1)適用所有存儲引擎
Com_select 執行select的次數,一次執行累加一
Com_insert 執行insert的次數
Com_update 執行update的次數
Com_delete 執行delete的次數
2)innodb引擎
show status like 'Innodb_rows_%';
Innodb_rows_deleted | 139 執行delete刪除的行數
Innodb_rows_inserted | 5067714 執行insert插入的行數
Innodb_rows_read |1164419790796 執行select 查詢返回的行數
Innodb_rows_updated |2841812 執行update更新的行數
3)show status like 'Slow_queries%'; 慢查詢次數
show status like 'Uptime%'; 服務器工作時間
show status like 'Connections%'; 視圖連接mysql服務器的次數
2、定位執行效率較低的SQL
1)通過慢查詢日誌定位那些執行效率較低的 SQL 語句
用--log-slow-queries[=file_name] 選項啓動時, mysqld 會 寫一個包含所有執行時間超過 long_query_time 秒的 SQL 語句的日誌文件,
通過查看這個日誌文件定位效率較低的 SQL 。慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題的時候查詢慢查詢日誌
並不能定位問題
2)使用 show processlist 命令查看當前 MySQL 在進行的線程,
包括線程的狀態、是否鎖表等,可以實時地查看 SQL 的 執行情況,同時對一些鎖表操作進行優化。
3、使用EXPLAIN分析低效SQL的執行計劃,確定問題並採取優化措施
(1)explain出來的各種item的意義
(2)profile的意義以及使用場景。
(3)explain中的索引問題。
(1) explain出來的各種item的意義
id:每個被獨立執行的操作的標誌,表示對象被操作的順序。一般來說,id值大,先被執行;如果id值相同,則順序從上到下。
select_type:查詢中每個select子句的類型。具體待明天補充。
table:名字,被操作的對象名稱,通常的表名(或者別名),但是也有其他格式。
partitions:匹配的分區信息。
type:join類型。具體指待明天補充。
possible_keys:列出可能會用到的索引。
key:實際用到的索引。
key_len:用到的索引鍵的平均長度,單位爲字節。
ref:表示本行被操作的對象的參照對象,可能是一個常量用const表示,也可能是其他表的key指向的對象,比如說驅動表的連接列。
rows:估計每次需要掃描的行數。
filtered:rows*filtered/100表示該步驟最後得到的行數(估計值)。
extra:重要的補充信息。具體待明天補充。
(2) profile的意義以及使用場景。
Profile用來分析sql性能的消耗分佈情況。當用explain無法解決慢SQL的時候,需要用profile來對sql進行更細緻的分析,找出sql所花的時間大部分消耗在哪個部分,確認sql的性能瓶頸。Profile(查詢到 SQL 會執行多少時間,並看出 CPU/Memory使用量,執行過程中Systemlock, Table lock花多少時間等等.)
(3) explain中的索引問題。
Explain結果中,一般來說,要看到儘量用index(type爲const、ref等,key列有值),避免使用全表掃描(type顯式爲ALL)。比如說有where條件且選擇性不錯的列,需要建立索引。被驅動表的連接列,也需要建立索引。被驅動表的連接列也可能會跟where條件列一起建立資
聯合索引。當有排序或者group by的需求時,也可以考慮建立索引來達到直接排序和彙總的需求。
6.2 cpu飆升500%的優化步驟
當cpu飆升到500%時,先用操作系統命令top命令觀察是不是mysqld佔用導致的,如果不是,找出佔用高的進程,並進行相關處理。如果是mysqld造成的,show processlist,看看裏面跑的session情況,是不是有消耗資源的sql在運行。找出消耗高的sql,看看執行計劃是否準確,index是否缺失,或者實在是數據量太大造成。一般來說,肯定要kill掉這些線程(同時觀察cpu使用率是否下降),等進行相應的調整(比如說加索引、改sql、改內存參數)之後,再重新跑這些SQL。也有可能是每個sql消耗資源並不多,但是突然之間,有大量的session連進來導致cpu飆升,這種情況就需要跟應用一起來分析爲何連接數會激增,再做出相應的調整,比如說限制連接數等。
6.3 mysql如何實現插入時如果不存在則插入如果存在則更新的操作?
1. replace intotbl_name(col_name, …) values(…)
2. replace into tbl_name(col_name, …) select …
3. replace into tbl_name set col_name=value, …
6.4 mysql如何實現高效分頁
先看一下分頁的基本原理(我拿的是CSDN那個百萬級數據庫來測試!):
SELECT* FROM `csdn` ORDER BY id DESC LIMIT 100000,2000;
耗時: 0.813ms
分析:對上面的mysql語句說明:limit 100000,2000的意思掃描滿足條件的102000行,扔掉前面的100000行,返回最後的2000行。
問題就在這裏,如果是limit 100000,20000,需要掃描120000行,在一個高併發的應用裏,每次查詢需要掃描超過100000行,性能肯定大打折扣。
在《efficient pagination using mysql》中提出的clue方式。
利用clue方法,給翻頁提供一些線索,比如還是SELECT * FROM `csdn`order by id desc,按id降序分頁,每頁2000條,當前是第50頁,當前頁條目id最大的是102000,最小的是100000。如果我們只提供上一頁、下一頁這樣的跳轉(不提供到第N頁的跳轉)。
那麼在處理上一頁的時候SQL語句可以是:
SELECT * FROM `csdn` WHERE id
耗時:0.015ms
處理下一頁的時候SQL語句可以是:
SELECT * FROM `csdn` WHERE id>102000 ORDER BY id ASC LIMIT 2000; #下一頁
耗時:0.015ms
這樣,不管翻多少頁,每次查詢只掃描20行。效率大大提高了!
但是,這樣分頁的缺點是隻能提供上一頁、下一頁的鏈接形式。
6.5 一張表,裏面有ID自增主鍵,當insert了17條記錄之後,刪除了第15,16,17條記錄,再把Mysql重啓,再insert一條記錄,這條記錄的ID是18還是15 ?
(1)如果表的類型是MyISAM,那麼是18。
因爲MyISAM表會把自增主鍵的最大ID記錄到數據文件裏,重啓MySQL自增主鍵的最大ID也不會丟失。
(2)如果表的類型是InnoDB,那麼是15。
InnoDB表只是把自增主鍵的最大ID記錄到內存中,所以重啓數據庫或者是對錶進行OPTIMIZE操作,都會導致最大ID丟失。
6.6 優化sql語句執行效率的方法,從哪些方面。sql語句性能如何分析?
(1)儘量選擇較小的列
(2)將where中用的比較頻繁的字段建立索引
(3)select子句中避免使用‘*’
(4)避免在索引列上使用計算,not,in和<>等操作
(5)當只需要一行數據的時候使用limit 1
(6)保證表單數據不超過200w,適時分割表
(7)針對查詢較慢的語句,可以使用explain來分析該語句具體的執行情況
6.7 select Count (*)和Select Count(1)以及Select Count(column)區別
一般情況下,Select Count(*)和Select Count(1)兩着返回結果是一樣的
假如表沒有主鍵(Primary key),那麼count(1)比count(*)快, 如果有主鍵的話,那主鍵作爲count的條件時候count(主鍵)最快
如果你的表只有一個字段的話那count(*)就是最快的 count(*)跟
count(1)的結果一樣,都包括對NULL的統計,而count(column)是不包括NULL的統計
6.8 對於mysql查詢,都有哪些情況導致存儲引擎放棄索引進行全表掃描?
(1)where子句中使用!=或<>操作符
(2)在 where子句中對字段進行 null值判斷,可以將null值設置成0 或者 “”
(3)在 where子句中使用 or來連接條件
select id from t where num=10 or num=20
可以這樣查詢:
select id from t wherenum=10
union all
select id from t wherenum=20
(4)in和 not in也要慎用,否則會導致全表掃描
(5)在 where子句中使用參數,也會導致全表掃描
因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where num=@num
可以改爲強制查詢使用索引:
select id from twith(index(索引名)) wherenum=@num
(6)在 where子句中對字段進行表達式操作
(7)在where子句中對字段進行函數操作
(8)在使用索引字段作爲條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用
6.9 sql語句中 exists和in哪個的效率更高?爲什麼
mysql中的in語句是把外表和內表作hash連接,而exists語句是對外表作loop循環,每次loop循環再對內表進行查詢。一直大家都認爲exists比in語句的效率要高,這種說法其實是不準確的。這個是要區分環境的。
如果查詢的兩個表大小相當,那麼用in和exists差別不大。
如果兩個表中一個較小,一個是大表,則子查詢表大的用exists,子查詢表小的用in:
例如:表A(小表),表B(大表)
1:select * from A where cc in (select cc from B)效率低,用到了A表上cc列的索引;
select * from A where exists(select cc from Bwhere cc=A.cc)效率高,用到了B表上cc列的索引。
相反的
2:select * from B where cc in (select cc from A)效率高,用到了B表上cc列的索引;
select *from B where exists(select cc from A where cc=B.cc)效率低,用到了A表上cc列的索引。
not in 和not exists如果查詢語句使用了not in那麼內外表都進行全表掃描,沒有用到索引;而not extsts的子查詢依然能用到表上的索引。所以無論那個表大,用not exists都比not in要快。
in 與 =的區別
select name from student where name in('zhang','wang','li','zhao');
與
select name from student where name='zhang' orname='li' or name='wang' or name='zhao'
的結果是相同的。