對于MySQL數據庫主從復制延遲的監控,可以借助percona的有力武器pt-heartbeat來實現。
pt-heartbeat的工作原理通過使用時間戳方式在主庫上更新特定表,然后在從庫上讀取被更新的時間戳然后與本地系統時間對比來得出其延遲。具體流程:
? ?1)在主上創建一張heartbeat表,按照一定的時間頻率更新該表的字段(把時間更新進去)。監控操作運行后,heartbeat表能促使主從同步!
? ?2)連接到從庫上檢查復制的時間記錄,和從庫的當前系統時間進行比較,得出時間的差異。
使用方法(主從和從庫上都可以執行監控操作):
pt-heartbeat [OPTIONS] [DSN] --update|--monitor|--check|--stop
注意:需要指定的參數至少有 --stop,--update,--monitor,--check。
其中--update,--monitor和--check是互斥的,--daemonize和--check也是互斥。
--ask-pass 隱式輸入MySQL密碼
--charset 字符集設置
--check 檢查從的延遲,檢查一次就退出,除非指定了--recurse會遞歸的檢查所有的從服務器。
--check-read-only 如果從服務器開啟了只讀模式,該工具會跳過任何插入。
--create-table 在主上創建心跳監控的表,如果該表不存在,可以自己手動建立,建議存儲引擎改成memory。通過更新該表知道主從延遲的差距。
CREATE TABLE heartbeat (ts varchar(26) NOT NULL,server_id int unsigned NOT NULL PRIMARY KEY,file varchar(255) DEFAULT NULL,position bigint unsigned DEFAULT NULL,relay_master_log_file varchar(255) DEFAULT NULL,exec_master_log_pos bigint unsigned DEFAULT NULL
);
heratbeat 表一直在更改ts和position,而ts是我們檢查復制延遲的關鍵。
--daemonize 執行時,放入到后臺執行
--user=-u, 連接數據庫的帳號
--database=-D, 連接數據庫的名稱
--host=-h, 連接的數據庫地址
--password=-p, 連接數據庫的密碼
--port=-P, 連接數據庫的端口
--socket=-S, 連接數據庫的套接字文件
--file 【--file=output.txt】 打印--monitor最新的記錄到指定的文件,很好的防止滿屏幕都是數據的煩惱。
--frames 【--frames=1m,2m,3m】 在--monitor里輸出的[]里的記錄段,默認是1m,5m,15m。可以指定1個,如:--frames=1s,多個用逗號隔開。可用單位有秒(s)、分鐘(m)、小時(h)、天(d)。
--interval 檢查、更新的間隔時間。默認是見是1s。最小的單位是0.01s,最大精度為小數點后兩位,因此0.015將調整至0.02。
--log 開啟daemonized模式的所有日志將會被打印到制定的文件中。
--monitor 持續監控從的延遲情況。通過--interval指定的間隔時間,打印出從的延遲信息,通過--file則可以把這些信息打印到指定的文件。
--master-server-id 指定主的server_id,若沒有指定則該工具會連到主上查找其server_id。
--print-master-server-id 在--monitor和--check 模式下,指定該參數則打印出主的server_id。
--recurse 多級復制的檢查深度。模式M-S-S...不是最后的一個從都需要開啟log_slave_updates,這樣才能檢查到。
--recursion-method 指定復制檢查的方式,默認為processlist,hosts。
--update 更新主上的心跳表。
--replace 使用--replace代替--update模式更新心跳表里的時間字段,這樣的好處是不用管表里是否有行。
--stop 停止運行該工具(--daemonize),在/tmp/目錄下創建一個“pt-heartbeat-sentinel” 文件。后面想重新開啟則需要把該臨時文件刪除,才能開啟(--daemonize)。
--table 指定心跳表名,默認heartbeat。
實例說明:
master:192.168.1.101
slave:192.168.1.102
同步的庫:test、test
主從庫都能使用root賬號、密碼123456登錄
先操作針對test庫的檢查,其他同步的庫的檢查操作類似!
更新主庫上的heartbeat,--interval=1表示1秒鐘更新一次(注意這個啟動操作要在主庫服務器上執行)
[root@vm371 fulltable]# pt-heartbeat --user=root --ask-pass --host=104.2.142.146 --create-table -D test --interval=1 --update --replace --daemonize
Enter password:
[root@vm371 fulltable]# ps -ef|grep pt-heartbeat
root ? ? 14073 ? ? 1 ?0 14:06 ? ? ? ? ?00:00:00 perl /app/percona-toolkit-3.3.1/bin/pt-heartbeat --user=root --ask-pass --host=10.2.132.166 --create-table -D test --interval=1 --update --replace --daemonize
root ? ? 14154 12310 ?0 14:06 pts/0 ? ?00:00:00 grep --color=auto pt-heartbeat
?
mysql> select * from heartbeat\G;
*************************** 1. row ***************************
? ? ? ? ? ? ? ? ? ?ts: 2021-08-02T14:27:23.002120
? ? ? ? ? ? server_id: 1583306
? ? ? ? ? ? ? ? ?file: mysql-bin.000026
? ? ? ? ? ? ?position: 332806008
relay_master_log_file:
? exec_master_log_pos: 0
1 row in set (0.00 sec)
ERROR:
No query specified
?
在主庫運行監測同步延遲:
[root@vm371 fulltable]# pt-heartbeat -D test --table=heartbeat --monitor --host=104.2.132.146 --user=root --password=123456
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
0.00s [ ?0.00s, ?0.00s, ?0.00s ]
?
解釋:0表示從沒有延遲。?[ 0.00s, 0.00s, 0.00s ] 表示1m,5m,15m的平均值。可以通過--frames去設置。
或者加上--master-server-id參數(主庫my.cnf里配置的server-id值)也可以將主庫的server-id打印出來(--print-master-server-id)
[root@vm371 fulltable]# pt-heartbeat -D test?--table=heartbeat --monitor --host=190.29.132.156 --user=root --password=123456 --print-master-server-id
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
0.00s [ ?0.00s, ?0.00s, ?0.00s ] 1583306
上面的監測命令會一直在運行狀態中,可以使用--check監測一次就退出
注意:使用了--check,就不能使用--monit
--update,--monitor和--check是互斥的,--daemonize和--check也是互斥。
通過pt-heartbeart工具可以很好的彌補默認主從延遲的問題,但需要搞清楚該工具的原理。
默認的Seconds_Behind_Master值是通過將服務器當前的時間戳與二進制日志中的事件時間戳相對比得到的,所以只有在執行事件時才能報告延時。備庫復制線程沒有運行,也會報延遲null。
還有一種情況:大事務,一個事務更新數據長達一個小時,最后提交。這條更新將比它實際發生時間要晚一個小時才記錄到二進制日志中。當備庫執行這條語句時,會臨時地報告備庫延遲為一個小時,執行完后又很快變成0。
一般上而言I/O線程不會造成過大的延遲,主要的延遲還是在SQL線程上:
Master_Log_File:表示從庫I/O線程當前讀取Binlog的文件名,如果比主庫當前的binlog日志還小的話說明從庫I/O接受主庫的日志慢了。
Read_Master_Log_File:表示SQL線程正在應用的Relay Log對應的Binlog,如果這個binlog文件比較老,說明SQL線程應用日志的速度過慢,因此基本可以判斷出延遲的線程是SQL線程了。
上面兩個多用于進行比較。
Read_Master_Log_Pos:表示從庫I/O線程讀取主庫Binlog的位置。
Exec_Master_Log_Pos:表示SQL線程正在應用Relay Log的位置對應于主庫Binlog的位置。
你可以通過 Read_Master_Log_Pos – Exec_Master_Log_Pos 得到的差值來計算SQL_THREAD的落后,但前提是Master_Log_File和Relay_Master_Log_File是相同的
1、首先看 Relay_Master_Log_File 和 Master_Log_File 是否有差異
2、如果Relay_Master_Log_File 和 Master_Log_File 有差異的話,那說明延遲很大
3、如果Relay_Master_Log_File 和 Master_Log_File 沒有差異,再來看Exec_Master_Log_Pos 和 Read_Master_Log_Pos 的差異,那么更加嚴謹的做法是同時在主庫執行show master status和在從庫上面執行show slave status 的輸出進行比較。MHA就是這樣保證數據一致性的。MMM都沒有做到。這也算MHA比MMM更加優秀的地方。
#!/bin/bash
# 判斷主從復制是否延遲
# write by yayun 2014-07-23
# http://www.cnblogs.com/gomysql/# slave
s_psswd=123456
s_user=root
s_port=3306
s_host=localhost# master
m_psswd=123456
m_user=root
m_port=3306
m_host=192.168.0.102slave_wan_ip=`ifconfig | sed -n '/inet /{s/.*addr://;s/ .*//;p}' | head -n1`while true
dosleep 1echo -e "\e[1;33m###################################\e[0m"Master_Log_File=$(mysql -u$s_user -p$s_psswd -h$s_host -P$s_port -e "show slave status\G" | grep -w Master_Log_File | awk -F": " '{print $2}')Relay_Master_Log_File=$(mysql -u$s_user -p$s_psswd -h$s_host -P$s_port -e "show slave status\G" | grep -w Relay_Master_Log_File | awk -F": " '{print $2}')Read_Master_Log_Pos=$(mysql -u$s_user -p$s_psswd -h$s_host -P$s_port -e "show slave status\G" | grep -w Read_Master_Log_Pos | awk -F": " '{print $2}')Exec_Master_Log_Pos=$(mysql -u$s_user -p$s_psswd -h$s_host -P$s_port -e "show slave status\G" | grep -w Exec_Master_Log_Pos | awk -F": " '{print $2}'|sed 's/[ \t]*$//g')Master_Log_File_Num=`echo $Master_Log_File | awk -F '.' '{print $2}' | sed 's/^0\+//'`Master_File=$(mysql -u$m_user -p$m_psswd -h$m_host -P$m_port -Nse "show master status" | awk '{print $1}')Master_Pos=$(mysql -u$m_user -p$m_psswd -h$m_host -P$m_port -Nse "show master status" | awk '{print $2}'|sed 's/[ \t]*$//g')Master_File_Num=`echo $Master_File | awk -F '.' '{print $2}' | sed 's/^0\+//'`if [ -z $Master_Log_File ] && [ -z $Relay_Master_Log_File ] && [ -z $Read_Master_Log_Pos ] && [ -z $Exec_Master_Log_Pos ]thenecho -e "\e[1;31mSLAVE 沒有取到值,請檢查參數設置!\e[0m"exit 1fiif [ $Master_Log_File = $Relay_Master_Log_File ] && [ $Read_Master_Log_Pos = $Exec_Master_Log_Pos ]thenif [ $Master_Log_File = $Master_File ] && [ $Exec_Master_Log_Pos = $Master_Pos ]thenecho -e "\e[1;32mMaster-slave 復制無延遲 ^_^\e[0m"elseif [ $Master_Log_File_Num -gt $Master_File_Num ] || [ $Master_Pos -gt $Exec_Master_Log_Pos ]thenlog_count=$(expr $Master_Log_File_Num - $Master_File_Num)pos_count=$(expr $Master_Pos - $Exec_Master_Log_Pos)echo -e "\e[1;31mMaster-slave 復制延遲 !!!\e[0m"echo -e "\e[1;31mMaster:$m_host Slave:$slave_wan_ip\e[0m"echo -e "\e[1;31mMaster當前binlog: $Master_File"echo -e "\e[1;31mSlave當前binlog: $Master_Log_File"echo -e "\e[1;31mbinlog相差文件數: $log_count\e[0m"echo -e "\e[1;31mPos點相差: $pos_count\e[0m"fififi
done