【C/C++】ghost ddl脚本简单实现

目的:本篇是自己用C++实现的ddl的简单脚本(改写自自己的shell,但是还有一部分没完成),用来锻炼自己写C++的能力


头文件exec_ddl.h

```

#include
#include
#include
#include
#include
size_t GetStrCurrTime(std::string &);
#ifndef header_cpp_fun_h
#define header_cpp_fun_h
class CDdlGhost {
private:
//数据库账号,端口,DDL用户,密码,
std::string host;
int port;
std::string user;
std::string password;
std::string database;
std::string tableName;
int threadRunning = 500;
int cthreadRunning = 500;
int maxLagMillis = 3000;
std::string cutOver = "default";
int chunkSize = 1000;
int lockSeconds = 60;
int retries = 3;
static CDdlGhost* instance;
CDdlGhost(std::string host,int port,std::string database,std::string tableName,std::string user,std::string password);

public:
static CDdlGhost* GetSingleInstance(std::string host,int port,std::string database,std::string tableName,std::string user,std::string password);
int GetGhostCmd(std::string sql,std::string &cmd);
int ExecDdlCmd(std::string &cmd);
};
#endif

```


exec_ddl.cpp

```

#include "exec_ddl.h"
//类中静态变量为什么不在类中初始化,是因为静态变量具有外部链接性,文件作用域
CDdlGhost* CDdlGhost::instance = nullptr;

//构造函数
CDdlGhost::CDdlGhost(std::string host,int port,std::string database,std::string tableName,std::string user,std::string password){
    //这里以后正则表达式做安全性过滤
    this->host = host;
    this->port = port;
    this->database = database;
    this->tableName = tableName;
    this->user = user;
    this->password = password;

}
//获取类的单实例函数,这里为什么返回的是指针而不是引用是因为我要用空指针才判断是否单实例
CDdlGhost* CDdlGhost::GetSingleInstance(std::string host,int port,std::string database,std::string tableName,std::string user,std::string password){
    if(nullptr == instance){
        instance = new CDdlGhost(host,port,database,tableName,user,password);
    }

    return instance;

}
//获得表结构更改的命令字符串
int CDdlGhost::GetGhostCmd(std::string sql,std::string &ghostCmd){
    //获取环境变量
    const char* pathEnv = std::getenv("PATH");
    std::string ghostLog;
    //这个变量用来获取当前时间戳
    std::string strTime;
    size_t ret = GetStrCurrTime(strTime);
    if(ret <= 0){
        std::cout<<"获取当前时间戳失败"<         return 0;
    }
    
    ghostLog=ghostLog+"/data1/upload/ghost_"+tableName+"_"+strTime+".log";
    ghostCmd="gh-ost --ok-to-drop-table --initially-drop-ghost-table --skip-foreign-key-checks --allow-on-master --switch-to-rbr --allow-master-master --exact-rowcount --verbose --initially-drop-old-table ";
    
    ghostCmd=ghostCmd + "--max-load=Threads_running="+std::to_string(threadRunning)+" --critical-load=Threads_running="+std::to_string(cthreadRunning)+" --chunk-size="+std::to_string(chunkSize)+" --cut-over="+cutOver+" --max-lag-millis="+std::to_string(maxLagMillis)+" --cut-over-lock-timeout-seconds="+std::to_string(lockSeconds)+" --default-retries="+std::to_string(retries)+" --host=\'"+host+"' --user='"+user+"' --password='"+password+"' --database='"+database+"'  --table='"+tableName+"' --alter='"+sql+"' --panic-flag-file=/tmp/"+tableName+".ghost.panic.flag --execute  > "+ghostLog+" 2>&1";    

    return 1;
    /*
    if(execl("/bin/sh","sh","-c",ghostCmd.c_str(),(char *) 0)<0){
        std::cout<<"执行ghostCmd失败,语句为:"<         return -2;

    }
    */

}
int CDdlGhost::ExecDdlCmd(std::string &ghostCmd){
    
    pid_t pidGhost;
    int GhostStatus;
    //这里我还要fork一个扫描ghost产生的日志的子进程,但是现在暂时没打开
    //pid_t pidScan;
    
    if((pidGhost = fork()) < 0){
        std::cout<<"pidGhost子进程fork失败"<         return -2;

    }

    /*
    if((pidScan = fork()) < 0){
        std::cout<<"pidScan子进程fork失败"<         return -2;
        

    }
    */
    //子进程要执行修改表结构命令了
    if(0 == pidGhost){
        //为什么不用system,因为不想子进程fork子进程
        if(execl("/bin/sh","sh","-c",ghostCmd.c_str(),(char *) 0)<0){
            std::cout<<"卧槽,执行命令失败了"<
        }
        _exit(127);

    }
    //另一个子进程,每一秒检查下日志文件,看看是否有锁,有锁就kill,暂时没启用
    /*
    if(0 == pidScan ){
        while(1){
            int ret = ScanGhostLogLock(std::string &ghostLog);
            if(ret > 0){
                KillSleepOrLongQuery();
            }
            sleep(1);
    }
    */

    /*这里后面会通过一个函数扫描子进程执行的命令的日志,执行完后通知父进程*/


    //等待子进程结束
    if(waitpid(pidGhost,&GhostStatus,0) < 0){
        std::cout<<"等待子进程出现异常"<         return -1;

    }
        
    /*这里以后会通过函数向后台扫描日志进程发送信号,通知其结束*/    

    if(WIFEXITED(GhostStatus)){
        return 0;

    }
    return -3;

}
//获取格式化后的日期字符串
size_t GetStrCurrTime(std::string &strTime){
    time_t now = time(NULL);
    struct tm timeinfo = *localtime(&now);
    char buf[30];
    size_t ret = strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", &timeinfo);
    std::string str(buf);
    strTime = str;
    return ret;


}

```



ddl_main.cpp

```

#include
#include "exec_ddl.h"
int main(){
    int i=0,ret;
    CDdlGhost *pDdlGhost=CDdlGhost::GetSingleInstance("10.17.4.23",3306,"test","test1","zaixinyuan","test123456");
    if (pDdlGhost == nullptr){
        std::cout<<"分配对象内存失败"<         return 1;
    }
    std::string ghostCmd;
    i = pDdlGhost->GetGhostCmd("add index cname(c)",ghostCmd);
    ret = pDdlGhost->ExecDdlCmd(ghostCmd);
    std::cout<<"返回结果是:"<     if(ret <0){
        std::cout<<"更改表结构失败"<     }

    delete pDdlGhost;
}

```

编译命令

```

g++ -std=gnu++11 -o ddl_ghost ddl_main.cpp exec_ddl.cpp

```


执行结果:

程序返回结果


数据库查看结果


请使用浏览器的分享功能分享到微信等