1. 程式人生 > 其它 >利用Anemometer做mysql慢日誌的查詢與視覺化

利用Anemometer做mysql慢日誌的查詢與視覺化

Anemometer 是一個圖形化顯示MySQL慢日誌的工具。結合pt-query-digest,Anemometer可以很輕鬆的幫你去分析慢查詢日誌,讓你很容易就能找到哪些SQL需要優化。

一、首先安裝LNMP環境

二、percona-toolkit工具的安裝

pt-query-digest是percona-toolkit裡面一個工具,其作用就是分析慢查詢日誌,將MySQL慢查詢日誌進行統計並友好的顯示出來

1、安裝依賴包

[root@zabbix_server bin]# yum install perl-DBI perl-DBD-MySQL perl-IO-Socket-SSL perl
-Digest-MD5 -y

2、下載percona-toolkit二進位制包

[root@zabbix_server conf.d]# mkdir /usr/local/src/percona-toolkit
[root@zabbix_server conf.d]# cd /usr/local/src/percona-toolkit
[root@zabbix_server percona-toolkit]# wget https://www.percona.com/downloads/percona-toolkit/3.0.10/binary/tarball/percona-toolkit-3.0.10_x86_64.tar.gz
--2020-04-07 16:44:49-- https://www.percona.com/downloads/percona-toolkit/3.0.10/binary/tarball/percona-toolkit-3.0.10_x86_64.tar.gz 正在解析主機 www.percona.com... 74.121.199.234 正在連線 www.percona.com|74.121.199.234|:443... 已連線。 已發出 HTTP 請求,正在等待迴應... 200 OK 長度:8170395 (7.8M) [application/x-gzip] 正在儲存至: “percona-toolkit-3.0.10_x86_64.tar
.gz” 28% [============================> ] 2,312,153 693K/s eta(英國中部時33% [==================================> ] 2,770,905 759K/s eta(英國中部時39% [========================================> ] 3,229,657 817K/s eta(英國中部時45% [==============================================> ] 3,688,409 871K/s eta(英國中部時50% [====================================================> ] 4,147,161 916K/s eta(英國中部時56% [==========================================================> ] 4,605,913 953K/s eta(英國中部時61% [================================================================> ] 5,064,665 986K/s eta(英國中部時67% [======================================================================> ] 5,523,417 1014K/s eta(英國中部時73% [============================================================================> ] 5,982,169 1.02M/s eta(英國中部時77% [=================================================================================> ] 6,326,233 1.01M/s eta(英國中部時83% [=======================================================================================> ] 6,850,521 1.10M/s eta(英國中部時87% [============================================================================================> ] 7,178,201 1.15M/s eta(英國中部時91% [================================================================================================> ] 7,505,881 1.25M/s eta(英國中部時96% [=====================================================================================================> ] 7,866,329 1.29M/s eta(英國中部時100%[=========================================================================================================>] 8,170,395 1.37M/s in 7.1s 2020-04-07 16:44:59 (1.09 MB/s) - 已儲存 “percona-toolkit-3.0.10_x86_64.tar.gz” [8170395/8170395]) [root@zabbix_server percona-toolkit]#

解壓

[root@zabbix_server percona-toolkit]# tar -zxvf percona-toolkit-3.0.10_x86_64.tar.gz 
percona-toolkit-3.0.10/
percona-toolkit-3.0.10/CONTRIBUTE.md
percona-toolkit-3.0.10/Makefile.PL
percona-toolkit-3.0.10/docker-compose.yml
percona-toolkit-3.0.10/CONTRIBUTING.md
percona-toolkit-3.0.10/Gopkg.lock
percona-toolkit-3.0.10/README.md
percona-toolkit-3.0.10/bin/
percona-toolkit-3.0.10/bin/pt-summary
percona-toolkit-3.0.10/bin/pt-slave-delay
percona-toolkit-3.0.10/bin/pt-mongodb-query-digest
percona-toolkit-3.0.10/bin/pt-slave-restart
percona-toolkit-3.0.10/bin/pt-variable-advisor
percona-toolkit-3.0.10/bin/pt-fingerprint
percona-toolkit-3.0.10/bin/pt-secure-collect
percona-toolkit-3.0.10/bin/pt-index-usage
percona-toolkit-3.0.10/bin/pt-archiver
percona-toolkit-3.0.10/bin/pt-find
percona-toolkit-3.0.10/bin/pt-heartbeat
percona-toolkit-3.0.10/bin/pt-fifo-split
percona-toolkit-3.0.10/bin/pt-fk-error-logger
percona-toolkit-3.0.10/bin/pt-mysql-summary
percona-toolkit-3.0.10/bin/pt-online-schema-change
percona-toolkit-3.0.10/bin/pt-table-usage
percona-toolkit-3.0.10/bin/pt-align
percona-toolkit-3.0.10/bin/pt-query-digest
percona-toolkit-3.0.10/bin/pt-ioprofile
percona-toolkit-3.0.10/bin/pt-visual-explain
percona-toolkit-3.0.10/bin/pt-stalk
percona-toolkit-3.0.10/bin/pt-mext
percona-toolkit-3.0.10/bin/pt-table-checksum
percona-toolkit-3.0.10/bin/pt-show-grants
percona-toolkit-3.0.10/bin/pt-pmp
percona-toolkit-3.0.10/bin/pt-upgrade
percona-toolkit-3.0.10/bin/pt-diskstats
percona-toolkit-3.0.10/bin/pt-sift
percona-toolkit-3.0.10/bin/pt-config-diff
percona-toolkit-3.0.10/bin/pt-slave-find
percona-toolkit-3.0.10/bin/pt-kill
percona-toolkit-3.0.10/bin/pt-duplicate-key-checker
percona-toolkit-3.0.10/bin/pt-deadlock-logger
percona-toolkit-3.0.10/bin/pt-mongodb-summary
percona-toolkit-3.0.10/bin/pt-table-sync
percona-toolkit-3.0.10/lib/
percona-toolkit-3.0.10/docs/
percona-toolkit-3.0.10/docs/percona-toolkit.pod
percona-toolkit-3.0.10/Gopkg.toml
percona-toolkit-3.0.10/MANIFEST
percona-toolkit-3.0.10/COPYING
percona-toolkit-3.0.10/Changelog
percona-toolkit-3.0.10/INSTALL
[root@zabbix_server percona-toolkit]# mv percona-toolkit-3.0.10 /usr/local/

3、執行檢視版本

[root@zabbix_server bin]# ./pt-query-digest --version
pt-query-digest 3.0.10
[root@zabbix_server bin]# 

三、安裝anemometer

1、下載原始碼

[root@zabbix_server www]# cd /var/www/
[root@zabbix_server www]# git clone git://github.com/box/Anemometer.git anemometer
Initialized empty Git repository in /var/www/anemometer/.git/
remote: Enumerating objects: 1218, done.
remote: Total 1218 (delta 0), reused 0 (delta 0), pack-reused 1218
Receiving objects: 100% (1218/1218), 1.51 MiB | 2 KiB/s, done.
Resolving deltas: 100% (627/627), done.
[root@zabbix_server www]# 

2、在nginx配置anemometer根目錄

server {
  2     listen       8083;
  3     server_name  172.28.18.75;
  4 
  5     #charset koi8-r;
  6     #access_log  /var/log/nginx/host.access.log  main;
  7 
  8     location / {
  9       root   /var/www/anemometer;
 10       index  index.php index.html index.htm;
 11     }
 12 

瀏覽器開啟http://172.28.18.75:8083

3、配置目標資料庫源

首先複製一份示例配置檔案sample.config.inc.php並改名為config.inc.php

[root@zabbix_server conf]# cp sample.config.inc.php config.inc.php 
[root@zabbix_server conf]# 

首先修改資料庫連線配置檔案datasource_localhost.inc.php

[root@zabbix_server conf]# vim datasource_localhost.inc.php 

  1 <?php
  2 $conf['datasources']['localhost'] = array(
  3     'host'  => 'localhost',
  4     'port'  => 3306,
  5     'db'    => 'slow_query_log',
  6     'user'  => 'root',
  7     'password' => 'xxxxxxxx',
  8     'tables' => array(
  9         'global_query_review' => 'fact',
 10         'global_query_review_history' => 'dimension'
 11     ),
 12     'source_type' => 'slow_query_log'
 13 );
 14 

db:slow_query_log :這是慢查詢日誌存放的資料庫,由/var/www/anemometer/install.sql指令碼建立,下面的操作將會有匯入這個SQL檔案

4、匯入install.sql初始化資料來源的資料庫表的配置

mysql> source /var/www/anemometer/install.sql;
Query OK, 0 rows affected, 1 warning (0.05 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
Query OK, 0 rows affected (0.10 sec)

ERROR 1067 (42000): Invalid default value for 'ts_min'

報錯,檢視install.sql,發現ts_min列的定義為`ts_min` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',

這是mysql5.7以上版本里設定的sql_mode引數的關係,檢視sql_mode引數設定

mysql> show variables like '%sql_mode%';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)
NO_ZERO_IN_DATE,NO_ZERO_DATE:表示不允許日期資料為零值,將sql_mode重新設定
mysql> set sql_mode='STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show variables like '%sql_mode%';
+---------------+-------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                     |
+---------------+-------------------------------------------------------------------------------------------+
| sql_mode      | STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

再匯入install.sql

mysql> source /var/www/anemometer/install.sql;
Query OK, 1 row affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.03 sec)

mysql> 

新建了一個slow_query_log資料庫裡面新建了兩個表:global_query_review和global_query_review_history,用於儲存從目標資料庫利用pt-query-digest工具收集的慢日誌資料。

5、在目標資料庫上使用pt-query-digest工具收集日誌推送到slow_query_log資料庫中

[root@zabbix_server bin]# ./pt-query-digest --user=root --password=Zaq1xsw@ --review h=172.28.18.75,D=slow_query_log,t=global_query_review --history h=172.28.18.75,D=slow_query_log,t=global_query_review_history --no-report --limit=0% --filter=" \$event->{Bytes} = length(\$event->{arg}) and \$event->{hostname}=\"$HOSTNAME\"" /home/mysql_data/mysql/zabbix_server-slow.log 

執行完上面語句,global_query_review和global_query_review_history就會有統計的資料資訊

重新整理頁面,可以看到慢日誌資料展示

四、資料庫說明

主要欄位說明:

  • hostname_max : MySQL服務所在主機名稱
  • db_max: 資料庫名稱
  • checksum : 同global_query_review表中的checksum,兩張表通過該值關聯
  • sample : sql示例
  • ts_min : 本次統計(每10分鐘一次)該型別sql語句出現的最小時間
  • ts_max: 本次統計(每10分鐘一次)該型別sql語句出現的最大時間
  • ts_cnt : 本次統計該sql語句出現的次數
  • Query_time_sum : 本次統計該型別sql語句花費的總時間
  • Query_time_min : 本次統計該型別sql語句執行最快的那個sql語句花費的時間
  • Query_time_max: 本次統計該型別sql語句執行最慢的那個sql語句花費的時間
  • Query_time_pct_95: 本次統計該型別sql語句執行時間位於95%分位的sql執行時間
  • Query_time_stddev: 本次統計該型別sql語句執行時間標準差(統計學概念)
  • Query_time_median: 本次統計該型別sql語句執行時間位於中位數位置的sql執行時間
  • index_ratio:表示的是掃描的行數/返回的結果行數

五、配置anemometer

1、首先將“custom_fields“數組裡的”'snippet' => 'LEFT(dimension.sample,50)'改為'snippet' => 'dimension.sample',

2、history表裡的ts_cnt列是Sum出來的,如果我們5分鐘收集一次日誌,這個結果完全不是想要的,增加一個數組元素'query_cnt' => 'MAX(ts_cnt)',這個資料為語句的執行次數,並且將history_defaults和report_defaults的table_fields配置中將ts_cnt改為了query_cnt。

3、單個查詢的過去90天曆史記錄裡,它是以ts_min為group by的,因為history表裡ts_min只有一個值,既語句第一次執行的時間,所以這會只顯示一條資料,而不是每天的資料,這裡改成ts_max。既語句最後執行時間將'date' => 'DATE(ts_min)改為'ate' => 'DATE(ts_max)'

4、$conf['report_defaults'] = array陣列儲存頁面統計列表裡的資料欄位和查詢條件,可以在這裡進行個性化設定,來顯示需要的資料

// custom fields
339     'custom_fields' => array(
340         'checksum' => 'checksum',
341         'snippet' => 'dimension.sample',
342         'query_cnt' => 'MAX(ts_cnt)',
343         'query_max' => 'ROUND(MAX(query_time_max),2)',
344         'query_min' => 'ROUND(MIN(Query_time_min),2)',
345         'query_avg' => 'ROUND(SUM(Query_time_sum)/SUM(ts_cnt),2)',
346         'lock_max' => 'ROUND(MAX(Lock_time_max),4)',
347         'lock_min' => 'ROUND(MIN(Lock_time_min),4)',
348         'lock_avg' => 'ROUND(SUM(Lock_time_sum)/SUM(ts_cnt),4)',
349         'rows_sent_avg' => 'ROUND(SUM(Rows_sent_sum)/SUM(ts_cnt))',
350         'rows_examined_avg' => 'ROUND(SUM(Rows_examined_sum)/SUM(ts_cnt))',
351         'date'  => 'DATE(ts_max)',
352         'hour'  => 'substring(ts_min,1,13)',
353         'hour_ts'   => 'round(unix_timestamp(substring(ts_min,1,13)))',
354         'minute_ts'     => 'round(unix_timestamp(substring(ts_min,1,16)))',
355         'minute'        => 'substring(ts_min,1,16)',

5、對於某些慢日誌裡Query ID是0xFF6BA40AE1ADBA294B52E89F1929E3F8類似這樣的ID,在頁面顯示中會查詢不到歷史記錄,這是因為Anemometer把checksum列設定為了bigint型別,所以,我們需要將checksum列設定為varchar(60)字元型,並修改PHP程式碼中相應的地方

修改config.inc.php檔案,搜尋"dec2hex"

將這句註釋掉

360     'callbacks'     => array(
361         'table' => array(
362             'date'  => function ($x) { $type=''; if ( date('N',strtotime($x)) >= 6) { $type = 'weekend'; } return array($x,$type); },
363             #'checksum' => function ($x) { return array(dec2hex($x), ''); }
364         )
365     )

修改lib/Anemometer.php檔案搜尋"translate_checksum()"函式,將裡面的"return $this->bchexdec($checksum);"註釋掉,在下面增加一句"return $checksum;"

private function translate_checksum($checksum)
359     {
360     if (!in_array($this->data_model->get_source_type(), array('slow_query_log','default')))
361     {
362         return $checksum;
363     }
364 
365         if (preg_match('/^[0-9]+$/', $checksum))
366         {
367             return $checksum;
368         }
369         else if (preg_match('/^[0-9A-Fa-f]+$/', $checksum))
370         {
371             #return $this->bchexdec($checksum);
372             return $checksum;
373         }
374         else if (strlen($checksum) == 0)
375         {
376             return null;
377         }
378         else
379         {
380             throw new Exception("Invalid query checksum");
381         }
382     }

修改AnemometerModel.php 檔案裡相關使用$checksum作為條件查詢的語句,在變數的兩邊加上單引號,表示引用的是字串

搜尋"$checksum"進行相應修改

public function checksum_exists($checksum) {
200         $checksum_field_name = $this->get_field_name('checksum');
201         $query = "SELECT `{$checksum_field_name}` FROM `{$this->fact_table}` WHERE `{$checksum_field_name}`='" . $this->mysqli->real_escape_string($checksum) . "'";
public function update_query($checksum, $fields) {
218         $mysqli = $this->mysqli;
219         $checksum_field_name = $this->get_field_name('checksum');
220         $sql = "UPDATE `{$this->fact_table}` SET ";
221         $sql .= join(
222                 ',', array_map(
223                         function ($x, $y) use ($mysqli) {
224                             if ($y == 'NULL') {
225                                 return "{$x} = NULL";
226                             }
227                             return "`{$x}` = \"" . $mysqli->real_escape_string($y) . '"';
228                         }, array_keys($fields), array_values($fields)
229                 )
230         );
231         $sql .= " WHERE `{$checksum_field_name}`='" . $this->mysqli->real_escape_string($checksum) . "'";
public function get_query_by_checksum($checksum) {
244         $checksum_field_name = $this->get_field_name('checksum');
245         $result = $this->mysqli->query("SELECT * FROM `{$this->fact_table}` WHERE `{$checksum_field_name}`='{$checksum}'");
246         check_mysql_error($result, $this->mysqli);
247         if ($row = $result->fetch_assoc()) {
248             return $row;
249         }
250         return null;
251     }
public function get_query_samples($checksum, $limit = 1, $offset = 0) {
262         $checksum_field_name = $this->get_field_name('checksum');
263         $time_field_name = $this->get_field_name('time');
264         $table = $this->dimension_table;
265         if ($this->get_source_type() == 'performance_schema')
266         {
267             $table = $this->fact_table;
268         }
269         $sql = "SELECT * FROM `{$table}` WHERE `{$checksum_field_name}`='{$checksum}' ORDER BY `{$time_field_name}` DESC LIMIT {$limit} OFFSET     {$offset}";
270         return $this->mysqli->query($sql);
271     }
public function checksum_exists($checksum) {
200         $checksum_field_name = $this->get_field_name('checksum');
201         $query = "SELECT `{$checksum_field_name}` FROM `{$this->fact_table}` WHERE `{$checksum_field_name}`='" . $this->mysqli->real_escape_string($checksum) . "'";
202         //print "query: {$query}<br>"; 
203         $result = $this->mysqli->query($query);
204         check_mysql_error($result, $this->mysqli);
205         if ($result->num_rows) {
206             return true;
207         }
208         return false;
209     }

儲存退出後,即可查詢正常