利用MySQL系統資料庫做性能負載診斷

来源:https://www.cnblogs.com/wy123/archive/2019/09/01/11431227.html
-Advertisement-
Play Games

某大師曾說過,像瞭解自己的老婆 一樣瞭解自己管理的資料庫,個人認為包含了兩個方面的瞭解: 1,在穩定性層面來說,更多的是關註高可用、讀寫分離、負載均衡,災備管理等等high level層面的措施(就好比要保證生活的穩定性) 2,在實例級別的來說,需要關註記憶體、IO、網路,熱點表,熱點索引,top s ...


某DBA大師曾說過,一個DBA要像瞭解自己的老婆一樣瞭解自己管理的資料庫,個人認為包含了兩個方面的瞭解: 1,在穩定性層面來說,更多的是關註高可用、讀寫分離、負載均衡,災備管理等等high level層面的措施(就好比要保證生活的穩定性) 2,在實例級別的來說,需要關註記憶體、IO、網路,熱點表,熱點索引,top sql,死鎖,阻塞,歷史上執行異常的SQL(好比生活品質細節) MySQL的performance_data庫和sys庫提供了非常豐富的系統日誌數據,可以幫助我們更好地瞭解非常細節的,這裡簡單地列舉出來了一些常用的數據。 sys庫是以較為可讀化的方式封裝了performance_data中的某些表,因此這些個數據來源還是performance_data庫中的數據。 這裡粗略列舉出個人常用的一些系統數據,buffer pool記憶體被那些對象占用了?系統熱點數據是哪些?TOP SQL是哪些?物理IO的大小?記憶體與IO的交換速度是怎麼樣的?記憶體有沒有壓力? 可以在實例級別更加清楚地瞭解MySQL的運行過程中資源分配情況。   Status中的信息 MySQL的status變數只是給出了一個總的信息,從status變數上無法得知詳細資源的消耗,比如IO或者記憶體的熱點在哪裡,庫、表的熱點在哪裡,如果想要知道具體的明細信息就需要系統庫中的數據。 前提要開啟performance_schema,因為sys庫的視圖是基於performance_schema的庫的。     記憶體使用: 記憶體/innodb_buffer_pool使用概要 innodb_buffer_pool的使用情況summary,已知當前實例262144*16/1024 = 4096MB buffer pool,已使用23260*16/1024 363MB

innodb_buffer_pool已占用記憶體的明細信息,可以按照庫\表的維度來統計

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

SELECT 
     database_name,
     SUM(compressed_size)/1024/1024  AS allocated_memory,
     SUM(data_size)/1024/1024  AS data_memory,
     SUM(is_hashed)*16/1024 AS is_hashed_memory,
     SUM(is_old)*16/1024 AS is_old_memory
FROM 
(
    SELECT 
        case when INSTR(TABLE_NAME,'.')>0 then replace(SUBSTRING(TABLE_NAME,1,INSTR(TABLE_NAME,'.')-1),'`','')
        else     'system_database' end as database_name,
        case when INSTR(TABLE_NAME,'.')>0 then replace(SUBSTRING(TABLE_NAME,INSTR(TABLE_NAME,'.')+1),'`','')
        ELSE 'system_obj' END AS table_name,
        if(compressed_size = 0, 16384, compressed_size) AS compressed_size,
        data_size,
        if(is_hashed = 'YES',1,0) is_hashed,
        if(is_old = 'YES',1,0)  is_old
    FROM information_schema.innodb_buffer_page
    WHERE TABLE_NAME IS NOT NULL
) t
GROUP BY database_name
ORDER BY allocated_memory DESC
LIMIT 10;

 

緩存命中率統計及冷熱數據變化

查詢緩存命中率相關:
information_schema.innodb_buffer_pool_stats中的數據行數是跟buffer_pool_instance一致的
也就是每個一行數據來描述一個buffer_pool_instance,這裡簡單取和,緩存命中率取平局值的方式來統計
需要註意的是
1,modified_database_pages是實時的,就是記憶體中的臟頁的數量,經checkpoint之後被刷新到磁碟,因此會時大時小。
2,pages_made_young和pages_not_made_young是累積的增加的,不會減少,就是MySQL實例截止到目前位置,做了多少pages_not_made_young和pages_not_made_young。
3,hit_rate在負載較低的情況下,沒有參考意義,這一點很奇怪,低負載情況下,會發現很多buffer_pool的hit_rate是0。
  反覆測試的過程中突然意識到,hit_rate的計算,是不是以某個時間間隔為基準,統計這個時間段內請求的命中率,如果這一小段時間內沒有請求,統計出來的hit_rate就是0。
4,與其他視圖不通,information_schema.innodb_buffer_pool_stats中的數據會在服務重啟後清零。

SELECT 
    SUM(modified_database_pages) AS total_modified_database_pages,
    SUM(pages_made_young) AS total_pages_made_young,
    SUM(pages_not_made_young) AS total_pages_not_made_young,
    SUM(hit_rate)/COUNT(hit_rate)*1000 AS hit_rate
FROM

(
    SELECT 
        pool_id,
        pool_size,
        database_pages,
        old_database_pages,
        modified_database_pages,
        pages_made_young,
        pages_not_made_young,
        hit_rate
    FROM information_schema.innodb_buffer_pool_stats
)t;

參考https://www.cnblogs.com/geaozhang/p/7276802.html這裡對這pages_made_young和page_not_made_young,個人覺得解釋的非常好。

這裡低負載下的information_schema.innodb_buffer_pool_stats中的信息,hit_rate的值簡直不可思議。
這個實例是4GB的記憶體,基本上沒有訪問量,hit_rate竟然出來好多值為0的情況。

相反在對當前實例做壓力測試的時候,這個數據看起來才是正常的,包括modified_database_pages,pages_made_young,pages_not_made_young,hit_rate

這裡用mysqlslap 做混合讀寫的壓力測試

./mysqlslap -uroot -proot -h127.0.0.1 -P8000 --concurrency=100 --iterations=10000 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=mixed --engine=innodb --number-of-queries=10000

用python定時列印innodb_buffer_pool_stats

import pymysql
import logging
import time
import decimal


def execute_query(conn_dict,sql):
    conn = pymysql.connect(host=conn_dict['host'],
                           port=conn_dict['port'],
                           user=conn_dict['user'],
                           passwd=conn_dict['password'],
                           db=conn_dict['db'])
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    cursor.execute(sql)
    list = cursor.fetchall()
    cursor.close()
    conn.close()
    return list

def check_innodb_buffer_pool_stats(flag,conn_dict):
    result = execute_query(conn_dict, '''SELECT 
                                            modified_database_pages,
                                            pages_made_young,
                                            pages_not_made_young,
                                            hit_rate
                                        FROM information_schema.innodb_buffer_pool_stats;''')
    if result:
        column = result[0].keys()
        current_row = ''
        if(flag<=0):
            for key in column:
                current_row += str(key) + "    "
            print(current_row)

        for row in result:
            current_row = ''
            for key in row.values():
                current_row += str(key) + "    "
            print(current_row)

if __name__ == '__main__':
    conn  = {'host': '127.0.0.1', 'port': my_port, 'user': 'root', 'password': '***', 'db': 'mysql', 'charset': 'utf8mb4'}
    flag = 0
    while 1>0:
        check_innodb_buffer_pool_stats(flag,conn)
        time.sleep(3)
        flag = 1

這樣子看下來,這個統計還是比較正常的。

hit_rate的計算,是不是以某個時間間隔為基準,統計這個時間段內請求的命中率,如果這一小段時間內沒有請求,統計出來的hit_rate就是0?

 

庫\表的讀寫統計,邏輯層面的熱點數據統計 目標表是performance_schema.table_io_waits_summary_by_table,某些文章上說是邏輯IO,其實這裡跟邏輯IO並無關係,這個表中的欄位含義是基於表,讀寫的到的行數的統計。 至於真正的邏輯IO層面的統計,筆者目前還有不知道有哪個可用的系統表來查詢。 這個庫可以很清楚地看到這個表中的統計結果是怎麼計算出來的。

基於表的讀寫的行的次數統計,這是一個累計值,單純的看這個值本身,個人覺得意義不大,需要定時收集計算差值,才具備參考意義。
以下按照庫級別統計表的讀寫情況。

 

庫\表的讀寫統計,物理IO層面的熱點數據統計 按照物理IO的維度統計熱點數據,哪些庫\表消耗了多少物理IO。 這裡原始系統表中的數據是一個累計統計的值,最極端的情況就是一個表為0行,卻存在大量的物理讀寫IO。  
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;


SELECT 
    database_name,
    IFNULL(cast(sum(total_read) as signed),0) AS total_read,
    IFNULL(cast(sum(total_written) as signed),0) AS total_written,
    IFNULL(cast(sum(total) AS SIGNED),0) AS total_read_written
FROM
(
    SELECT 
        substring(REPLACE(file, '@@datadir/', ''),1,instr(REPLACE(file, '@@datadir/', ''),'/')-1) AS database_name,
        count_read,
        case 
            when instr(total_read,'KiB')>0 then  replace(total_read,'KiB','')/1024
            when instr(total_read,'MiB')>0 then  replace(total_read,'MiB','')/1024
            when instr(total_read,'GiB')>0 then replace(total_read,'GiB','')*1024
        END AS total_read,
        case 
            when instr(total_written,'KiB')>0 then replace(total_written,'KiB','')/1024
            when instr(total_written,'MiB')>0 then replace(total_written,'MiB','')
            when instr(total_written,'GiB')>0 then replace(total_written,'GiB','')*1024
        END AS total_written,
        case 
            when instr(total,'KiB')>0 then replace(total,'KiB','')/1024
            when instr(total,'MiB')>0 then replace(total,'MiB','')
            when instr(total,'GiB')>0 then replace(total,'GiB','')*1024
        END AS total
    from sys.io_global_by_file_by_bytes 
    WHERE FILE LIKE '%@@datadir%' AND instr(REPLACE(file, '@@datadir/', ''),'/')>0 
)t
GROUP BY database_name
ORDER BY total_read_written DESC;
ps:個人不太喜歡MySQL自定義的format_***函數,這個函數的初衷是好的,把一些數據(時間,存儲空間)等格式化成更加可讀的模式。 但是卻不支持單位的參數,更多的時候想以某個固定的單位來顯示,比如格式化一個的時間,格式化後根據單位大小可能會顯示微妙,或者是毫秒,或者是秒,或者分鐘,或者天。 比如想把時間統一格式化成秒,對不起,不支持,某些個數據不僅僅是看一眼那麼簡單,甚至是要讀出來存檔分析的,因此這裡不建議也不會使用那些個format函數  

TOP SQL 統計

可以按照執行時間,阻塞時間,返回行數等等維度統計top sql。
另外可以按照時間篩選last_seen,可以統計最近某一段時間出現過的top sql

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

SELECT 
    schema_name,
    digest_text,
    count_star,
    avg_timer_wait/1000000000000 AS avg_timer_wait,
    max_timer_wait/1000000000000 AS max_timer_wait,
    sum_lock_time/count_star/1000000000000 AS avg_lock_time ,
    sum_rows_affected/count_star AS avg_rows_affected,
    sum_rows_sent/count_star AS avg_rows_sent ,
    sum_rows_examined/count_star AS avg_rows_examined,
    sum_created_tmp_disk_tables/count_star AS avg_create_tmp_disk_tables,
    sum_created_tmp_tables/count_star AS avg_create_tmp_tables,
    sum_select_full_join/count_star AS avg_select_full_join,
    sum_select_full_range_join/count_star AS avg_select_full_range_join,
    sum_select_range/count_star AS avg_select_range,
    sum_select_range_check/count_star AS avg_select_range,
    first_seen,
    last_seen
FROM performance_schema.events_statements_summary_by_digest
WHERE last_seen>date_add(NOW(), interval -1 HOUR)
ORDER BY 
max_timer_wait
-- avg_timer_wait
-- sum_rows_affected/count_star 
-- sum_lock_time/count_star
-- avg_lock_time
-- avg_rows_sent
DESC
limit 10;

需要註意的是,這個統計是按照MySQL執行一個事務消耗的資源做統計的,而不是一個語句,筆者一開始懵逼了一陣子,舉個簡單的例子。
參考如下,這裡是迴圈寫個數據的一個存儲過程,調用方式就是call create_test_data(N),寫入N條測試數據。
比如call create_test_data(1000000)就是寫入100W的測試數據,這個執行過程耗費了幾分鐘的時間,按照筆者的測試實例情況,avg_timer_wait的維度,絕對是一個TOP SQL。
但是在查詢的時候,始終沒有發現這個存儲過程的調用被列為TOP SQL,後面嘗試在存儲過程內部加了一個事物,然後就順利地收集到了整個TOP SQL.
因此說performance_schema.events_statements_summary_by_digest裡面的統計,是基於事務的,而不是某一個批處理的執行時間的。

CREATE DEFINER=`root`@`%` PROCEDURE `create_test_data`(
    IN `loopcnt` INT
)
LANGUAGE SQL
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
    -- START TRANSACTION; 
        while loopcnt>0 do
            insert into test_mrr(rand_id,create_date) values (RAND()*100000000,now(6));
            set loopcnt=loopcnt-1;
        end while;
    -- commit;
END

另外一點比較有意思的是,這個系統表是為數不多的支持truncate的,當然它在內部,也是在不斷收集的一個過程。

 

執行失敗的SQL 統計

一直以為系統不會記錄執行失敗的\解析錯誤的SQL,比如想統計因為超時而執行失敗的語句,後面才發現,這些信息,MySQL會完整地記錄下來

這裡會詳細記錄執行錯誤的語句,包括最終執行失敗(超時之類的),語法錯誤,執行過程中產生了警告之類的語句。用sum_errors>0 or sum_warnings>0去performance_schema.events_statements_summary_by_digest篩選一下即可。

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

select 
    schema_name,
    digest_text,
    count_star,
    first_seen,
    last_seen
from performance_schema.events_statements_summary_by_digest
where sum_errors>0 or sum_warnings>0 
order by last_seen desc;

 

 

Index使用情況統計

基於performance_schema.table_io_waits_summary_by_index_usage這個系統表,其統計的維度同樣是“按照某個索引查詢返回的行數的統計”。

可以按照哪些索引使用最多\最少等情況進行統計。

不過這個統計有一個給人潛在一個誤區:
count_read,count_write,count_fetch,count_insert,count_update,count_delete統計了某個索引上使用到索引的情況下,受影響的行數,sum_timer_wait是累計在該索引上等待的時間。
如果使用到了該索引,但是沒有數據受影響(就是沒有DML語句的條件沒有命中數據),將count_***不會統計進來,但是sum_timer_wait會統計進來
這就存在一個容易受到誤導的地方,這個索引明明沒有命中過很多次,但是卻產生了大量的timer_wait,索引看到類似的信息,也不能貿然刪除索引。

 

等待事件統計

MySQL資料庫中的任何一個動作,都需要等待(一定的時間來完成),一共有超過1000個等待事件,分屬不懂的類別,每個版本都不一樣,且預設不是所有的等待事件都啟用。

個人認為等待事件這個東西,僅做參考,不具備問題的診斷性,即便是再優化或者低負載的資料庫,累計一段時間,某些事件仍舊會積累大量的等待事件。
這些事件的等待事件,不一定都是負面性的,比如事物的鎖等待,是在併發執行過程中必然會生成的,這個等待事件的統計結果,也是累計的,單純的看一個直接的值,不具備任何參考意義。
除非定期收集,做差值計算,根據實際情況,才具備參考意義。

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;

SELECT SUBSTRING_INDEX(NAME, '/', 1) as wait_type,COUNT(1)  
FROM performance_schema.setup_instruments
GROUP BY 1  
ORDER BY 2 DESC;


SELECT
event_name,
count_star,
sum_timer_wait
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE event_name != 'idle'
order by sum_timer_wait desc
limit 100;

 

最後,需要註意的是,
1,MySQL提供的諸多的系統表(視圖)中的數據,單純的看這個值本身,因為它是一個累計值,個人覺得意義不大,尤其是avg_***,需要結合多方面的綜合因素,做參考使用。
2,任何系統表的查詢,都可能對系統性能的本身造成一定的影響,不要再對系統可能產生較大負面影響的情況下做數據的統計收集。

 

參考:

http://blog.woqutech.com/

https://www.cnblogs.com/cchust/p/5061131.html

 

 

耐克的廣告,竟然是這麼的煽情

你能從一片空白里,看到可能嗎?
有些人要看到證據,等有人做到了才敢出手。
但那些第一個行動的人,他們等過嗎?
他們直接出手,不管有沒有人做到過。
你能從一片空白里,看到可能嗎?
不等別人,出手即證明。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 網上的教程坑賊雞兒多 一、安裝maven RocketMQ依賴maven打包,所以先要在虛擬機中安裝maven,我使用的是v3.3.9。 1:進入指定目錄下載maven 包 2:解壓並重新命名文件夾 3:修改倉庫地址為阿裡雲,不修改下載依賴的時候總報超時 在中間偏下的位置添加<mirror>標簽(默 ...
  • 1、安裝vue-i18n: npm install vue-i18n 如果npm長時間無反應,或安裝失敗,可以換成淘寶鏡像安裝: cnpm install vue-i18n 2、在main.js中引用: import VueI18n from 'vue-i18n' Vue.use(VueI18n) ...
  • 1. 更新yum源 CentOS7預設yum源的PostgreSQL版本過低,不適合在本版本上使用。在上找到適合CentOS7的RPM源,複製其url地址,使用yum安裝。 同時安裝epel(Extra Packages for Enterprise Linux 7),為了穩定性,CentOS7的默 ...
  • 一、用smbclient命令登錄成功但看不了文件 原因:SELinux的阻擋 解決:1、關閉SELinux : setenforce 0 (臨時生效,重啟後失效) 或vi /etc/sysconfig/selinux將SELINUX=enforcing改為SELINUX=disabled(永久生效) ...
  • 一. LVS概述 LVS是一種工作在四層協議上的負載均衡解決方案,在1998年5月由章文嵩博士創建。目前廣泛使用的負載均衡模型主要有: 1)工作在四層協議(LVS):主要用於四層協議上的負載均衡,性能相較於工作在七層的協議更好。但是協議棧是工作在傳輸層,對於傳輸層以上的高級特性支持不足。 2)工作在 ...
  • 卸載Hyper-V,然後重裝,再重啟已有的Hyper-V伺服器,報錯如下: 嘗試啟動選定的虛擬機時出錯。“SP2019SER”無法更改狀態。 原因:卸載後導致虛擬網卡出現問題導致的。 解決辦法: 右擊目標虛擬機 設置 網路適配器,提示有配置錯誤,將其修正即可。 ...
  • 這兩天遇到一個頭疼的問題,我們系統需要請求第三方數據,第三方收到請求後會生成相應的數據併入庫,我們通過定時任務將第三方數據同步到我們資料庫。當我們發送請求後第三方會立即返回一個值,我們會根據返回值去資料庫更新同步過來的表欄位,sql語句執行完了,沒有任何錯誤,在同步表中查看同步的數據都有且where ...
  • 引言 MySQL中定義數據欄位的類型對你資料庫的優化是非常重要的。 MySQL支持多種類型,大致可以分為三類:數值、日期/時間和字元串(字元)類型,如下腦圖所示: 數值類型 其中: 這些類型,是定長的,其容量是不會隨著後面的數字而變化的,比如int(11)和int(8),都是一樣的占4位元組。tiny ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...