MySQL高級第四篇(查詢截取分析)

一、查詢優化

  1. 小表驅動大表(即小的數據集驅動大的數據集
    原因:如下兩個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++){
    }
}
  1. 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都要創建索引
    提示:
    1. exists(子查詢) 只返回true 或者false,因此子查詢中的select * 也可以是select 1或者任何常量,官方的解釋是,執行時會忽略掉select清單,因此寫什么都沒有區別。
    2. exists往往可以用條件表達式或者其他子查詢或者join來代替,那種最優需要具體問題具體分析
      總結:當子查詢的數據多于主查詢時用exists優于in,當子查詢的數據少于主查詢時,用in優于exists
  2. order by 關鍵字優化
    • MySQL支持兩種方式的排序:using index和using filesort
      index效率高,指MySQL掃描索引本身完成排序。filesort效率低,使用了文件外排序。盡量使用index方式排序,避免使用filesort方式排序。
    • 使用index方式排序,需要滿足以下兩種情況:
      • order by語句使用索引最左前列(注意:select的列要與索引列相同)
      • 使用where字句和order by字句條件列組合,滿足索引最前列
        例如:創建一個符合索引,test_code 和 gen_code。
使用索引.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使用率。
    
  1. group by 關鍵字優化
    • group by 是先排序后分組,用到索引跟order by 一樣,遵從索引最佳左前綴原則
    • 當無法使用索引列時,要加大sort_buffer_sizemax_length_for_sort_data的值
    • where高于having,能寫到where限定條件里的,就不要去having中限定。

二、慢查詢日志

  1. 慢查詢日志解釋:
    • 慢查詢日志是MySQL提供的一種日志記錄,它用來記錄MySQL中響應時間超過閥值的語句,具體指運行時間超過long_query_time值的sql,則會被記錄到慢查詢日志中
    • long_query_time的默認值是10,意思是運行10秒以上的語句。
    • 默認情況下,MySQL沒有開啟慢查詢日志,需要手工設置這個參數,如果不是調優需要的話,盡量不要開啟,因為開啟會影響MySQL的性能,慢查詢日志支持將日志寫到文件
  2. 開啟
    • 查看是否開啟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;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容