日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL性能分析工具

發布時間:2024/3/13 数据库 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL性能分析工具 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一節 統計SQL執行成本

統計SQL的查詢成本: last_query_cost
一條SQL查詢語句在執行前需要確定查詢執行計劃,如果存在多種執行計劃的話,MySQL會計算每個執行計劃所需要的成本,從中選擇成本最小的一個作為最終執行的執行計劃。
如果我們想要查看某條SQL語句的查詢成本,可以在執行完這條SQL語句之后,通過查看當前會話中的last_query_cost變量值來得到當前查詢的成本。它通常也是我們評價一個查詢的執行效率的一個常用指標。這個查詢成本對應的是SQL語句所需要讀取的頁的數量。
SQL查詢是一個動態的過程,從頁加載的角度來看,我們可以得到以下兩點結論:

  • 位置決定效率。如果頁就在數據庫緩沖池中,那么效率是最高的,否則還需要從磁盤中進行讀取,當然針對單個頁的讀取來說,如果頁存在于內存中,會比在磁盤中讀取效率高很多。
  • 批量決定效率。如果我們從磁盤中對單一頁進行隨機讀,那么效率是很低的(差不多10ms),而采用順序讀取的方式,批量對頁進行讀取,平均一頁的讀取效率就會提升很多,甚至要快于單個頁面在內存中的隨機讀取。
    所以說,遇到I/O并不用擔心,方法找對了,效率還是很高的。我們首先要考慮數據存放的位置,如果是經常使用的數據就要盡量放到緩沖池中,其次我們可以充分利用磁盤的吞吐能力,一次性批量讀取數據,這樣單個頁的讀取效率也就得到了提升。
  • 第二節 分析查詢語句:EXPLAIN

    2.1 基本語法

    如果我們想看看某個查詢的執行計劃的話,可以在具體的查詢語句前邊加一個 EXPLAIN ,就像這樣:

    EXPLAIN SELECT 1;

    EXPLAIN 語句輸出的各個列的作用如下:

    列名描述
    id在一個大的查詢語句中每個SELECT關鍵字都對應一個 唯一的id
    select_typeSELECT關鍵字對應的那個查詢的類型
    table表名
    partitions匹配的分區信息
    type☆針對單表的訪問方法
    possible_keys可能用到的索引
    key實際上使用的索引
    key_len ☆實際使用到的索引長度
    ref當使用索引列等值查詢時,與索引列進行等值匹配的對象信息
    rows ☆預估的需要讀取的記錄條數
    filtered某個表經過搜索條件過濾后剩余記錄條數的百分比
    Extra ☆一些額外的信息

    2.2 詳細解釋

    為了讓大家有比較好的體驗,下面調整了下 EXPLAIN 輸出列的順序。

    2.2.1 table

    不論我們的查詢語句有多復雜,里邊兒包含了多少個表 ,到最后也是需要對每個表進行 單表訪問的,所以MySQL規定EXPLAIN語句輸出的每條記錄都對應著某個單表的訪問方法,該條記錄的table列代表著該表的表名(有時不是真實的表名字,可能是簡稱)。

    2.2.2 id

    我們寫的查詢語句一般都以 SELECT 關鍵字開頭,比較簡單的查詢語句里只有一個 SELECT 關鍵字,比如下邊這個查詢語句:

    SELECT * FROM s1 WHERE key1 = 'a'; mysql> EXPLAIN SELECT * FROM s1 WHERE key1 = 'a’;


    稍微復雜一點的連接查詢中也只有一個 SELECT 關鍵字,比如:

    SELECT * FROM s1 INNER JOIN s2 ON s1.key1 = s2.key1 WHERE s1.common_field = 'a'; mysql> EXPLAIN SELECT * FROM s1 INNER JOIN s2;

    mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key1 FROM s2) OR key3 = 'a';

    mysql> EXPLAIN SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field= 'a');


    查看mysql優化后的sql:

    EXPLAIN EXTENDED SELECT * FROM s1 WHERE key1 IN (SELECT key2 FROM s2 WHERE common_field= 'a'); SHOW WARNINGS;

    小結:
    id如果相同,可以認為是一組,從上往下順序執行
    在所有組中,id值越大,優先級越高,越先執行
    關注點:id號每個號碼,表示一次獨立的查詢, 一個sql的查詢次數越少越好

    2.2.3 select_type

    名稱描述
    SIMPLE簡單查詢(不使用UNION或子查詢)
    PRIMARY最外層的查詢
    UNIONUNION中第二個語句或后面的語句
    UNION RESULTUNION每個結果集的取出來后,進行合并操作
    dependentSubquery子查詢中的第一個 SELECT,依賴了外面的查詢
    derived派生表,子查詢在 FROM子句中


    SIMPLE
    SIMPLE,最簡單的查詢方式。

    EXPLAIN SELECT * FROM `sys_user`

    primary、union、union result
    PRIMARY,主查詢。
    UNION,UNION中第二個語句或后面的語句。
    UNION RESULT,UNION每個結果集的取出來后,進行合并操作。

    EXPLAIN select * from sys_user where user_id=1 union select * from sys_user where user_id=2

    Subquery
    子查詢:#如果包含子查詢的查詢語句不能夠轉為對應的semi-join(多表連接)的形式,并且該子查詢是不相關子查詢。#該子查詢的第一個SELECT'關鍵字代表的那個查詢的select type '就是SUBQUERY

    EXPLAIN SELECT* FROMcoupon_give_gift WHEREcoupon_id = ( SELECT coupon_id FROM coupon WHERE id = '111);

    EXPLAIN SELECT* FROMcoupon_give_gift WHEREcoupon_id in ( SELECT coupon_id FROM coupon WHERE id = '111);


    查詢優化器把in換成關聯查詢

    dependentSubquery
    DEPENDENT SUBQUERY,子查詢中內層的第一個SELECT,依賴于外部查詢的結果集。

    EXPLAIN SELECT* FROMcoupon_give_gift WHEREcoupon_id = (SELECT sub_title FROM coupon WHERE coupon_give_gift.coupon_id = coupon.coupon_id );


    derived
    DERIVED,派生表,子查詢在 FROM子句中。

    explain select * from ( select user_id,count(*) from sys_user group by user_id ) user1

    2.2.4 partitions

    EXPLAIN SELECT * FROM user_partitions WHERE id>200;

    查詢id大于200(200>100,p1分區)的記錄,查看執行計劃,partitions是p1,符合我們的分區規則。

    2.2.5 type

    完整的訪問方法如下: system , const , eq_ref , ref , fulltext , ref_or_null ,index_merge , unique_subquery , index_subquery , range , index , ALL 。

    system : 該表只有一行(相當于系統表),system是const類型的特例
    const:當我們根據主鍵或者唯一二級索引列與常數進行等值匹配時,對單表的訪問方法就是`const ’

    mysql> EXPLAIN SELECT * FROM interests where interests_id = 'CRDB951AF8FE05641140FA8FC6638A2B62D20210113016’;


    eq_ref:#在連接查詢時,如果被驅動表是通過主鍵或者唯一二級索引列等值匹配的方式進行訪問的(如果該主鍵或者唯一二級索引是聯合索引的話,所有的索引列都必須進行等值比較),則對該被驅動表的訪問方法就是eq _ref。

    EXPLAIN SELECT * FROM coupon c LEFT JOIN coupon_put_rule cpr on c.coupon_id = cpr.id;


    ref:當通過普通的二級索引列與常量進行等值匹配時來查詢某個表,那么對該表的訪問方法就可能是`ref’,聯合索引時只匹配部分字段也為ref。

    EXPLAIN SELECT * FROM coupon_store where coupon_id = 'C5BE9A420C9C26F07580DA1EB13D6A28' and store_id = '3570012;


    ref_or_null:當對普通二級索引進行等值匹配查詢,該索引列的值也可以是NULL值時,那么對該表的訪問方法就是‘ref or null`

    EXPLAIN SELECT * FROM coupon where promotion_id = 'C5BE9A420C9C26F07580DA1EB13D6A28' or promotion_id is null;


    index_merge:此類型表示使用了索引合并優化,表示一個查詢里面用到了多個索引

    EXPLAIN SELECT * FROM coupon where promotion_id = 'C5BE9A420C9C26F07580DD6A28' or coupon_id = 'C5BE9A42DA1EB13D6A8';


    unique subquery :是針對在一些包含’IN子查詢的查詢語句中,如果查詢優化器決定將IN子查詢轉換為EXISTS子查詢,而且子查詢可以使用到主鍵進行等值匹配的話,那么該子查詢執行計劃的 type '列的值就是unique_subquery

    EXPLAIN SELECT* FROMcoupon_give_gift WHEREcoupon_id in (SELECT id FROM coupon WHERE coupon_give_gift.coupon_id = coupon.coupon_id ) or give_quantity = 1;


    range:表示使用了范圍類型的查詢,一般出現在使用了<>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE, 貨 IN() 之類的字句和常量值作比較時

    EXPLAIN SELECT * FROM coupon where coupon_id in ('111','222','333','444')

    index:當我們可以使用索引覆蓋,但需要掃描全部的索引記錄時,該表的訪問方法就是index。

    EXPLAIN SELECT coupon_id FROM coupon_give_gift where give_coupon_id = '111';

    ALL:全表掃描。
    上述分析的過程為從上至下,效率一次降低。

    2.2.6 possible_keys和key

    在EXPLAIN語句輸出的執行計劃中,possible_keys列表示在某個查詢語句中,對某個表執行單表查詢時可能用到的索引有哪些。一般查詢涉及到的字段上若存在索引,則該索引將被列出,但不一定被查詢使用。key列表示實際用到的索引有哪些,如果為NULL,則沒有使用索引。比方說下邊這個查詢:

    EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111' and promotion_id = '111;

    EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111’or promotion_id = '111';

    2.2.7 key_len

    實際使用到的索引長度(即:字節數)#幫你檢查是否充分的利用上了索引,同一個索引`值越大越好’,主要針對聯合索引。

    列類型KEY_LEN備注
    id intkey_len = 4+1int為4bytes,允許為NULL,加1byte(1字節用來記錄是否為null)
    id bigint not nullkey_len=8bigint為8bytes
    user char(30) utf8key_len=30*3+1utf8每個字符為3bytes,允許為NULL,加1byte
    user varchar(30) not null utf8key_len=30*3+2utf8每個字符為3bytes,變長數據類型,加2bytes(2字節用來記錄實際長度)
    user varchar(30) utf8key_len=30*3+2+1utf8每個字符為3bytes,允許為NULL,加1byte,變長數據類型,加2bytes
    EXPLAIN SELECT * FROM s1 WHERE id = 1;EXPLAIN SELECT * FROM s1 WHERE key2 = 2;EXPLAIN SELECT * FROM s1 WHERE key1 = 'a';EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a';EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part2 = 'b';

    2.2.8 ref

    當使用索引列等值查詢時,與索引列進行等值匹配的對象信息。比如只是一個常數或者是某個列。

    EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111';

    EXPLAIN SELECT* FROMcoupon inner JOINcoupon_give_gift on coupon_give_gift.coupon_id = coupon.coupon_id;

    2.2.9 rows

    預估的需要讀取的記錄條數,值越小越好。

    EXPLAIN SELECT * FROM coupon WHERE id < 100;

    2.2.10 filtered

    某個表經過搜索條件過濾后剩余記錄條數的百分比。如果使用的是索引執行的單表掃描,那么計算時需要估計出滿足除使用到對應索引的搜索條件外的其他搜索條件的記錄有多少條。

    EXPLAIN SELECT * FROM coupon WHERE id < 100 and campaign_coupon_code = '202007071120';

    2.2.11 Extra

    顧名思義,Extra列是用來說明一些額外信息的,包含不適合在其他列中顯示但十分重要的額外信息。我們可以通過這些額外信息來更準確的理解MySQL到底將如何執行給定的查詢語句。MySQL提供的額外信息有好幾十個,我們就不一個一個介紹了,所以只挑比較重要的額外信息介紹給大家。

  • No tables used 當查詢語句的沒有FROM子句時將會提示該額外信息。
  • EXPLAIN SELECT 1;


    2. Impossible where 查詢語句的WHERE子句永遠為FALSE時將會提示該額外信息

    EXPLAIN SELECT * FROM s1 WHERE 1 != 1;


    3. Using where 當我們使用全表掃描來執行對某個表的查詢,并且該語句的where 子句中有針對該表的搜索條件時,在Extra列中會提示上述額外信息。當使用索引訪問來執行對某個表的查詢,并且該語句的WHERE子句中有除了該索引包含的列之外的其他搜索條件時,在`Extra '列中也會提示上述額外信息。

    EXPLAIN SELECT * FROM coupon WHERE campaign_coupon_code = '111’; EXPLAIN SELECT * FROM coupon WHERE coupon_id = '111' and campaign_coupon_code = '111;


    4. Using index 當我們查詢列表以及搜索條件中只包含屬于某個索引的列,也就是在可以使用覆蓋索引的情況下,在Extra列將會提示該額外信息。比方說下邊這個查詢中只需要用到‘coupon_id’而不需要回表操作:

    EXPLAIN SELECT coupon_id FROM coupon WHERE coupon_id = '111;


    5. Using index condition 搜索條件中出現了索引列,按最左匹配原則無法匹配上,但是用到了索引下推。

    EXPLAIN SELECT * FROM s1 WHERE key_part1 = 'a' AND key_part3 = 'b';


    6. Using join buffer 在連接查詢執行過程中,當被驅動表不能有效的利用索引加快訪問速度,MySQL一般會為其分配一塊名叫`join buffer '的內存塊來加快查詢速度,也就是我們所講的‘基于塊的嵌套循環算法’

    EXPLAIN SELECT * FROM s1 INNER JOIN s2 ON s1.common_field = s2.common_field;


    7. Using union 表示需要進行索引合并的索引名稱,如果出現了’Using union(…)提示,說明準備使用union索引合并的方式執行查詢; 出現了Using sort union(…)提示,說明準備使用’sort-Union `索引合并的方式執行查詢。

    EXPLAIN SELECT * FROM s1 WHERE key1 = 'a' or key3 = 'a';

  • Using filesort 很多情況下排序操作無法使用到索引,只能在內存中(記錄較少的時候)或者磁盤中(記錄較多的時候)進行排序,Mysql把這種在內存中或者磁盤上進行排序的方式統稱為文件排序(英文名: ‘filesort’)。如果某個查詢需要使用文件排序的方式執行查詢,就會在執行計劃的Extra列中顯示’Using filesort `提示。
  • EXPLAIN SELECT * FROM s1 ORDER BY common_field LIMIT 10;

  • Using temporary 在許多查詢的執行過程中,MySQL可能會借助臨時表來完成一些功能,比如去重、排序之類的,比如我們在執行許多包含DISTINCT '、GROUP BY'、‘UNION等子句的查詢過程中,如果不能有效利用索引來完成查詢,MysQL很有可能尋求通過建立內部的臨時表來執行查詢。如果查詢中使用到了內部的臨時表,在執行#計劃的‘Extra列將會顯示Using temporary`提示。
  • EXPLAIN SELECT DISTINCT common_field FROM s1;

    2.3 擴展

    2.3.1 最左匹配原則

    最左優先,以最左邊的為起點任何連續的索引都能匹配上。同時遇到范圍查詢(>、<、between、like)就會停止匹配。
    為什么最左匹配:是因為mysql創建聯合索引時,首先會對最左邊字段排序,也就是第一個字段,然后再在保證第一個字段有序的情況下,再排序第二個字段,以此類推。所以聯合索引最左列是絕對有序的,其他字段無序。舉個例子:可以把聯合索引看成“通訊錄”,姓名作為聯合索引,姓是第一列,名是第二列,當查找人名時,是先確定這個人姓再根據名確定人。只有名沒有姓就查不到。

    2.3.2 索引下推

    什么是索引下推
    索引下推(Index Condition Pushdown,簡稱ICP),是MySQL5.6版本的新特性,它能減少回表查詢次數,提高查詢效率。MySQL服務層負責SQL語法解析、生成執行計劃等,并調用存儲引擎層去執行數據的存儲和檢索。索引下推的下推其實就是指將部分上層(服務層)負責的事情,交給了下層(引擎層)去處理。

    索引下推優化的原理
    先來看一下MySQL的大致框架

  • 不使用ICP索引掃描的過程


    如上圖所示:
    storage層:只將滿足index key條件的索引記錄對應的整行記錄取出,返回給server層
    server 層:對返回的數據,使用后面的where條件過濾,直至返回最后一行。

  • 使用ICP掃碼的過程

    如圖所示:
    storage層:首先將index key條件滿足的索引記錄區間確定,然后在索引上使用index filter進行過 濾。將滿足的index filter條件的索引記錄才去回表取出整行記錄返回server層。不滿足index filter條件的索引記錄丟棄,不回表、也不會返回server層。
    server 層:對返回的數據,使用table filter條件做最后的過濾。

  • 使用前后的成本差別
    使用ICP前,存儲層多返回了需要被index filter過濾掉的整行記錄。使用ICP后,直接就去掉了不滿足index filter條件的記錄,省去了他們回表和傳遞到server層的成本。ICP的 加速效果 取決于在存儲引擎內通過 ICP篩選 掉的數據的比例。

    使用ICP條件
    ① 只能用于二級索引(secondary index)
    ② explain顯示的執行計劃中type值(join 類型)為 range 、ref 、eq_ref 或者 ref_or_null 。
    ③ 并非全部where條件都可以用ICP篩選,如果where條件的字段不在索引列中,還是要讀取整表的記錄到server端做where過濾。
    ④ ICP可以用于MyISAM和InnnoDB存儲引擎
    ⑤ MySQL 5.6版本的不支持分區表的ICP功能,5.7版本的開始支持。
    ⑥ 當SQL使用覆蓋索引時,不支持ICP優化方法

    2.3.2 前綴索引

    什么是前綴索引?
    前綴索引也叫局部索引,比如給身份證的前 10 位添加索引,類似這種給某列部分信息添加索引的方式叫做前綴索引。
    為什么要用前綴索引?
    前綴索引能有效減小索引文件的大小,讓每個索引頁可以保存更多的索引值,從而提高了索引查詢的速度。但前綴索引也有它的缺點,不能在 order by 或者 group by 中觸發前綴索引,也不能把它們用于覆蓋索引。
    索引選擇性是什么?
      索引的選擇性,指的是不重復的索引值(基數)和表記錄數的比值。選擇性是索引篩選能力的一個指標。索引的取值范圍是 0—1 ,當選擇性越大,索引價值也就越大。
      舉例說明:假如有一張表格,總共有一萬行的記錄,其中有一個性別列sex,這個列的包含選項就兩個:男/女。那么,這個時候,這一列創建索引的話,索引的選擇性為萬分之二,這時候,在性別這一列創建索引是沒有啥意義的。假設個極端情況,列內的數據都是女,那么索引的選擇性為萬分之一,其效率還不如直接進行全表掃描。如果是主鍵索引的話,那么選擇性為1,索引價值比較大。可以直接根據索引定位到數據。
    什么情況下適合使用前綴索引?
    當字符串本身可能比較長,而且前幾個字符就開始不相同,適合使用前綴索引;相反情況下不適合使用前綴索引,比如,整個字段的長度為 20,索引選擇性為 0.9,而我們對前 10 個字符建立前綴索引其選擇性也只有 0.5,那么我們需要繼續加大前綴字符的長度,但是這個時候前綴索引的優勢已經不明顯,就沒有創建前綴索引的必要了。
    索引選擇性怎么計算?
    首先我們可以通過如下 SQL 得到全列選擇性:

    SELECT COUNT(DISTINCT column_name) / COUNT(*) FROM table_name;

    例如:

    SELECT COUNT(DISTINCT coupon_id) / COUNT(*) FROM coupon;

    然后再通過如下 SQL 得到某一長度前綴的選擇性:

    SELECT COUNT(DISTINCT LEFT(column_name, prefix_length)) / COUNT(*) FROM table_name;

    例如:

    SELECT COUNT(DISTINCT LEFT(coupon_id, 6)) / COUNT(*) FROM coupon; SELECT COUNT(DISTINCT LEFT(coupon_id, 8)) / COUNT(*) FROM coupon;

    總結

    以上是生活随笔為你收集整理的MySQL性能分析工具的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。