第一次使用linux并编写了一个dump mysql的程序,记录下简单的步骤

MYSQL/MariaDB 操作系统

机器安装linux redhat5.0 64位。客户管理员给了我账户密码,我用SecureCRT登录。

连接后:

ls 列表文件,当然支持通配符

cd path 进入某个目录 *SecureCRT中可以像window一样复制路径,比较方便。

cd .. 返回上层

cd ~ 切换到用户目录

cd / 切换到根目录

rm -f aaa.zip 删除文件

rm -rf aaa 删除目录 r应该是循环的意思

reboot 重启

date -s 12/31/2015 修改日期

zip -r abc.zip abc 压缩

unzip abc.zip 解压缩

rz SecureCRT从本地传文件上去。一般用zip就行。注意看是否100%,0 error。遇到已经存在的文件要先rm掉才能上传。

sz aa.zip 从linux往本地下载一个文件。下载的位置在win7的“下载”文件夹。

linux可执行文件可以无后缀,或者 .o, 执行时就算在当前目录也要输入 ./abc 才能执行

如果提示Permission denied,则执行chmod 777 abc 修改权限即可

安装mysql可以直接下载rpm安装包

MySQL-server-community-5.0.96-1.rhel5.x86_64.rpm

MySQL-client-community-5.0.96-1.rhel5.x86_64.rpm

安装rpm包: rpm -ivh abc.rpm

注意:可能为了商业开发的稳定性,并未使用更高版本

为了开发必须有lib库,这个安装client是没有的。我采用下载源码来安装

下载mysql5.0.96的源码:mysql-5.0.96.tar.gz,解压缩到某个path,然后su切换到root账号,cd path
然后执行:
./configure –prefix=/usr/local/mysql/ –without-server –with-named-curses-libs=/usr/lib/libncursesw.so.5
注意看到:Thank you for choosing MySQL! 的提示才算成功否则百度错误提示解决

执行:make
执行:make install
安装后可以拷贝一份mysql的HEADER头文件和lib库文件,到其他电脑上也方便

为了测试目的安装的mysql没有自动启动,执行: mysqld –user=root 启动它

mysql进入命令行。

use database 切换数据库

show tables; 看表名

exit 退出命令行

编写c程序用vim工具

vim abc.c

进去默认是只读的,按i 进入编辑状态,需要保存时,按esc,进入命令模式按 :wq 保存退出 :w只保存不退出

程序编写好后,我将其单独放入一个文件夹内,并把mysql的头文件和mysql的库文件放入HEADER和LIB文件夹,建立sh文件便于快速编译和运行

compile.sh //编译时用

!/bin/sh

rm -f abc
cc -o abc abc.c -L ./LIB -lmysqlclient

说明: 第一行删除可执行文件,第二行编译并用-L参数指定库路径。为了简单我已经把源码编译得到的mysq库放入了单独的文件夹,便于带到其他地方。

run.sh //运行时用

!/bin/sh

export LD_LIBRARY_PATH=./LIB:$LD_LIBRARY_PATH
cd usr/abc
find ./LOG -size +50M -exec rm {} \;
./abc | tee -a ./LOG/2.log

说明:第一行 指定lib,第二行切换到当前目录,这样定时任务时,不会说相对路径找不到,第三行删除日志,当体积大于50M时,第四行执行并输出过程为日志文件

如果用虚拟机安装的linux是启动到桌面的,则打开网络可以看到主机,输入管理员账户密码,就能打开磁盘分区拷贝文件。

默认是非root登录的,比如chen,只需要打开终端,输入: su 提示输入密码,就能切换到root,其他操作同上述。

最后说一下程序的目标:dump mysql的表同步到镜像的数据库中去。

查询目标mysql上的information_schema中的tables表,得到某个表最后的同步时间,如果早于今天凌晨或者不存在这个表则调用命令行mysqldump导出。最后用mysql命令连接远程的目标主机执行导入。之前其他人开发的拷贝程序不是闭环控制,在网络闪断的情况下几乎天天失败,拷贝和导入脱节,两部分都未对最终结果负责。

为何要使用c来做这个简单程序是因为客户不需要这个机器安装更多的东西,比如php。用c编写比较简单一点。不会额外增加什么。

当然作为初识linux和c在linux,以及调用mysql编程,作为一个机会练习吧。

具体源码

/* auth : chen
dt: 2015-06-20
func: 实现从v3000拷贝表格及其数据到缓冲服务器
*/

include

include

include

include

include

include

include <./HEADER/mysql.h>

//延迟时间,这样定时任务可以设置到统一的时间,但是拷贝过程会分布到0-30分钟之间。
//为了计算式中不被零除,默认1.

define DELAY_MINUTE 1

//文件最小体积

define MIN_FILESIZE 595

struct _strc_tbllist{
int type; //1.固定表 2.月表 3.日表 11:固定表e_customer如果不存在头天晚上9点之后的表,则更新
//22.月表,但是每天要更新
char tblnamePre[80];
char tblnameNew[80];
};

int printtime(){
time_t now; //实例化time_t结构
struct tm *timenow; //实例化tm结构指针
time(&now);
//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);
//localtime函数把从time取得的时间now换算成你电脑中的时间(就是你设置的地区)
printf(“%04d-%02d-%02d %02d:%02d:%02d\n”,1900+timenow->tm_year,timenow->tm_mon+1,timenow->tm_mday,
timenow->tm_hour,timenow->tm_min,timenow->tm_sec);
}

int gettime_yyyymm(char* dt,int AddDay){
time_t now; //实例化time_t结构
struct tm *timenow; //实例化tm结构指针
time(&now);

now += (86400*AddDay);

//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);

sprintf(dt,”%04d%02d”,1900+timenow->tm_year,timenow->tm_mon+1);
}

int gettime_yyyymmdd(char* dt,int AddDay){
time_t now; //实例化time_t结构
struct tm timenow; //实例化tm结构指针 time(&now); now += (86400AddDay);

//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);

sprintf(dt,”%04d%02d%02d”,1900+timenow->tm_year,timenow->tm_mon+1,timenow->tm_mday);
}

int gettime_yyyy_mm_dd(char* dt,int AddDay){
time_t now; //实例化time_t结构
struct tm timenow; //实例化tm结构指针 time(&now); now += (86400AddDay);

//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);

sprintf(dt,”%04d-%02d-%02d”,1900+timenow->tm_year,timenow->tm_mon+1,timenow->tm_mday);
}

int gettime_daynumber(){
time_t now; //实例化time_t结构
struct tm *timenow; //实例化tm结构指针
time(&now);
//time函数读取现在的时间(国际标准时间非北京时间),然后传值给now
timenow = localtime(&now);

return timenow->tm_mday;
}

int ReadConfig(char* CfgFile,char* PlatFormID,char* IP,char* Port,char* User,char* Pwd){
//conf中的配置信息
FILE *fp;
char StrLine[1024];

if((fp = fopen(CfgFile,”r”))== NULL) //判断文件是否存在及可读
{
printf(“fopen [config file] error\n”);
return -1;
}
//read it
int r = 0;
while (!feof(fp))
{
r ++;
//特别注意fgets包含了末尾的回车符\n,之前涉及到登录时一直存在问题。注意!!!
fgets(StrLine,1024,fp); //读取一行
StrLine[strlen(StrLine) -1] = ‘\0’; //抹掉回车符号
switch(r){
case 12: //platform
strcpy(PlatFormID,StrLine);
printf(“PlatFormID: %s\n”, PlatFormID); //输出
break;
case 14: //ip
strcpy(IP,StrLine);
printf(“IP: %s\n”,IP); //输出
break;
case 16: //port
strcpy(Port,StrLine);
printf(“Port: %s\n”,Port); //输出
break;
case 18: //User
strcpy(User,StrLine);
printf(“User: %s\n”, User); //输出
break;
case 20:
strcpy(Pwd,StrLine);
printf(“Pwd: %s\n”, Pwd); //输出
break;
}
}
fclose(fp);
return 1;
}

int ReadTableList(char* TableListFile,struct _strc_tbllist _tablelist[100]){
//conf中的配置信息
FILE *fp;
char StrLine[1024];

if((fp = fopen(TableListFile,”r”))== NULL) //判断文件是否存在及可读
{
printf(“fopen [config file] error”);
return -1;
}
//read it
int r = -1;
int s;
char tbltype[6];
while (!feof(fp))
{
//特别注意fgets包含了末尾的回车符\n,之前涉及到登录时一直存在问题。注意!!!
fgets(StrLine,1024,fp); //读取一行
StrLine[strlen(StrLine) -1] = ‘\0’; //抹掉回车符号

if(r > -1){
//找到开始{}一对括号作为开始和结束
if(strcmp(StrLine,”}”)==0){
break;
}
//输出
printf(“Table List: %s\n”,StrLine);
for(s = 0;s < strlen(StrLine);s++){
if(StrLine[s] == ‘,’){
memset(tbltype,0,6);
memcpy(tbltype,StrLine,s);

 //赋值
 _tablelist[r].type = atoi(tbltype);
 strcpy(_tablelist[r].tblnamePre,&StrLine[s+1]);
 break;
}

}
r ++;
}else{
//找到开始{}一对括号作为开始和结束
if(strcmp(StrLine,”{“)==0){
r = 0; //第一个
continue;
}
}
}
fclose(fp);

printf(“Table Qty: %d\n\n”,r);
return r;
}

unsigned long get_filesize(const char *path){
unsigned long filesize = -1;
struct stat statbuff;
if(stat(path, &statbuff) < 0){
return filesize;
}else{
filesize = statbuff.st_size;
}
return filesize;
}

int main(int argc,char** argv){
printf(“\n\n\n-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n”);
printtime();
printf(“系统拷贝数据开始…\nbegin export and import…\n\n”);

//延迟执行,这样每个服务器可以分布开
srand((unsigned) time(NULL)); //用时间做种,每次产生随机数不一样
int delay_second = 0;
if(DELAY_MINUTE <= 1){
//not delay
}else{
delay_second = 60*(rand()% DELAY_MINUTE + 1);
printf(“将各台服务器执行时间分布错开: 延迟触发 %d 秒++++++++++++++++\n”,delay_second);
sleep(delay_second);
}

//因为这个程序会从凌晨1点执行到早晨7点
//7:30分执行更新动作
//所以首先按table create time来判断某个表是否成功创建,如未创建,加入到mysqldump的列表中去

int TBL_QTY;
struct _strc_tbllist tablelist[100];

#define TBL_LISTS “./tables.tbl”
TBL_QTY = ReadTableList(TBL_LISTS,tablelist);

//读取配置文件信息
#define CONFIG_FILE “./host.cfg”
char PlatFormID[127],IP[127],User[127],Pwd[127];
char Port[16];
char DB_SCHEMA[64] = “information_schema”;
char DB_PLATFORM[64] = “v_”;

if(ReadConfig(CONFIG_FILE,PlatFormID,IP,Port,User,Pwd)<0){
return -1;
}

//mysql init
MYSQL my_conn_schema;
MYSQL_RES *result;
int res;
mysql_init(&my_conn_schema);

//连接到information_schema数据库
if(!mysql_real_connect(&my_conn_schema,IP,User,Pwd,DB_SCHEMA,60000,NULL,0)){
printf(“>>>>>>>>connect [information_schema db] 失败!\n原因: %s\n”,mysql_error(&my_conn_schema));
return -1;
}

//正常的提示
printf(“connect [information_schema db] 成功++++++++++++++++++++++++++++++++++++++++++++++++\n\n\n”);

//排查fixed表
char SQL[2048];
char MyDump[1024],MyImport[1024];
char ChangeDB[60];
char SourcePath[60] = “./SQL_EXPORT/”;
char yyyymm[7],yyyymmdd[9],yyyy_mm_dd[11];
int daysnumber;
char dt_tmp[10];
//db name
strcat(DB_PLATFORM,PlatFormID);

//daysnumber
daysnumber = gettime_daynumber();

int beginday,endday; //当天需要检查多个表时使用。避免某天不成功第二天可以补救
int t;
int d;
for(t = 0;t < TBL_QTY;t++){ //区分某天拷贝哪些表 switch(tablelist[t].type){ case 1: beginday = -1; //只用执行一次内循环 endday = 0; break; case 11: beginday = -1; //只用执行一次内循环 endday = 0; break; case 2: //1-4号检查,并且只检查上个月的表 //原则上,如果不是1号就不执行拷贝,实际条件下可能1号出问题,则1-4天都检查一遍 if(daysnumber >= 4){
printf(“此表只在1-4号检查执行,跳过++++++++++++++++\n\n”);
continue;
}
beginday = (-1)daysnumber; endday = (-1)daysnumber +1; //通过上限控制只执行一次
break;
case 22:
//5-31号只判断上月的
if(daysnumber > 4){
beginday = -1;
endday = 0;
}
//1-4号既要判断上月的,又要判断本月的
if(daysnumber <= 4){
beginday = (-1)*daysnumber; //上个月的
endday = 0; //昨天的
}
break;
case 3:
beginday = -36; //检查31天内的未成功拷贝的表,这个表按名称即可判断不需要判断导入时间
endday = 0;
break;
case 33:
beginday = -36; //检查31天内的未成功拷贝的表,这个表按名称即可判断不需要判断导入时间
endday = 0;
break;
}

for(d =beginday;d<endday;d++){
//不同类型的表需要动态添加后缀,比如20150601
strcpy(tablelist[t].tblnameNew,tablelist[t].tblnamePre);

switch(tablelist[t].type){
case 1:
//do nothing
break;
case 11:
//do nothing
break;
case 2:
//增加月份后缀
gettime_yyyymm(yyyymm,d);
strcat(tablelist[t].tblnameNew,yyyymm);
break;
case 22:
//1-4号既要判断上月的,又要判断本月的
if(daysnumber <= 4){ //上个月执行完就执行昨天的,中间几天不用判断,因为是月表 if(d > beginday){
d = endday -1;
}
}

 //增加月份后缀
 gettime_yyyymm(yyyymm,d);
 strcat(tablelist[t].tblnameNew,yyyymm);
 break;
case 3:
 gettime_yyyymmdd(yyyymmdd,d);
 strcat(tablelist[t].tblnameNew,yyyymmdd);
 break;
case 33:
 gettime_yyyymmdd(yyyymmdd,d);
 strcat(tablelist[t].tblnameNew,yyyymmdd);
 break;    

}

//SELECT create_time
//FROM TABLES
//WHERE TABLE_SCHEMA = ‘v_9999’ AND
//table_name = ‘e_customer’
strcpy(SQL,”SELECT create_time\n”
“FROM TABLES\n”
“WHERE TABLE_SCHEMA = \’vos_”);
strcat(SQL,PlatFormID);
strcat(SQL,”\’ AND\n”);
strcat(SQL,”table_name = \'”);
strcat(SQL,tablelist[t].tblnameNew);
strcat(SQL,”‘ AND\n”);

//不同类型的表需要动态添加后缀,比如20150601
switch(tablelist[t].type){
case 1:
strcat(SQL,”create_time > CURRENT_DATE()”);
break;
case 2:
strcat(SQL,”1 = 1″); //日表只需要判断是否存在,不需要判断日期
break;
case 3:
strcat(SQL,”1 = 1″); //日表只需要判断是否存在,不需要判断日期
break;
case 33:
strcat(SQL,”1 = 1″); //日表只需要判断是否存在,不需要判断日期
break;
case 11:
strcat(SQL,”create_time > \'”);
gettime_yyyy_mm_dd(yyyy_mm_dd, -1);
strcat(SQL,yyyy_mm_dd);
strcat(SQL,” 21:00:00.001\'”);
break;
case 22:
strcat(SQL,”create_time > date_add(CURRENT_DATE(),interval “);
memset(dt_tmp,0,sizeof(dt_tmp));
sprintf(dt_tmp,”%d”,d +1);
strcat(SQL,dt_tmp);
strcat(SQL,” day)”);
break;
}

printf(“执行SQL:———————–[表 %d: %s]—[第 %d 天]———————–\n”,
t,tablelist[t].tblnameNew,d);
printf(tablelist[t].tblnamePre);
printf(“\n”);

printf(SQL);
printf(“\n\n”);

res=mysql_query(&my_conn_schema,SQL);//查询
if(res != 0){
printf(“>>>>>>>>res=mysql_query(&my_conn_schema,SQL<判断表是否存在>) 时失败: res = %d\n”,res);
return -1;
}

result=mysql_store_result(&my_conn_schema);//保存查询到的数据到result
if((unsigned long)mysql_num_rows(result)>0){
printf(“已经同步了这个表 [跳过]++++++++++++++++\n\n”);
continue;
}

//需要导出的

printf(“尚未同步这个表,加入到mysqldump列表中去++++++++++++++++\n”);

//导出文件名
char ExportFile[255];
strcpy(ExportFile,SourcePath);
strcat(ExportFile,tablelist[t].tblnamePre);
strcat(ExportFile,”.sql”);

//注意本地mysql没有密码,所以mysqldump不需要加什么密码等参数
strcpy(MyDump,”mysqldump v9999 “);
strcat(MyDump,tablelist[t].tblnameNew);
strcat(MyDump,” –skip-comments”);

strcat(MyDump,”>”);
strcat(MyDump,ExportFile);

//执行
printf(“执行导出命令: %s\n”,MyDump);
//删除文件先
remove(ExportFile);
system(MyDump);

//如果命令行有错误,仍然生成空文件,所以判断文件是否写成功
if(get_filesize(ExportFile)<= MIN_FILESIZE){
printf(“导出出现异常,跳过该表的导入++++++++++++++++\n\n”);
continue;
}

//连接到平台数据库,导入
//格式: mysql -u root -p123456 test <d:\a.sql
strcpy(MyImport,”mysql -h”);
strcat(MyImport,IP);
strcat(MyImport,” -P”);
strcat(MyImport,Port);
strcat(MyImport,” -u”);
strcat(MyImport,User);
strcat(MyImport,” -p”);
strcat(MyImport,Pwd);
strcat(MyImport,” “);
strcat(MyImport,DB_PLATFORM);
strcat(MyImport,” < “);
strcat(MyImport,ExportFile);

printf(MyImport);
printf(“\n”);

//执行
system(MyImport);
printf(“\n”);
} //for d
}//for t

mysql_free_result(result);//释放结果资源
mysql_close(&my_conn_schema);//断开连接

//结束提示
printf(“已经结束全部的处理过程++++++++++++++++++++++++++++++++\n\n\n”);

//main结束
exit(0);
}