MySQL 慢查詢(上):爲啥會這麼慢?

我是架構精進之路,點擊上方「關注」,堅持天天爲你分享技術乾貨,私信我回復「01」,送你一份程序員成長進階指南大禮包。程序員

發現的一些問題

問題1sql

在過去的半年時間裏,研發團隊內部嘗試抓了一波兒慢查詢SQL跟進處理率。發現有些同窗對於慢查詢處理的思路就是看看有沒有用到索引,沒有用到就試圖加一個,實在不行就甩鍋給這種狀況是歷史設計問題或者自行斷定爲用戶特殊操做下觸發的小几率事件,隨即使申請豁免掉...數據庫

這樣其實問題沒有根本上解決。緩存

問題2性能優化

還有就是網絡上常常能夠看到一些相似這樣的文章:服務器

「慢SQL性能優化大全」markdown

「慢SQL性能優化看這篇就夠了」網絡

......架構

其實內容大同小異,要麼建議加索引,要麼建議重寫SQL....性能

怎麼說呢?知識點是對的,但不全面,這個很容易誤導新同窗,哈哈哈。

本文初衷

在業務項目發展過程當中,咱們經常會面對要處理 MySQL 慢查詢問題,那咱們應該如何分析解決問題呢?

部分同窗在處理MySQL慢查詢時候主要思路是加索引來解決,確實加索引是一個很好的解決問題的手段,但不是所有。既然慢查詢做爲問題,那就須要明確問題發生緣由,和解決問題路徑分析, 授人以魚不如授人以漁,讓咱們一塊兒來解鎖 下MySQL處理慢查詢的正確姿式。

本文計劃主要讓你們搞明白查詢SQL爲何會變慢

MySQL 慢查詢(上):爲啥會這麼慢?

廢話很少說,直接開幹~

寫在前面

在業務項目發展過程當中,咱們經常會面對要處理 MySQL 慢查詢問題,那咱們應該如何分析解決問題呢?

部分同窗在處理MySQL慢查詢時候主要思路是加索引來解決,確實加索引是一個很好的解決問題的手段,但不是所有。既然慢查詢是問題,那就須要明確問題發生緣由,和解決問題路徑分析。咱們一塊兒來get下MySQL慢查詢的正確姿式。

本文主要內容包括:

一、查詢SQL執行到底經歷了什麼?

二、查詢SQL爲何會慢?

1. 查詢SQL執行到底經歷了什麼?

首先須要明確:一個查詢SQL的執行到底經歷了什麼?

數據庫執行SQL的大體流程以下:

  • 創建與MySQL服務器鏈接(基礎)

  • 客戶端發送查詢SQL到數據庫,數據庫驗證是否有執行的權限

  • MySQL服務器先檢查查詢緩存,若是命中了緩存,則當即返回存儲在緩存中的結果,不然繼續流轉;

  • MySQL服務器語法解析器,進行詞法與語法分析,預處理

  • 流轉至查詢優化器生成執行計劃

  • 根據生成的執行計劃,調用存儲引擎暴露的API來執行查詢

  • 將查詢執行結果返回給客戶端

  • 關閉MySQL鏈接

具體執行過程可能會因MySQL服務器具體配置和執行場景有一些差別。

1)如未開啓應用查詢緩存,則直接忽略查詢緩存的檢查;

2)執行過程當中,如同時對於被掃描的行可能加鎖,同時也可能會被其餘sql阻塞

2. 查詢SQL爲何會慢?

咱們能夠把查詢SQL執行看作是一個任務的話,那它是由一些列子任務組成的,每一個子任務都存在必定的時間消耗。一般狀況下,致使慢查詢最根本的問題就是須要訪問的數據太多,致使查詢不可避免的須要篩選大量的數據。

面對慢查詢,咱們須要注意如下兩點:

1)查詢了過多不須要的數據

2)掃描了額外的記錄

2.1 查詢了過多不須要的數據

MySQL並非只返回須要的數據,實際上會返回所有結果集再進行計算。

尤爲是多表關聯查詢 select * 的狀況,咱們是否是真的須要所有的列呢?若是不是,那咱們直接指定對應字段就行了。

例如咱們要查詢用戶關聯訂單下的商品信息,以下所示:

SELECT *
FROM users
  LEFT JOIN orders ON orders.user_id = users.user_id
  LEFT JOIN goods ON goods.good_id = orders.good_id
WHERE users.name = 'zhangsan';
複製代碼

這將返回三個表的所有數據列,能夠調整爲僅取須要的列:

SELECT goods.title, goods.description
FROM users
  LEFT JOIN orders ON orders.user_id = users.user_id
  LEFT JOIN goods ON goods.good_id = orders.good_id
WHERE users.name = 'zhangsan';
複製代碼

取出所有列,會讓優化器沒法完成索引覆蓋掃描這類優化,還會爲服務器帶來額外的I/O、內存和CPU的消耗。

2.2 掃描了額外的記錄

此種狀況大部分屬於索引應用不當形成的(包括:應該建的索引沒有建,或者未應用到最佳索引)。

示例表結構以下:

CREATE TABLE `test_table` (
  `name` varchar(32) DEFAULT NULL,
  `desc` varchar(32) DEFAULT NULL,
  `age` int(16) DEFAULT NULL,
  `id` bigint(11) DEFAULT NULL,
  KEY `idx_age` (`age`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
複製代碼

存在索引 `idx_age` 的狀況下,查詢執行計劃結果展現以下:

EXPLAIN SELECT * FROM test_table WHERE age = 10;
複製代碼

預估訪問1行數據便可命中數據,如刪除有效索引 `idx_age` 後則會變成全表掃描(ALL),預估須要掃描121524條記錄才能完成這個查詢,以下圖所示:

總結

根據梳理 MySQL中的 SQL執行過程咱們發現,任何流程的執行都存在其執行環境和規則,其實產生慢SQL的本質是:咱們沒有按照數據庫的要求方式來執行SQL。

主要致使慢查詢最根本的問題就是須要訪問的數據太多,致使查詢不可避免的須要篩選大量的數據。

做者:架構精進之路,專一軟件架構研究,技術學習與我的成長,關注並私信我回復「01」,送你一份程序員成長進階指南大禮包。

Thanks for reading!

相關文章
相關標籤/搜索