MYSQL 慢日誌收集和解析

公司使用Percona組件管理mysql,通過這個組件,各個mysql節點會定時上報各類監控指標到SERVER端存儲,但是監控指標只統計了兩個snapshot時間間隔中的慢查詢發生數量,沒有具體的SQL信息和過程數據,所以要想分析慢SQL,還需要回到原庫去查SLOWLOG。當集羣規模較大時(例如我們測試環境接近2000個MYSQL節點,跑不同的業務),逐臺統計費時費力,因此做了一個簡單的python腳本,去自動收集和分析這些慢日誌。

大致思路如下:
1、首先從多個server端獲取mysql節點信息,更新至臺賬表
2、由於各個mysql數據庫和操作系統的用戶密碼並不完全統一,因此需要先使用幾個常用用戶密碼去探測,並更新至臺賬表。
3、由於各個mysql數據庫系統時間不是自然時間,因此還需要獲取各個節點當前的操作系統時間。
4、連server端,檢查指定時間段內,是否出現過慢查詢
5、利用fabric去存在慢查詢的節點操作系統下載slowlog
6、解析slowlog併入庫保存

代碼如下,非常初級,請輕拍

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
import hashlib
from sqlalchemy import *
from sqlalchemy import exc
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import *
import traceback
import datetime
import time
import subprocess
import threading
import errno
import os
import re
import cx_Oracle
from email.mime.text import MIMEText
from email.mime.multipart import  MIMEMultipart
import smtplib
import timeout_decorator
import sys
import MySQLdb

# 臺賬庫
mc = {
    'Host':127.0.0.1',
    'Port': 3307,
    'User': 'test',
    'Pass': 'test',
    'db': 'test',
}

# 連接數據庫
try:
    conn = MySQLdb.connect(host=mc.get('Host', None), port=mc.get('Port', None),
                           user=mc.get('User', None), passwd=mc.get('Pass', None), db=mc.get('db', None), charset='utf8')
    conn.autocommit(True)
except Exception as e:
    print (e)


Base = declarative_base()
class MYAWRHOST(Base):
    __tablename__ = "myawr_host"
    id = Column("id", Integer(), primary_key=True)
    ip = Column("ip_addr", String(20))
    port = Column("port", Integer())
    dbname = Column("db_name", String(50))
    hostname = Column("hostname", String(50))
    version = Column("version", String(50))
    snapid = Column("snap_id", Integer())
    snaptime = Column("snap_time", DateTime())

class SLOWLOG(Base):
    __tablename__ = "slow_log"
    id = Column("id", Integer(), primary_key=True)
    mysql_ip = Column("mysql_ip", String(50))
    start_time = Column("start_time", Integer())
    db_user = Column("db_user", String(20))
    app_ip = Column("app_ip", String(15))
    sqlid = Column("SQLID", String(40))
    thread_id = Column("thread_id", Integer())
    exectime = Column("exec_duration", String(30))
    rows_sent = Column("rows_sent", Integer())
    rows_examined = Column("rows_examined", Integer())
    slowsql = Column("slowsql", Text())
    update_time=Column("update_time", DateTime())

class SLOWRESULT(Base):
    __tablename__ = "slow_result"
    id = Column("id", Integer(), primary_key=True)
    mysql_ip = Column("mysql_ip", String(50))
    client = Column("client", String(50))
    sqlid = Column("SQLID", String(40))
    exectime = Column("exec_duration", Integer())
    locktime = Column("lock_duration", Integer())
    rows_sent = Column("rows_sent", Integer())
    exec_count = Column("exec_count", Integer())
    slowsql = Column("slowsql", Text())
    fullstatic = Column("fullstatic", String(200))
    update_time=Column("update_time", DateTime())

class MYSQLMONILOG(Base):
    __tablename__ = "mysql_moni_log"
    jobid = Column("jobid", Integer(),autoincrement=True, primary_key=True)
    id = Column("id", Integer())
    ip = Column("ip_addr", String(20))
    port = Column("port", Integer())
    dbname = Column("db_name", String(50))
    hostname = Column("hostname", String(50))
    version = Column("version", String(50))
    snapid_begin = Column("snap_id_begin", Integer())
    snaptime_begin = Column("snap_time_begin", DateTime())
    snapid_end = Column("snap_id_end", Integer())
    snaptime_end = Column("snap_time_end", DateTime())
    jobname = Column("jobname", String(50))
    desc = Column("desc", String(50))
    status = Column("status", String(50))
    updatetime = Column("update_time", DateTime())

class TBFULLLOG(Base):
    __tablename__ = "tbfull_log"
    id = Column("id", Integer(), autoincrement=True, primary_key=True)
    MYSQL_IP = Column("MYSQL_IP", String(50))
    DB_NAME = Column("DB_NAME", String(50))
    snap_id = Column("snap_id", Integer())
    snap_time = Column("snap_time", Integer())
    query = Column("query", Text)
    db = Column("db", String(64))
    exec_count = Column("exec_count", Integer())
    total_latency = Column("total_latency", Integer())
    no_index_used_count = Column("no_index_used_count", Integer())
    no_good_index_used_count = Column("no_good_index_used_count", Integer())
    no_index_used_pct = Column("no_index_used_pct", Integer())
    rows_sent = Column("rows_sent", Integer())
    rows_examined = Column("rows_examined", Integer())
    rows_sent_avg = Column("rows_sent_avg", Integer())
    rows_examined_avg = Column("rows_examined_avg", Integer())
    first_seen = Column("first_seen", String(19))
    last_seen = Column("last_seen", String(19))
    digest = Column("digest", String(32))


dbstrmoni = "mysql://test:[email protected]:3307/test?charset=utf8"

def select(connstr,str):
    try:
        conn = cx_Oracle.connect(connstr)
        cursor0 = conn.cursor()
        cursor0.execute(str)
        res0 = cursor0.fetchall()
    except Exception as e:
        print(e)
        res0=""
    return res0

def selectsql(dbstr,sql):
    #dbstr = type + "://" + user + ":" + password + "@" + dbstr + "?charset=utf8"
    engine = create_engine(dbstr)
    DBsession = sessionmaker(bind=engine)
    session = DBsession()
    rset = []
    try:
        e = session.execute(sql)
        #rkey = e.keys()
        for x in e.fetchall():
            rset.append(list(x))
    except Exception as e:
        traceback.print_exc()
    finally:
        session.close()
    if len(rset)==0:
        print("selectsql查詢結果爲空:",dbstr,sql)
    return rset

def updatesql(dbstr,sql):
    #dbstr = type + "://" + user + ":" + password + "@" + dbstr + "?charset=utf8"
    engine = create_engine(dbstr)
    DBsession = sessionmaker(bind=engine)
    session = DBsession()
    try:
        session.execute(sql)
        session.commit()
        #rkey = e.keys()
    except Exception as e:
        print("updatesql查詢報錯",sql)
        traceback.print_exc()
    finally:
        session.close()

def setmysql(id, ip, port, dbname, hostname, version, snapid, snaptime):
    try:
        #print("insert",id, ip, port, dbname, hostname, version, snapid, snaptime)
        engine = create_engine("mysql://test:[email protected]:3307/test?charset=utf8")
        DBsession = sessionmaker(bind=engine)
        session = DBsession()
        myawrhost = MYAWRHOST(
            id=id,
            ip=ip,
            port=port,
            dbname=dbname,
            hostname=hostname,
            version=version,
            snapid=snapid,
            snaptime=snaptime
        )
        session.add(myawrhost)
        session.commit()
    except exc.SQLAlchemyError as e:
        traceback.print_exc()
        pass
    session.close()


def settbfulllog(id,MYSQL_IP,DB_NAME,ret2):
    try:
        #print("insert",id, ip, port, dbname, hostname, version, snapid, snaptime)
        engine = create_engine("mysql://test:[email protected]:3307/test?charset=utf8")
        DBsession = sessionmaker(bind=engine)
        session = DBsession()
        for i in range(len(ret2)):
            SNAP_ID = ret2[i][0]
            SNAP_TIME = ret2[i][1]
            QUERY = ret2[i][2]
            DB = ret2[i][3]
            EXEC_COUNT = ret2[i][4]
            TOTAL_LATENCY = ret2[i][5]
            no_index_used_count = ret2[i][6]
            no_good_index_used_count = ret2[i][7]
            no_index_used_pct = ret2[i][8]
            rows_sent = ret2[i][9]
            rows_examined = ret2[i][10]
            rows_sent_avg = ret2[i][11]
            rows_examined_avg = ret2[i][12]
            first_seen = ret2[i][13]
            last_seen = ret2[i][14]
            DIGEST = ret2[i][15]
            tbfulllog = TBFULLLOG(
                id=id,
                MYSQL_IP=MYSQL_IP,
                DB_NAME=DB_NAME,
                snap_id=SNAP_ID,
                snap_time=SNAP_TIME,
                query=QUERY,
                db=DB,
                exec_count=EXEC_COUNT,
                total_latency=TOTAL_LATENCY,
                no_index_used_count=no_index_used_count,
                no_good_index_used_count=no_good_index_used_count,
                no_index_used_pct=no_index_used_pct,
                rows_sent=rows_sent,
                rows_examined=rows_examined,
                rows_sent_avg=rows_sent_avg,
                rows_examined_avg=rows_examined_avg,
                first_seen=first_seen,
                last_seen=last_seen,
                digest=DIGEST
            )
            session.add(tbfulllog)
        session.commit()
    except exc.SQLAlchemyError as e:
        traceback.print_exc()
        pass
    session.close()



def geterror():
    sql0="SELECT ID,IP_ADDR,PORT,DB_NAME,HOSTNAME,VERSION FROM MYAWR_HOST where DESC1='連接失敗'"
    ret=selectsql(dbstrmoni,sql0)
    return ret

def awrserverip(dbstrawr):
    mysqldb=dbstrawr[dbstrawr.index("@") + 1:-1].split("/")[0]
    return  mysqldb
#step1 獲取主機清單

#step2 更新db信息表
def initjob(dbstrawr):
    sql0="SELECT ID,IP_ADDR,PORT,DB_NAME,HOSTNAME,VERSION FROM MYAWR_HOST"
    macs=selectsql(dbstrawr,sql0)
    for mac in macs:
        id=mac[0]
        ip=mac[1]
        port=mac[2]
        dbname=mac[3]
        hostname=mac[4]
        version=mac[5]
        sqlcheckdb="SELECT DB_NAME FROM TEST.MYAWR_HOST WHERE IP_ADDR='%s' AND PORT='%s'"%(ip,port)
        repcheckdb=selectsql(dbstrmoni,sqlcheckdb)
        if len(repcheckdb)==0:
            pattern = '[a-z]{3,4}-[0-9]{4}-[0-9]'  # Converts 'djm' to 'd.*j.*m'
            regex = re.compile(pattern)  # Compiles a regex.
            if regex.match(hostname):
                user="root"
                passwd="root"
                remoteday=getremoteday_docker(user,passwd,ip)
                if remoteday=="timeout":
                    user = "root"
                    passwd = "root123"
                    remoteday = getremoteday_docker(user,passwd,ip)
            else:
                user="sysadmin"
                passwd="pass1234"
                remoteday=getremoteday(user,passwd,ip)
            #print(ip,remoteday,len(remoteday),type(remoteday))
            if remoteday !="error":
                desc="連接成功"
                if remoteday==time.strftime("%Y%m%d"):
                    remoteday="realday"
                else:
                    remoteday="sysday"
                #setmysql(id,ip,port,dbname,hostname,version,snapid,snap_time)
            else:
                desc="連接失敗"
                remoteday="unknown"
                #setmysql(id,ip,port,dbname,hostname,version,snapid,snap_time)
            app,env=gethostinfo_new(hostname,ip)
            sql0="replace into test.myawr_host(id,ip_addr,port,app,env,db_name,hostname,version,desc1,remoteday,user1,passwd,awrdb) values('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s')"%(id,ip,port,app,env,dbname,hostname,version,desc,remoteday,user,passwd,awrserverip(dbstrawr))
            #print(sql0)
            updatesql(dbstrmoni,sql0)
        else:
            print(ip+":"+str(port)+"已經登記,跳過")

def addawrdb(dbstrawr):
    sql0="SELECT ID,IP_ADDR,PORT,DB_NAME,HOSTNAME,VERSION FROM MYAWR_HOST"
    macs=selectsql(dbstrawr,sql0)
    for mac in macs:
        id=mac[0]
        ip=mac[1]
        port=mac[2]
        dbname=mac[3]
        hostname=mac[4]
        version=mac[5]
        sqlcheckdb="SELECT DB_NAME FROM TEST.MYAWR_HOST WHERE IP_ADDR='%s' AND PORT='%s'"%(ip,port)
        repcheckdb=selectsql(dbstrmoni,sqlcheckdb)
        sql0 = "update  test.myawr_host set awrdb='%s' where ip_addr='%s' and port='%s'" % (awrserverip(dbstrawr),ip, port)
        # print(sql0)
        updatesql(dbstrmoni, sql0)

#step3 獲取需要計算的snapid
def getsnapid(realday,sysday,dbstrawr):
    # 更新歷史作業狀態
    sql4 = "UPDATE TEST.mysql_moni_log  SET STATUS='關閉' WHERE STATUS='待運行' AND JOBNAME='slowquery' "
    updatesql(dbstrmoni, sql4)
    sql0="SELECT ID,IP_ADDR ,PORT,DB_NAME,HOSTNAME,VERSION,DESC1,REMOTEDAY,USER1,PASSWD FROM TEST.MYAWR_HOST WHERE AWRDB='%s'"%awrserverip(dbstrawr)
    rets=selectsql(dbstrmoni,sql0)
    for ret in rets:
        id=ret[0]
        ip=ret[1]
        port=ret[2]
        dbname=ret[3]
        hostname=ret[4]
        version=ret[5]
        desc=ret[6]
        remoteday=ret[7]
        user = ret[8]
        passwd=ret[9]
        if desc=="連接成功":
            if remoteday=="realday":
                checkday=realday
                sql1 = "SELECT MIN(SNAP_ID),MAX(SNAP_ID)  FROM %s.myawr_snapshot where SNAP_TIME BETWEEN STR_TO_DATE('%s','%%Y%%m%%d') and STR_TO_DATE('%s','%%Y%%m%%d')" % (dbname, checkday, time.strftime("%Y%m%d"))
            else:
                checkday=sysday
                sql1 = "SELECT MIN(SNAP_ID),MAX(SNAP_ID)  FROM %s.myawr_snapshot where SNAP_TIME >= STR_TO_DATE('%s','%%Y%%m%%d')"  % (dbname, checkday)
            rets1=selectsql(dbstrawr,sql1)
            if len(rets1)>0:
                if rets1[0][0] != None and rets1[0][1] != None:
                    minsnapid=int(rets1[0][0])
                    maxsnapid=int(rets1[0][1])
                    if maxsnapid>minsnapid:
                        #初始化作業控制表
                        sql2="INSERT INTO TEST.mysql_moni_log VALUES(NULL,%s,'%s','%s','%s','%s','%s',%s,CURRENT_TIMESTAMP,'%s',CURRENT_TIMESTAMP,'%s','%s','%s',CURRENT_TIMESTAMP,'%s')"%(id,ip,port,dbname,hostname,version,minsnapid,maxsnapid,"slowquery","待計算","待運行",awrserverip(dbstrawr))
                        updatesql(dbstrmoni,sql2)

#初始化全表掃描作業
def initjobtbfull(realday,sysday,dbstrawr):
    # 更新歷史作業狀態
    sql1 = "UPDATE TEST.mysql_moni_log  SET STATUS='關閉' WHERE STATUS='待運行' AND JOBNAME='tableaccessfull' "
    updatesql(dbstrmoni, sql1)
    sql0="SELECT ID,IP_ADDR ,PORT,DB_NAME,HOSTNAME,VERSION,DESC1,REMOTEDAY,USER1,PASSWD FROM TEST.MYAWR_HOST WHERE AWRDB='%s'"%awrserverip(dbstrawr)
    rets=selectsql(dbstrmoni,sql0)
    for ret in rets:
        id=ret[0]
        ip=ret[1]
        port=ret[2]
        dbname=ret[3]
        hostname=ret[4]
        version=ret[5]
        desc=ret[6]
        remoteday=ret[7]
        user = ret[8]
        passwd=ret[9]
        if desc=="連接成功":
            if remoteday=="realday":
                checkday=realday
            else:
                checkday=sysday
            sql1="SELECT MIN(SNAP_ID),MAX(SNAP_ID)  FROM %s.myawr_snapshot where SNAP_TIME BETWEEN STR_TO_DATE('%s','%%Y%%m%%d') and STR_TO_DATE('%s','%%Y%%m%%d')"%(dbname,checkday,time.strftime("%Y%m%d"))
            rets1=selectsql(dbstrawr,sql1)
            if len(rets1)>0:
                if rets1[0][0] != None and rets1[0][1] != None:
                    minsnapid=int(rets1[0][0])
                    maxsnapid=int(rets1[0][1])
                    if maxsnapid>minsnapid:
                        #初始化作業控制表
                        sql2="INSERT INTO TEST.mysql_moni_log VALUES(NULL,%s,'%s','%s','%s','%s','%s',%s,CURRENT_TIMESTAMP,'%s',CURRENT_TIMESTAMP,'%s','%s','%s',CURRENT_TIMESTAMP,'%s')"%(id,ip,port,dbname,hostname,version,minsnapid,maxsnapid,"tableaccessfull","待計算","待運行",awrserverip(dbstrawr))
                        updatesql(dbstrmoni,sql2)

#計算全表掃描
def jobcounttbfull(dbstrawr):
    sql0="SELECT A.JOBID, A.ID,A.IP_ADDR,A.DB_NAME,A.snap_id_begin,A.snap_id_end,A.hostname,B.APP,B.ENV  FROM mysql_moni_log A,MYAWR_HOST B WHERE A.IP_ADDR=B.IP_ADDR AND A.DB_NAME=B.DB_NAME AND A.STATUS='待運行' AND A.JOBNAME='tableaccessfull' AND AWRDB='%s'"%awrserverip(dbstrawr)
    rets0=selectsql(dbstrmoni,sql0)
    for r0 in rets0:
        jobid=r0[0]
        id=r0[1]
        ip=r0[2]
        dbname=r0[3]
        minid=r0[4]
        maxid=r0[5]
        hostname=r0[6]
        app = r0[7]
        env = r0[8]
        print(id, ip, dbname, "%s任務開始執行" % jobid)
        if maxid-minid>0:
            sql2="SELECT A.SNAP_ID,A.SNAP_TIME,B.QUERY,B.DB,B.EXEC_COUNT,B.TOTAL_LATENCY,B.no_index_used_count,B.no_good_index_used_count,B.no_index_used_pct,B.rows_sent,B.rows_examined,B.rows_sent_avg,B.rows_examined_avg,B.first_seen,B.last_seen,B.DIGEST FROM %s.`myawr_db_statements_with_full_table_scans` B,%s.`myawr_snapshot` A WHERE A.SNAP_ID=B.SNAP_ID and B.DB IS NOT NULL and a.snap_id>=%s AND a.snap_id<=%s"%(dbname,dbname,minid,maxid)
            ret2=selectsql(dbstrawr,sql2)
            fullcount = len(ret2)
            if fullcount>0:
                if ret2[0][0] != None and ret2[0][1] != None:
                    print("全表掃描數量:",fullcount)
                    settbfulllog(None, ip, dbname,ret2)
                        #settbfulllog(None,ip,dbname,SNAP_ID,SNAP_TIME,QUERY,DB,EXEC_COUNT,TOTAL_LATENCY,no_index_used_count,no_good_index_used_count,no_index_used_pct,rows_sent,rows_examined,rows_sent_avg,rows_examined_avg,first_seen,last_seen,DIGEST)
                        #fields = ['SNAP_ID','SNAP_TIME','QUERY','DB','EXEC_COUNT','TOTAL_LATENCY','no_index_used_count','no_good_index_used_count','no_index_used_pct','rows_sent','rows_examined','rows_sent_avg','rows_examined_avg','first_seen','last_seen','DIGEST']
                        #do_save_to_mysql('tbfull_log', ','.join(fields), tmp)
            status="關閉"
            desc=fullcount
        else:
            status="關閉"
            desc="snapid無變化"
        sql5 = "UPDATE test.mysql_moni_log T SET T.DESC='%s',T.STATUS='%s',T.SNAP_TIME_END=CURRENT_TIMESTAMP WHERE T.JOBID=%s" % (desc, status, jobid)
        updatesql(dbstrmoni,sql5)
        print(id,ip,dbname,"%s任務執行完成"%jobid,status,desc)

#計算slowcout數量
def jobcountslow(realday,sysday,dbstrawr):
    sql0="SELECT JOBID, ID,IP_ADDR,DB_NAME,snap_id_begin,snap_id_end,hostname FROM mysql_moni_log WHERE STATUS='待運行' AND JOBNAME='slowquery' AND AWRDB='%s'"%awrserverip(dbstrawr)
    rets0=selectsql(dbstrmoni,sql0)
    for r0 in rets0:
        jobid=r0[0]
        id=r0[1]
        ip=r0[2]
        dbname=r0[3]
        minid=r0[4]
        maxid=r0[5]
        hostname=r0[6]
        print(id, ip, dbname, "%s任務開始執行" % jobid)
        if maxid-minid>0:
            sql2="SELECT MAX(variable_value),MIN(variable_value) from %s.myawr_db_metrics where variable_name ='slow_queries' AND snap_id>=%s AND snap_id<=%s"%(dbname,minid,maxid)
            ret2=selectsql(dbstrawr,sql2)
            if ret2[0][0] != None and ret2[0][1] != None:
                maxslow = int(ret2[0][0])
                minslow = int(ret2[0][1])
                print("SLOWLOG最大值",maxslow, "SLOWLOG最小值",minslow)
                if maxslow-minslow>0:
                    print("從自然日期%s(系統日期%s)開始,共出現了%s次饅查詢"%(realday,sysday,maxslow-minslow))
                    sql3 = "SELECT variable_value from %s.myawr_db_variables where variable_name ='SLOW_QUERY_LOG_FILE' AND snap_id=%s" % (dbname, maxid)
                    try:
                        logdir = selectsql(dbstrawr, sql3)[0][0]
                    except Exception as e:
                        print(e, dbstrawr, sql3)
                    sql4 = "SELECT variable_value from %s.myawr_db_variables where variable_name ='BASEDIR' AND snap_id=%s" % (dbname, maxid)
                    try:
                        basedir = selectsql(dbstrawr, sql4)[0][0]
                    except Exception as e:
                        print(e, dbstrawr, sql4)
                    day=time.strftime("%Y%m%d")
                    pattern = '[a-z]{3,4}-[0-9]{4}-[0-9]'  # Converts 'djm' to 'd.*j.*m'
                    regex = re.compile(pattern)  # Compiles a regex.
                    sql5 = "SELECT user1,passwd from myawr_host where ip_addr ='%s' AND db_name= '%s'" % (ip,dbname)
                    ret5 = selectsql(dbstrmoni, sql5)
                    user=ret5[0][0]
                    passwd=ret5[0][1]
                    if regex.match(hostname):
                        # 容器化部署mysql 用戶/密碼 root/icbc
                        resp = fabjob_docker(ip, logdir, basedir, day, realday, sysday,user,passwd)
                    else:
                        # 傳統部署mysql 用戶/密碼 sysop/Qwert789)
                        resp = fabjob(ip, logdir, basedir, day, realday, sysday,user,passwd)

                    if resp=="succ":
                        status="執行成功"
                        desc=maxslow-minslow
                    elif resp=="error":
                        status="fabric執行失敗"
                        desc=maxslow-minslow
                else:
                    status = "執行成功"
                    desc = "0"
            else:
                status="執行失敗"
                desc="未獲取到值"
        else:
            status="關閉"
            desc="snapid無變化"
        sql5 = "UPDATE test.mysql_moni_log T SET T.DESC='%s',T.STATUS='%s',T.SNAP_TIME_END=CURRENT_TIMESTAMP WHERE T.JOBID=%s" % (desc, status, jobid)
        updatesql(dbstrmoni,sql5)
        print(id,ip,dbname,"%s任務執行完成"%jobid,status,desc)

@timeout_decorator.timeout(60)
def getremoteday(user,passwd,ip):
    strfab = '''fab -f fab_MYSQL_slowlog.py -u %s -p "%s" -H %s  getday:ip="%s"''' % (user,passwd,ip,ip)
    print(strfab)
    try:
        p=subprocess.Popen(strfab, shell=True)
        p.wait()
        f = open('./logs/mysqldate_%s.log'%ip, 'rt', encoding='GBK')
        day=f.readline().replace("\r","").replace("\n","")
        print("%s remote day:%s"%(ip,day))
        return day
    except timeout_decorator.timeout_decorator.TimeoutError as e1:
        print("語句超時")
        p.kill()
        return("error")
    except Exception as e2:
        print(e2)
        p.kill()
        return("error")

@timeout_decorator.timeout(60)
def getremoteday_docker(user,passwd,ip):
    strfab = '''fab -f fab_MYSQL_slowlog_docker.py  -u %s -p "%s" -H %s  getday:ip="%s"''' % (user,passwd,ip,ip)
    print(strfab)
    try:
        p=subprocess.Popen(strfab, shell=True)
        p.wait()
        f = open('./logs/mysqldate_%s.log'%ip, 'rt', encoding='GBK')
        day=f.readline().replace("\r","").replace("\n","")
        print("%s remote day:%s"%(ip,day))
        return day
    except timeout_decorator.timeout_decorator.TimeoutError as e1:
        print("語句超時")
        p.kill()
        return("timeout")
    except Exception as e2:
        print(e2)
        p.kill()
        return("error")

@timeout_decorator.timeout(120)
def fabjob(ip, logdir,basedir,logday,realday,sysday,user,passwd):
    strfab = '''fab -f fab_MYSQL_slowlog.py -u %s -p "%s" -H %s   task:ip="%s",logdir="%s",basedir="%s",day="%s",realday="%s",sysday="%s"''' % (user,passwd,ip, ip, logdir.replace("slow_queries.log",""), basedir,logday,realday,sysday)
    print(strfab)
    try:
        p=subprocess.Popen(strfab, shell=True)
        p.wait()
        return "succ"
    except timeout_decorator.timeout_decorator.TimeoutError as e:
        print("語句超時")
        p.kill()
        return("error")

@timeout_decorator.timeout(120)
def fabjob_docker(ip, logdir, basedir, logday, realday, sysday,user,passwd):
    strfab = '''fab -f fab_MYSQL_slowlog_docker.py -u %s -p "%s" -H %s  task:ip="%s",logdir="%s",basedir="%s",day="%s",realday="%s",sysday="%s"''' % (
    user,passwd,ip, ip, logdir.replace("slow_queries.log", ""), basedir, logday, realday, sysday)
    print(strfab)
    try:
        p = subprocess.Popen(strfab, shell=True)
        p.wait()
        return "succ"
    except timeout_decorator.timeout_decorator.TimeoutError as e:
        print("語句超時")
        p.kill()
        return ("error")

#獲取應用信息
def gethostinfo_new(hostname,ip):
    pattern = '[a-z]{3,4}-[0-9]{4}-[0-9]'  # Converts 'djm' to 'd.*j.*m'
    regex = re.compile(pattern)  # Compiles a regex.
    if regex.match(hostname):
        app="F-"+hostname.split("-")[0].upper()
        env="DOCKER環境"
        return app, env
    else:
        constr0='test/[email protected]/test'
        str0="SELECT APP,ENV||'-'||FUNC||'-'||NODE DESC1  FROM V_SERVER_ALL_EXT WHERE IP='%s'"%ip
        res0 = select(constr0,str0)
        #print("res0",res0)
        app=""
        env=""
        if len(res0)>0:
            if res0[0][0]!=None:
                app=res0[0][0]
                env=res0[0][1]
        return app , env



def read_slow_log_to_list(file_name):
    # 組合每一分列表[],[]...
    sqltext = []
    # 每組分列表
    sql = []
    # 拼接多個SQL語句
    output = ''
    # 設置分組列表標識
    isflag = 1
    #print("file_name",file_name)
    with open(file_name,encoding="utf-8") as f:
        try:
            for line in f:
                line = line.strip()
                if line.startswith('# Time'):
                    if len(output)>0:
                        sql.append(output)
                    if len(sql)>0:
                        sqltext.append(sql)
                    sql = []
                    output=''
                    sql.append(line)
                elif line.startswith('SET'):
                    sql.append(line)
                elif line.startswith('# User@Host'):
                    sql.append(line)
                elif line.startswith('# Query_time'):
                    sql.append(line)
                elif line.startswith('use'):
                    continue
                elif line.startswith('# administrator'):
                    continue
                else:
                    if line.endswith(';'):
                        if len(output) == 0:
                            sql.append(line)
                            isflag = 0
                        else:
                            line = output + ' ' + line
                            sql.append(line)
                            output = ''
                            isflag = 0
                    else:
                        output += str(' ') + line
                if isflag == 0:
                    sqltext.append(sql)
                    isflag = 1
                    sql = []
        except Exception as e:
            print(e)
            pass
    #print("sqltxt")
    return sqltext






def handler_slowlog(file_name):
    slow_info = []
    ip = file_name.replace(".log", "").split("_")[1]
    res = read_slow_log_to_list(file_name)
    for res in res:
        if len(res)==5:
            try:
                # print res
                # time = res[0]
                # User@Host 信息
                userhost = res[1].strip()
                # 連接數據庫用戶
                db_user = userhost.replace('# User@Host:', '').split('[')[0].strip()
                # 應用程序連接DB所在的ip
                app_ip = userhost.replace('# User@Host:', '').split()[2].replace('[', '').replace(']', '').strip()
                app=''
                env=''
                # 開啓線程id
                thread_id = userhost.replace('# User@Host:', '').split(':')[1].strip()
                # print db_user, app_ip, thread_id
                querytime = res[2].strip()
                # 執行持續時長
                exec_duration = querytime.replace('# ', '').split()[1]
                # 執行結果集返回行數
                rows_sent = querytime.replace('# ', '').split()[5]
                # 完成查詢需要評估行數量
                rows_examined = querytime.replace('# ', '').split()[7]
                # print exec_duration, rows_sent, rows_examined
                # 開始時間
                #print("res3",res[3])
                start_time = res[3].replace(';', '').split('=')[1]
                # 慢SQL語句
                slowsql = res[4]
                # print start_time, db_user, app_ip, thread_id, exec_duration, rows_sent, rows_examined, slowsql
                tmp = (ip,start_time, db_user, app_ip,app,env,thread_id,
                       exec_duration, rows_sent, rows_examined, slowsql)
                slow_info.append(tmp)
                # break
            except Exception as e:
                print("res",res,traceback.print_exc())
                pass
        else:
            #print("res元素數量錯誤",len(res),res)
            pass
    return slow_info


def log2mysql():
    engine = create_engine("mysql://test:[email protected]:3307/test?charset=utf8")
    DBsession = sessionmaker(bind=engine)
    session = DBsession()
    for dir,root,loglist in os.walk("./logs"):
        for log in loglist:
            type=log.replace(".log","").split("_")[0]
            if type=="monislowlog":
                file_name = os.path.join("./logs/", log)
                # 返回慢查詢日誌文件中處理後的各列數據
                res = handler_slowlog(file_name)
                # 表中需要插入數據的列名
                try:
                    for val in res:
                        hash = hashlib.sha1()
                        hash.update(val[10].encode('utf-8'))
                        sqlid=hash.hexdigest()
                        ret=session.query(SLOWLOG).filter_by(sqlid=sqlid).first()
                        if ret is None:
                            slowlog= SLOWLOG(
                                mysql_ip = val[0],
                                start_time = val[1],
                                db_user = val[2],
                                app_ip = val[3],
                                sqlid = sqlid,
                                thread_id = val[6],
                                exectime = val[7],
                                rows_sent = val[8],
                                rows_examined = val[9],
                                slowsql = val[10],
                                update_time=datetime.datetime.now()
                            )
                            session.add(slowlog)
                        else:
                            if  val[7] >ret.exectime:
                                ret.start_time=val[1]
                                ret.app_id=val[3]
                                ret.thread_id=val[6]
                                ret.exectime = val[7]
                                ret.rows_sent = val[8]
                                ret.rows_examined = val[9]
                                ret.update_time = datetime.datetime.now()
                    session.commit()
                except Exception as e:
                    print ("入庫錯誤:%s" % (e))
                        #print(sql)

def read_slowresult_log_to_list(file_name):
    # 組合每一分列表[],[]...
    sqltext = []
    # 每組分列表
    sql = []
    # 拼接多個SQL語句
    output = ''
    # 設置分組列表標識
    isflag = 1
    #print("file_name",file_name)
    with open(file_name,encoding="utf-8") as f:
        try:
            for line in f:
                line = line.strip()
                if line.startswith('Count: '):
                    if len(output)>0:
                        sql.append(output)
                    if len(sql)>0:
                        sqltext.append(sql)
                    sql = []
                    output=''
                    sql.append(line)
                else:
                    output += str(' ') + line
                if isflag == 0:
                    sqltext.append(sql)
                    isflag = 1
                    sql = []
        except Exception as e:
            print(e)
            pass
    #print(sqltext)
    return sqltext


def handler_slowresult(file_name):
    slow_info = []
    ip = file_name.replace(".log", "").split("_")[1]
    res = read_slowresult_log_to_list(file_name)
    for res in res:
        if len(res)==2:
            try:
                # 數據格式 ['Count: 1  Time=3266.61s (3266s)  Lock=0.00s (0s)  Rows=75.0 (75), chashu[chashu]@[193.168.1.1]' , 'SQL']
                # SQL的統計信息
                sqlstat = res[0].strip()
                # sql語句
                sqltext = res[1].strip()
                # 連接用戶信息
                sqlperf, client = sqlstat.split(", ")
                sqlcount,sqltimes,sqllocks,sqlrows=sqlperf.split("  ")
                #print("SQL的統計信息",sqlcount,sqltimes,sqllocks,sqlrows,client)
                #print("SQL的文本", sqltext)
                #執行次數
                count=sqlcount.replace("Count: ","").strip()
                #最大執行時間
                exec_duration=sqltimes.replace("Time=","").split("s")[0].strip()
                #最大鎖時間
                locktime=sqllocks.replace("Lock=","").split("s")[0].strip()
                #最大返回記錄數
                rows_sent=sqlrows.replace("Rows=","").split(" (")[0].strip()
                tmp = (ip,client,exec_duration,locktime,rows_sent,count,sqltext,sqlperf)
                slow_info.append(tmp)
                # break
            except Exception as e:
                print("res",res,traceback.print_exc())
                pass
        else:
            #print("res元素數量錯誤",len(res),res)
            pass
    return slow_info

def slowresult2mysql():
    engine = create_engine("mysql://test:[email protected]:3307/test?charset=utf8")
    DBsession = sessionmaker(bind=engine)
    session = DBsession()
    for dir,root,loglist in os.walk("./logs"):
        for log in loglist:
            type=log.replace(".log","").split("_")[0]
            if type=="monislowresult":
                file_name = os.path.join("./logs/", log)
                # 返回慢查詢日誌文件中處理後的各列數據
                res = handler_slowresult(file_name)
                # 表中需要插入數據的列名
                try:
                    for val in res:
                        hash = hashlib.sha1()
                        hash.update(val[6].encode('utf-8'))
                        sqlid=hash.hexdigest()
                        ret=session.query(SLOWRESULT).filter_by(sqlid=sqlid).first()
                        if ret is None:
                            slowresult= SLOWRESULT(
                                mysql_ip = val[0],
                                client = val[1],
                                sqlid = sqlid,
                                exectime = val[2],
                                locktime=val[3],
                                rows_sent = val[4],
                                exec_count = val[5],
                                slowsql = val[6],
                                fullstatic=val[7],
                                update_time=datetime.datetime.now()
                            )
                            session.add(slowresult)
                    session.commit()
                except Exception as e:
                    print ("入庫錯誤:%s" % (e))
                        #print(sql)


if __name__ == '__main__':
    #flag=1 : step1 step2
    #flag=2 : step3 step4
    #flag=3 : step 1-4
    flag= sys.argv[1]
    realday = sys.argv[2]
    sysday = sys.argv[3]
    #realday="20190412"
    #sysday="20190331"
    # percona server
    dbstrawrs=["mysql://root:root@awr1:3306/myawr?charset=utf8","mysql://root:root@awr2:3306/myawr?charset=utf8"]
    for dbstrawr in dbstrawrs:
        if len(sys.argv)!=4:
            print("調用方法: python MYSQL_slowlog_new.py <標誌:1-只初始化作業 2-只執行作業 3-初始化並執行作業> <版本開始自然日期> <版本開始系統日期> ")
        else:
            print("參數列表:標誌-%s,版本開始自然日期-%s,版本開始系統日期-%s"%(flag,realday,sysday))
            if flag=="1" or flag=="3":
                # 清理過期日誌
                ls = os.listdir("./logs")
                for i in ls:
                    os.remove(os.path.join("./logs", i))
                #step 2 初始化MYAWR_HOST,並獲取數據庫當前系統時間
                initjob(dbstrawr)
            if flag=="2" or flag=="3":
                # 清理過期日誌
                ls = os.listdir("./logs")
                for i in ls:
                    os.remove(os.path.join("./logs", i))
                # step 3 獲取自然時間(系統時間)至今的滿日誌記錄數
                getsnapid(realday,sysday,dbstrawr)
                #step 4 fabric整合日誌備份,生成結果文件
                jobcountslow(realday,sysday,dbstrawr)
                #step 5 結果文件入庫
                log2mysql()
                initjobtbfull(realday,sysday,dbstrawr)
                jobcounttbfull(dbstrawr)
            if flag=="4" or flag=="3":
                #計算全表掃描
                initjobtbfull(realday,sysday,dbstrawr)
                jobcounttbfull(dbstrawr)
            if flag=="5":
                print("測試開始")
                slowresult2mysql()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章