一、查詢優化
- 小表驅動大表(即小的數據集驅動大的數據集
原因:如下兩個for循環,執行結果是一樣的,但是如果放到mysql中,第一種執行,先對數據庫建立5次連接,一次連接查詢1000條數據,但是第二種在數據庫中要建立1000次連接,一次連接才查5條數據,而數據庫連接建立釋放是比較耗性能的,所以小表驅動大表的來源就在此。
//第一種情況:
for(int i=0 ;i<5 ;i++){
for(int j=0;j<1000;j++){
}
}
//第二種情況:
for(int i=0 ;i<1000 ;i++){
for(int j=0;j<5;j++){
}
}
- in 和 exists查詢,如何進行選擇
假如有兩個表,A表和B表,其中A表的數據比B表多,那么如果進行子查詢
select * from A where id in (select id from B)
,相當于嵌套循環中的先查B,再查A,這就屬于小表驅動大表。
假如必須要查A表中的所有數據,但是A表的數據比B表少,B表的數據比較多,那么要使用exists,而不能是用in,如下:
select * from A where exists(select 1 from B where B.id = A.id)
此查詢是將主查詢的數據放到子查詢中做條件驗證,根據驗證結果true 或 false 來決定主查詢的數據是否得以保留,此種情況也是屬于小表驅動大表;
注意:A表和B表的id都要創建索引
提示:- exists(子查詢) 只返回true 或者false,因此子查詢中的select * 也可以是select 1或者任何常量,官方的解釋是,執行時會忽略掉select清單,因此寫什么都沒有區別。
- exists往往可以用條件表達式或者其他子查詢或者join來代替,那種最優需要具體問題具體分析
總結:當子查詢的數據多于主查詢時用exists優于in,當子查詢的數據少于主查詢時,用in優于exists。
- order by 關鍵字優化
- MySQL支持兩種方式的排序:using index和using filesort
index效率高,指MySQL掃描索引本身完成排序。filesort效率低,使用了文件外排序。盡量使用index方式排序,避免使用filesort方式排序。 - 使用index方式排序,需要滿足以下兩種情況:
- order by語句使用索引最左前列(注意:select的列要與索引列相同)
- 使用where字句和order by字句條件列組合,滿足索引最前列
例如:創建一個符合索引,test_code 和 gen_code。
- MySQL支持兩種方式的排序:using index和using filesort
使用索引.png
filesort.png
index.png
索引失效.png
image.png
如果不在索引列上,實在用到了filesort,那么MySQL就要啟動雙路排序和單路排序算法:
雙路排序:兩次掃描磁盤最終得到數據,讀取行指針和order by列,對他們進行排序,然后掃描已排序好的列表按照列表中的值,重新從列表中讀取對應的數據輸出,這是MySQL4.1之前使用的,兩次讀取磁盤io,非常耗性能
-
單路排序:MySQL4.1之后出現的,從磁盤讀取查詢需要的所有列,按照order by 列在緩沖區對他們進行排序,然后掃描排序后的列表進行輸出,效率更快,避免了二次讀取數據,并且把隨機io變成了順序io,但是會使用更多的空間,因為每一行都在內存中保存著。
注意:單路排序是比雙路排序效率高,但是有前提,內存足夠大,因為單路排序是把所有的字段都取出來,所有有可能取出的數據大小超出了sort_buffer的容量,導致每次只能取sort_buffer容量大小的數據,進行排序,排完再取sort_buffer容量的大小...反而導致多次讀取磁盤io 。解決方案: 1.order by 時select * 是一個大忌,只select 需要的字段,而不要用* 1.1 當Query的字段大小總和小于max_length_for_sort_data 而且排序字段不是text|bolb類型, 會用改進后的算法-單路排序,否則會用老算法-雙路排序 1.2 兩種算法的數據都有可能超出sort_buffer的容量,超出之后,會創建temp文件進行合并排序, 導致多次I/O,使用單路排序算法的風險會大一些,所以要提高sort_buffer_size 2. 提高sort_buffer_size,不管用哪種算法,提高這個參數,都會提高效率,當然要根據系統的能力去提高, 因為這個參數是針對每個進程的 3.提高max_length_for_sort_data,會增加用改進算法的概率,但是如果設置太高,數據總量超過 sort_buffer_size的概率就會增大,明顯癥狀是,高的磁盤I/O活動和低的cup使用率。
- group by 關鍵字優化
- group by 是先排序后分組,用到索引跟order by 一樣,遵從索引最佳左前綴原則
- 當無法使用索引列時,要加大sort_buffer_size和max_length_for_sort_data的值
- where高于having,能寫到where限定條件里的,就不要去having中限定。
二、慢查詢日志
- 慢查詢日志解釋:
- 慢查詢日志是MySQL提供的一種日志記錄,它用來記錄MySQL中響應時間超過閥值的語句,具體指運行時間超過long_query_time值的sql,則會被記錄到慢查詢日志中
- long_query_time的默認值是10,意思是運行10秒以上的語句。
- 默認情況下,MySQL沒有開啟慢查詢日志,需要手工設置這個參數,如果不是調優需要的話,盡量不要開啟,因為開啟會影響MySQL的性能,慢查詢日志支持將日志寫到文件
- 開啟
- 查看是否開啟
show variables like '%slow_query_log%'
,如果slow_query_log=off,那么就是關閉的, - 開啟慢查詢
set global slow_query_log=1
,使用此語句以后,只對當前數據庫生效,如果MySQL重啟則會失效,如果要永久生效,那么需要修改my.cnf文件,在[mysqld]增加或修改參數slow_query_log=1和slow_query_log_file=你要的地址,然后重啟服務器,不建議長期開啟 - 如果沒有設置慢查詢日志文件存放位置,即slow_query_log_file是空的,那么系統會默認給一個缺省的文件host-name-slow.log
- 命令
show variables like '%long_query_time%'
,此命令可以查看慢查詢日志時間,可以在命令行里面修改,也可以在my.cnf中修改。SET GLOBAL long_query_time=1
,設置為1秒,修改以后可能沒看到修改,需要先斷開連接,重新連接才能看到效果。 - 命令
show global status like '%Slow_queries%'
,可以查看有多少條慢sql - 日志分析工具mysqldumpslow,執行命令mysqldumpslow --help可以查看幫助信息,以下是每個命令的解釋:
- -s:是表示按照何種方式排序;
- c:訪問次數
- l:鎖時間
- r:返回記錄
- t:查詢時間
- al:平均鎖定時間
- ar:平均返回記錄數
- at:平均查詢時間
- -t:即為返回多少條數據
- -g:后面可以搭配正在表達式,大小寫不敏感
- 例子1:得到返回記錄集最多的10個sql
mysqldumpslow -s r -t 10 /var/lib/mysql/mysql-slow.log
; - 得到訪問次數最多的10個sql
mysqldumpslow -s c -t 10 /var/lib/mysql/mysql-slow.log
; - 得到按照時間排序的前10條里面含有左連接的查詢語句
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/mysql-slow.log
; - 結合 | more可以防止爆屏,
mysqldumpslow -s r -t 10 /var/lib/mysql/mysql-slow.log | more
;
- 例子1:得到返回記錄集最多的10個sql
- 查看是否開啟