案例7、監控MySQL主從狀態

在生產環境中,常見MySQL架構使用最多的就是主從了,所以對於主從是否同步數據的監控尤其重要。若是使用了專業的監控軟件(如zabbix)監控MySQL,那麼選擇監控工具提供的模板或插件去監控就很方便,可是若是涉及到一些特殊要求就另當別論了。咱們能夠寫shell腳原本實現定製化的需求。python

本案例須要寫一個shell腳原本監控MySQL主從,需求以下:mysql

1)每分鐘檢測一次,本次執行腳本時需檢測上一次是否執行完成,若是還未完成則本次不執行linux

2)若是不一樣步須要發送告警郵件給admin@admin.comsql

3)須要作告警收斂,在沒有解決問題前每隔30分鐘發一次告警郵件shell

4)假設本機mysql   root帳戶密碼爲tpH40Kznvvim


知識點一:特殊符號$?bash

在命令行或者在shell中,每執行完一條命令,都會有一個返回值,返回值爲0則表示這條命令執行成功,非0表示執行不成功,返回值能夠經過$?查看,以下:服務器

# ls
123 1.txt
# echo $?
0

在shell腳本中,要想判斷一條命令是否執行成功,就可使用$?的值是否爲0來實現。架構


知識點二:查看MySQL主從狀態ide

判斷MySQL主從是否正常有兩種方法,第一種是對比兩臺MySQL上的數據是否一致,第二種是經過執行「show slave status」指令查看輸出的結果,判斷主從狀態。第一種是最精準的,可是比較麻煩,因此一般用第二種來判斷MySQL主從狀態。

操做以下(在從機上執行):

# mysql -uroot -ptpH40Kznv -e "show slave status \G"
*************************** 1. row ***************************
               Slave_IO_State: Connecting to master
                  Master_Host: 192.168.93.130
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: aminglinux1.000001
          Read_Master_Log_Pos: 664307
               Relay_Log_File: CLAY-relay-bin.000001
                Relay_Log_Pos: 4
        Relay_Master_Log_File: aminglinux1.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 664307
              Relay_Log_Space: 120
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 2003
                Last_IO_Error: error connecting to master 'repl@192.168.93.130:3306' - retry-time: 60  retries: 1
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 0
                  Master_UUID: 
             Master_Info_File: /data/mysql/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 190718 18:49:05
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0

須要關注的行有:Slave_IO_Running、Slave_SQL_Running,只有這兩行的值所有是Yes纔算是主從同步狀態正常。 任何一個爲No,則不正常,當不正常時須要查看下面的Error(如Last_Error、Last_IO_error、Last_SQL_Error)相關信息進一步判斷形成不一樣步的緣由。

在生產環境中,有很大一部分問題不是主從不一樣步,而是主從延遲太嚴重,對於以上輸出信息中,有一行顯示的是主從延遲信息,

Seconds_Behind_Master,這個顯示的是一個時間,單位是秒,表示從落後主多少秒。這個值其實並不徹底精準,但咱們卻能夠經過這個數值判斷主從是否有延遲(保證主從兩臺機器時間設置一致)。


知識點三:shell中的數學運算

shell中的加減乘除和取餘有點特殊,主要是格式上和C不太同樣。

# a=1;b=2
# c=$[$a+$b]
# echo $c
3
# d=$[$b-$a]
# echo $d
1
# e=$[$a*$b]
# echo $e
2
# f=$[$b/$a]
# echo $f
2
# g=$[$b%$a]  //取餘
# echo $g
0

取餘隻能整數除以整數,若除數比被除數大,直接除數就是餘數;若除數比被除數小,被除數就除以除數直到剩下的數比除數小,則這個數就是餘數,並且注意餘數的符號要與被除數的符號一致。

c÷b,讀做c除以b(或b除c)。其中,c叫作被除數,b叫作除數。

在shell的數學運算中要想使用小數,或者限定小數位數,能夠藉助bc的scale實現:

# a=10;b=3
# echo $[$a/$b]
3
# echo "scale=2;$a/$b"|bc   //scale等於2表示小數有2位
3.33


知識點四:查看MySQL隊列

在Linux系統中,能夠經過ps、top等指令來查看系統的進程狀況,在MySQL中,能夠經過「show porcesslist"指令查看MySQL進程隊列。當MySQL服務器負載變高或者訪問卡頓時,查看一下進程隊列很是有必要。操做以下:

# mysql -uroot -ptpH40Kznv -e "show processlist"
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host      | db   | Command | Time | State | Info             |
+----+------+-----------+------+---------+------+-------+------------------+
|  2 | root | localhost | NULL | Query   |    0 | init  | show processlist |
+----+------+-----------+------+---------+------+-------+------------------+

由於是實驗機器並無什麼訪問量,因此只列出了show processlist自己。

有時候列出來的隊列命令太長從而顯示不完整,則須要使用另一個指令:show full processlist。


知識點五:shell中的內置變量

在shell腳本中,你會不會奇怪,哪來的$1和$2,其實就是shell腳本中的預設變量,其中$1的值就是在執行的時候輸入的第一個參數,$2就是執行的時候輸入的第二個參數,shell腳本的預設變量是沒有限制的。另外還有一個$0,它表明的是腳本自己的名字,編寫一個測試腳本,以下:

# vim option.sh
#!/bin/bash
echo "$1 $2 $0"

執行結果以下:

# sh option.sh 1 2
1 2 option.sh

若是這樣執行腳本,那$0的值會有所不一樣,以下:

# chmod a+x option.sh 
# ./option.sh 1 2
1 2 ./option.sh

就是說,$0的值會跟着執行時的命令變化,用絕對路徑它就會顯示絕對路徑,用相對路徑它就會顯示相對路徑。


本案例參考腳本:

完成本腳本最核心的一個知識點就是查看slave的狀態,而後藉助grep、sed、awk把想要的字符串截取出來,而後進行判斷是否和預期一致。腳本中的mail.py參考案例二中的mail.py。

#!/bin/bash
##檢測MySQL主從是否同步
##做者:
##日期:
##版本:v0.1

#把腳本名字存入變量s_name
s_name=`echo $0 | awk -F '/' '{print $NF}'`
Mysql_c="mysql -uroot -ptpH40Kznv"
mail_user=aming_test@163.com

#該函數實現郵件告警收斂,在案例五中出現過,通用
m_mail(){
   log=$1  #此處的$1表示第一個函數chk_sp
   t_s=`date +%s`
   t_s2=`date -d "1 hours ago" +%s`
   if [ ! -f /tmp/$log ]
   then
       #建立$log文件
       touch /tmp/$log
       #增長a權限,只容許追加內容,不容許更改或刪除
       chattr +a /tmp/$log
       #第一次告警,能夠直接寫入1小時之前的時間戳
       echo $t_s2 >> /tmp/$log
   fi
   #不管$log文件是不是剛剛建立,都須要查看最後一行的時間戳
   t_s2=`tail -1 /tmp/$log|awk '{print $1}'`
   #取出最後一行即上次告警的時間戳後,當即寫入當前的時間戳
   echo $t_s>>/tmp/$log
   #取兩次時間戳差值
   v=$[$t_s-$t_s2]
   #若是差值超過1800,當即發郵件
   if [ $v -gt 1800 ]
   then
      #發郵件,其中$2爲mail函數的第二個參數,這裏爲一個文件
      python mail.py $mail_user "磁盤使用率超過90%" "`cat $2`" 2>/dev/null
      #定義計數器臨時文件,並寫入0
      echo "0" > /tmp/$log.count
   else
      #若是計數器臨時文件不存在,須要建立並寫入0
      if [ ! -f /tmp/$log.count ]
      then
          echo "0" > /tmp/$log.count
      fi
      nu=`cat /tmp/$log.count`
      #30分鐘內每發生一次告警,計數器加1
      nu2=$[$nu+1]
      echo $nu2>/tmp/$log.count
      #當告警次數超過30次,須要再次發郵件
      if [ $nu2 -gt 30 ]
      then
          python mail.py $mail_user "磁盤使用率超過90%持續30分鐘了" "`cat $2`" 2>/dev/null
          #第二次告警後,將計數器再次從0開始
          echo "0" > /tmp/$log.count
      fi
    fi
}

#把進程狀況存入臨時文件,若是加管道求行數會有問題
ps aux |grep "$s_name" |grep -vE "$$|grep">/tmp/ps.tmp
p_n=`wc -l /tmp/ps.tmp|awk '{print $1}'`

#當進程數大於0,則說明上次的腳本還未執行完
if [ $p_n -gt 0 ]
then
    exit
fi

#先執行一條show processlist,看是否執行成功
$Mysql_c -e "show processlist" >/tmp/mysql_pro.log 2>/tmp/mysql_log.err

#若是上一條命令執行不成功,說明這個MySQL服務出現問題了。
if [ $? -gt 0 ]
then
    m_mail mysql_service_error /tmp/mysql_log.err
    exit
else
    $Mysql_c -e "show slave status\G" >/tmp/mysql_s.log
    n1=`wc -l /tmp/mysql_s.log | awk '{print $1}'`

    if [ $n1 -gt 0 ]
    then
        y1=`grep 'Slave_IO_Running:' /tmp/mysql_s.log | awk -F : '{print $2}' | sed 's/ //g'`
        y2=`grep 'Slave_SQL_Running:' /tmp/mysql_s.log | awk -F : '{print $2}' | sed 's/ //g'`

        if [ $y1 == "No" ] || [ $y2 == "No" ]
        then
            m_mail mysql_slavestatus_error /tmp/mysql_s.log
        fi
    fi
fi
相關文章
相關標籤/搜索