情景
經過zabbix監控發現有一個應用和數據庫之間存在不定時的流量異常(也不頻繁),具體爲應用server的入向流量和數據庫server的出向流量會有短期(一般在一分鐘左右)的激增,甚至快達到千兆網卡的傳輸上限。
###分析過程
經過症狀,幾乎能夠判定是因爲某些sql語句須要返回大量數據致使。但此次問題的分析結果確是很不順利(其中有我前期方法不當的緣由)。mysql
初次分析:由於在那以前作了一個針對於故障/異常分析的日誌分析系統(詳見),而且基於此完成了兩次異常uri的定位爲題。因此慣性思惟仍是用這套東西來對流量異常時間段內出現的請求,按照出現次數或響應時間或者響應大小排序,觀察。折騰了一通,然這個方法就不是分析這個問題最直接的方法,天然分析不出啥結果。
在這期間也想到了tcpdump抓包,可是有兩個問題:由於異常時間不固定;不合適的方法會致使抓包產生的文件體積很是大。當時也沒相處好方法來抓包,因此就沒再深究這個想法。sql
再次分析:仔細想仍是抓包纔是最直接的方法。因而寫了一個腳本,在應用server上每隔5s計算一下以前5s的入向流量的帶寬,根據應用和數據庫之間正常的流量設置一個閾值,超過次閾值則觸發抓包動做。此次是能看到一部分異常的流量了,可是因爲這個腳本的機制是先判斷流量再觸發抓包,因此先天缺陷是沒法抓到引起流量異常的sql的。數據庫
最終分析,大腦通過一晚上的後臺運行,終於想到了一個「完美」的抓包方法,大體思路以下:網絡
開始抓包,經過tcpdump的-w 參數將結果寫到文件。而後在死循環裏每隔5s計算一下前5s的平均流量,跟閾值相比:
若是小於閾值,則kill掉以前的tcpdump進行,從新開始一個tcpdump,抓包數據還寫到先前的文件中(會覆蓋掉以前內容,解決了抓包文件不斷變大的問題);
若是大於閾值,則繼續抓包,下一次循環再檢測流量,若下一個5s週期流量降到了閾值以內,則kill掉tcpdump進程,同時將抓包文件重命名(這個是能夠用來分析的有效抓包數據),而後再開啓新的tcpdump重複以前的動做。tcp
來看一下具體腳本(這個腳本是整個過程的關鍵)ide
#!/bin/sh #by ljk 2017/03/18 #對於不按期出現的網絡流量異常進行合理抓包 file=/proc/net/dev i=0 #用來標記是否出現了流量異常以及異常持續了幾個檢測週期 cd /usr/local/src/mysql-sniffer/logs/query/ alias capture='tcpdump -nnvv port 3306 -w tcpdump.txt &>/dev/null' (capture) & #放到後臺執行,不至於阻塞主進程的邏輯 while true;do RX_bytes=`cat $file|grep eth0|sed 's/^ *//g'|awk -F'[ :]+' '{print $2}'` sleep 10 #間隔10s RX_bytes_later=`cat $file|grep eth0|sed 's/^ *//g'|awk -F'[ :]+' '{print $2}'` speed_RX=`echo "scale=0;($RX_bytes_later - $RX_bytes)*8/1024/1024/10"|bc` tcpdump_pid=`ps -ef|grep tcpdump|grep -v grep|awk '{print $2}'` if [ $speed_RX -lt 15 ];then #個人閾值是15Mb kill -9 $tcpdump_pid if [ $i -gt 0 ];then mv tcpdump.txt "$i"_tcpdump_`date +"%F_%H:%M:%S"` fi (capture) & i=0 else i=$(($i+1)) fi done
把該腳本放到後臺運行,而後我很輕鬆的抓取到了一次異常週期內的數據包,而後經過wireshark進行分析,也很快的找到了問題sql,不出所料,就是由於對用戶輸入的驗證不夠嚴謹,致使where條件裏出現了空字串進而須要返回將近全表的數據(真坑)。.net