@TOC
1. 数据挖掘子系统:rc.local
如下数据挖掘dmining和数据交换exptables
差不多(exptables自己设计数据表有keyid字段,从其他地方拿数据未必有keyid字段需要指定,也就是数据是增量的,但不提供增量提取方式即没有keyid字段),将数据从数据库中取出导入文件(执行一个sql拿出数据)。vi /etc/rc.local(随操作系统自启动)
vi /htidc/gz.....sh上面如下,如下要写的其实就是< selectsql >字段里内容。比如取近一小时数据,每次取到都有重复但在入库时处理重复就行,获取到的数据生成xml再入自己库
//dminoracle.cpp
#include "_public.h"
#include "_ooci.h" // oracle数据库是_ooci.h,mysql数据库换成_mysql.h,PostgreSQL数据库换成_postgresql.h
// 主程序的参数
struct st_arg
{
char connstr[101];
char charset[51];
char tname[51];
char cols[1001];
char fieldname[1001];
char fieldlen[501];
int exptype;
char andstr[501];
char bname[51];
char ename[51];
char idfieldname[51];
char idfilename[301];
char exppath[301];
int timetvl;
} starg;
CLogFile logfile;
connection conn;
// 本程序的业务流程主函数
bool _dmintables();
void EXIT(int sig);
vector vfieldname; // 存放拆分fieldname后的容器
vector vfieldlen; // 存放拆分fieldlen后的容器
int maxfieldlen; // 存放fieldlen的最大值
void SplitFields(); // 拆分fieldname和fieldlen
// 显示程序的帮助
void _help(char *argv[]);
long maxkeyid; // 已挖掘数据的最大的id
bool LoadMaxKeyid(); // 从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
bool UptMaxKeyid(); // 更新已挖掘数据的最大的id到文件中
// 把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer);
int main(int argc,char *argv[])
{
if (argc!=3) { _help(argv); return -1; }
// 关闭全部的信号和输入输出
CloseIOAndSignal();
// 处理程序退出的信号
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[1],"a+")==false)
{
printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
}
// 把xml解析到参数starg结构中
if (_xmltoarg(argv[2])==false) return -1;
while (true)
{
// 连接数据库
if (conn.connecttodb(starg.connstr,starg.charset) != 0)
{
logfile.Write("connect database %s failed.\n",starg.connstr); sleep(starg.timetvl); continue;
}
// logfile.Write("export table %s.\n",starg.tname);
// 挖掘数据的主函数
if (_dmintables() == false) logfile.Write("export tables failed.\n");
conn.disconnect(); // 断开与数据库的连接
sleep(starg.timetvl);
}
return 0;
}
void EXIT(int sig)
{
logfile.Write("程序退出,sig=%d\n\n",sig);
exit(0);
}
// 显示程序的帮助
void _help(char *argv[])
{
printf("\n");
printf("Using:/htidc/public/bin/dminoracle logfilename xmlbuffer\n\n");
printf("增量挖掘示例:\n");
printf("Sample:/htidc/public/bin/dminoracle /log/shqx/dminoracle_surfdata_from_qx.log \"shqx/pwdidc@snorcl11g_198 Simplified Chinese_China.ZHS16GBK T_SURFDATA obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis obtid,ddatetime,t,p,u,wd,wf,r,vis 5,14,8,8,8,8,8,8,8 1 and obtid in ('59293','50745') SURFDATA_ _from_qx /data/dmin/SURFDATA_from_qx.txt keyid /data/shqx/sdata/fromqx 30 \"\n\n");
printf("全量挖掘示例:\n");
printf("Sample:/htidc/public/bin/dminoracle /log/shqx/dminoracle_obtcode_from_qx.log \"shqx/pwdidc@snorcl11g_198 Simplified Chinese_China.ZHS16GBK T_OBTCODE obtid,obtname,provname,lat,lon,height obtid,obtname,provname,lat,lon,height 5,30,30,8,8,8 2 and rsts=1 and obtid in ('59293','50745') OBTCODE_ _from_qx /data/shqx/sdata/fromqx 300 \"\n\n");
printf("本程序是数据中心的公共功能模块,从其它业务系统的数据库中挖掘数据,用于入库到数据中心。\n");
printf("logfilename是本程序运行的日志文件。\n");
printf("xmlbuffer为文件传输的参数,如下:\n");
printf("数据库的连接参数 shqx/pwdidc@snorcl11g_198 \n");
printf("数据库的字符集 Simplified Chinese_China.ZHS16GBK 这个参数要与数据源数据库保持>一致,否则会出现中文乱码的情况。\n");
printf("待挖掘数据的表名 T_SURFDATA \n");
printf("需要挖掘字段的列表 obtid,to_char(ddatetime,'yyyymmddhh24miss'),t,p,u,wd,wf,r,vis 可以采用函数。\n");
printf("挖掘字段的别名列表 obtid,ddatetime,t,p,u,wd,wf,r,vis 必须与cols一一对应。\n");
printf("挖掘字段的长度列表 5,14,8,8,8,8,8,8,8 必须与cols一一对应。\n");
printf("挖掘数据的方式 1 1-增量挖掘;2-全量挖掘,如果是增量挖掘,要求表一定要有表达记录序号的id字段。\n");
printf("挖掘数据的附加条件 and obtid in ('59293','50745') 注意,关键字and不能少。\n");
printf("数据文件的命名的前部分 SURFDATA_ \n");
printf("数据文件的命名的后部分 _from_qx \n");
printf("挖掘数据表记录号字段名 keyid 当exptype=1时该参数有效。\n");
printf("已挖掘数据id保存的文件名 /data/dmin/SURFDATA_from_qx.txt 当exptype=1时该参数有效。\n");
printf("挖掘文件存放的目录 /data/shqx/sdata/fromqx \n");
printf("挖掘数据的时间间隔 30 单位:秒,建议大于10。\n");
printf("以上参数,除了idfieldname、idfilename和andstr,其它字段都不允许为空。\n\n\n");
}
// 把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer)
{
memset(&starg,0,sizeof(struct st_arg));
GetXMLBuffer(strxmlbuffer,"connstr",starg.connstr);
if (strlen(starg.connstr)==0) { logfile.Write("connstr is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"charset",starg.charset);
if (strlen(starg.charset)==0) { logfile.Write("charset is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"tname",starg.tname);
if (strlen(starg.tname)==0) { logfile.Write("tname is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"cols",starg.cols);
if (strlen(starg.cols)==0) { logfile.Write("cols is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"fieldname",starg.fieldname);
if (strlen(starg.fieldname)==0) { logfile.Write("fieldname is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"fieldlen",starg.fieldlen);
if (strlen(starg.fieldlen)==0) { logfile.Write("fieldlen is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"exptype",&starg.exptype);
if ( (starg.exptype!=1) && (starg.exptype!=2) ) { logfile.Write("exptype is not in (1,2).\n"); return false; }
GetXMLBuffer(strxmlbuffer,"andstr",starg.andstr);
if (strlen(starg.andstr)==0) { logfile.Write("andstr is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"bname",starg.bname);
if (strlen(starg.bname)==0) { logfile.Write("bname is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"ename",starg.ename);
if (strlen(starg.ename)==0) { logfile.Write("ename is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"idfieldname",starg.idfieldname);
if ( (starg.exptype==1) && (strlen(starg.idfieldname)==0) ) { logfile.Write("idfieldname is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"idfilename",starg.idfilename);
if ( (starg.exptype==1) && (strlen(starg.idfilename)==0) ) { logfile.Write("idfilename is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"exppath",starg.exppath);
if (strlen(starg.exppath)==0) { logfile.Write("exppath is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);
if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }
// 拆分fieldname和fieldlen
SplitFields();
// 判断fieldname和fieldlen中元素的个数一定要相同
if (vfieldname.size() != vfieldlen.size() ) { logfile.Write("fieldname和fieldlen的元素个数不同。.\n"); return false; }
return true;
}
//////////////////////////////////////////////////1.本程序的业务流程主函数
bool _dmintables()
{
// 从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
if (LoadMaxKeyid()==false) { logfile.Write("LoadMaxKeyid() failed.\n"); return false; }
// 生成挖掘数据的SQL语句
char strsql[4096];
char fieldvalue[vfieldname.size()][maxfieldlen+1]; // 输出变量定义为一个二维数组
memset(strsql,0,sizeof(strsql));
if (starg.exptype==1)
sprintf(strsql,"select %s,%s from %s where 1=1 and %s>%ld %s order by %s",starg.cols,starg.idfieldname,starg.tname,starg.idfieldname,maxkeyid,starg.andstr,starg.idfieldname);
else
sprintf(strsql,"select %s from %s where 1=1 %s",starg.cols,starg.tname,starg.andstr);
sqlstatement stmt(&conn);
stmt.prepare(strsql);
for (int ii=0;ii\n");
}
for (int ii=0;ii%s%s>",vfieldname[ii].c_str(),fieldvalue[ii],vfieldname[ii].c_str());
}
File.Fprintf(" \n");
/////////////////////////////////////////////////////////1.1 1000条记录写入一个文件完成
if (stmt.m_cda.rpc%1000==0)
{
File.Fprintf("\n");
if (File.CloseAndRename()==false)
{
logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;
}
// 更新已挖掘数据的最大的id到文件中
if (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }
logfile.Write("create file %s ok.\n",strFileName);
}
}
/////////////////////////////////////////////////////1.2 不够1000条的写入一个文件
if (File.IsOpened()==true)
{
File.Fprintf("\n");
if (File.CloseAndRename()==false)
{
logfile.Write("File.CloseAndRename(%s) failed.\n",strFileName); return false;
}
// 更新已挖掘数据的最大的id到文件中
if (UptMaxKeyid()==false) { logfile.Write("UptMaxKeyid() failed.\n"); return false; }
logfile.Write("create file %s ok.\n",strFileName);
}
if (stmt.m_cda.rpc>0) logfile.Write("本次挖掘了%d条记录。\n",stmt.m_cda.rpc);
return true;
}
/////////////////////////////////////////2.拆分fieldname和fieldlen
void SplitFields()
{
vfieldname.clear(); vfieldlen.clear(); maxfieldlen=0;
CCmdStr CmdStr;
CmdStr.SplitToCmd(starg.fieldname,",");
vfieldname.swap(CmdStr.m_vCmdStr);
int ifieldlen=0;
CmdStr.SplitToCmd(starg.fieldlen,",");
for (int ii=0;iimaxfieldlen) maxfieldlen=ifieldlen; // 得到fieldlen的最大值
vfieldlen.push_back(ifieldlen);
}
}
/////////////////////////////3.从记录已获取数据最大id的文件中加载已挖掘数据的最大的id
bool LoadMaxKeyid()
{
if (starg.exptype!=1) return true;
CFile File;
if (File.Open(starg.idfilename,"r")==false)
{
logfile.Write("注意,%s文件不存在,程序将从新开始挖掘数据。\n",starg.idfilename); return true;
}
char strBuf[21];
memset(strBuf,0,sizeof(strBuf));
File.Fread(strBuf,20);
maxkeyid=atol(strBuf);
logfile.Write("maxkeyid=%d\n",maxkeyid);
return true;
}
////////////////////////////4.更新已挖掘数据的最大的id到文件中
bool UptMaxKeyid()
{
if (starg.exptype!=1) return true;
CFile File;
if (File.Open(starg.idfilename,"w")==false)
{
logfile.Write("File.Open(%s) failed.\n",starg.idfilename); return false;
}
File.Fprintf("%ld",maxkeyid);
return true;
}
2.HTTP协议:wget,转义
浏览器输入网址后回车,就是向服务端(web系统)发送请求,浏览器就是客户端
注册后会分配一个appkeyid加在如上url后面。数据共享平台也有一个web系统(服务端)用java做的,vi /etc/rc.local如下
如下http客户端访问不了,startup后可访问
按照http格式向网站发报文也会得到回应,如下http客户端
wgetclient是用wget命令下载,httpclient下载的内容有时候会有断行,wget下载不会,优先wget,wget下载不了的用http。wget支持http,https,ftp,sftp等协议。linux下同步文件rsync,curl,wget,ftp,sftp
如上调用接口,如下还可以右击网页上图片复制图片地址
程序在后台跑,有新图就拿下来,wgetclient将网页内容全弄下来,搞清图片命名规律进行解析
//wgetclient.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
int main(int argc, char *argv[])
{
if(argc!=6)
{
printf("Usage:%s weburl tmpfilename outputfilename logfilename charset\n",argv[0]);
printf("本程序用于获取WEB网页的内容。\n");
printf("weburl 网页WEB的地址。\n");
printf("tmpfilename 获取到的网页的内容存放的全路径的临时文件名,该文件可能是utf-8或其它编码。\n");
printf("outputfilename 最终的输出文件全路径文件名,该文件是gb18030编码,注意tmpfilename被转换为outputfilename后,tmpfilename文件被自动删除。\n");
printf("logfilename 本程序的运行产生的日志文件名。\n");
printf("charset 网页的字符集,如utf-8\n\n");
exit(1);
}
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件
if (logfile.Open(argv[4],"a+") == false)
{
printf("logfile.Open(%s) failed.\n",argv[4]); return -1;
}
MKDIR(argv[2],true); MKDIR(argv[3],true);
char strweburl[3001];
memset(strweburl,0,sizeof(strweburl));
strncpy(strweburl,argv[1],3000);
char strcmd[3024];
memset(strcmd,0,sizeof(strcmd));
snprintf(strcmd,3000,"/usr/bin/wget -c -q -O %s \"%s\" 1>>/dev/null 2>>/dev/null",argv[2],strweburl);
system(strcmd);
logfile.Write("%s\n",strcmd);
char strfilenametmp[301];
memset(strfilenametmp,0,sizeof(strfilenametmp));
snprintf(strfilenametmp,300,"%s.tmp",argv[3]);
// 把获取到的网页转换为中文
memset(strcmd,0,sizeof(strcmd));
snprintf(strcmd,256,"iconv -c -f %s -t gb18030 %s -o %s",argv[5],argv[2],strfilenametmp);
system(strcmd);
logfile.Write("%s\n",strcmd);
REMOVE(argv[2]); // 删除临时文件
RENAME(strfilenametmp,argv[3]);
return 0;
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("wgetclient exit.\n");
exit(0);
}
/*
除了普通的字母,数字,中文,还有特殊字符,但是规范的使用应该是使用字符转义。
十六进制值
1. + URL 中+号表示空格 %2B
2. 空格 URL中的空格可以用+号或者编码 %20
3. / 分隔目录和子目录 %2F
4. ? 分隔实际的 URL 和参数 %3F
5. % 指定特殊字符 %25
6. # 表示书签 %23
7. & URL 中指定的参数间的分隔符 %26
8. = URL 中指定参数的值 %3D
*/
//wgetrain24.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
bool GetURL(char *strBuffer,char *strURL,char *strFileName);
int main(int argc, char *argv[])
{
if(argc!=4)
{
printf("Usage:%s logfilename tmpfilename outputfilename\n",argv[0]);
printf("Sample:./wgetrain24 /log/shqx/wgetrain24.log /data/wgettmp /data/wfile/zhrain24\n\n");
printf("本程序用于从中国天气网获取逐小时降雨量实况图。\n");
printf("中国天气网的url是http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\n");
printf("如果中国天气网的url改变,程序也在做改动。\n");
printf("logfilename 本程序的运行产生的日志文件名。\n");
printf("tmpfilename 本程序运行产生的临时文件存放的目录。\n");
printf("获取逐小时降雨量实况图存放的目录。\n\n");
exit(1);
}
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件
if (logfile.Open(argv[1],"a+") == false)
{
printf("logfile.Open(%s) failed.\n",argv[1]); return -1;
}
MKDIR(argv[2],false); MKDIR(argv[3],false);
while (true)
{
// 调用wgetclient获取网页内容
char strwgetclient[2001];
memset(strwgetclient,0,sizeof(strwgetclient));
snprintf(strwgetclient,2000,"/htidc/public/bin/wgetclient \"http://products.weather.com.cn/product/Index/index/procode/JC_JSL_ZH.shtml\" %s/wgetclient_%d.tmp %s/wgetclient_%d.html %s/wgetclient.log utf-8",argv[2],getpid(),argv[2],getpid(),argv[2]);
system(strwgetclient);
// logfile.Write("%s\n",strwgetclient);
// 打开网页内容文件
char stroutputfile[301];
memset(stroutputfile,0,sizeof(stroutputfile));
snprintf(stroutputfile,300,"%s/wgetclient_%d.html",argv[2],getpid());
CFile File;
if (File.Open(stroutputfile,"r")==false)
{
logfile.Write("File.Open(%s) failed.\n",stroutputfile); sleep(60); continue;
}
char strBuffer[1001],strURL[501],strFullFileName[301],strFileName[101];
// 得到全部的图片文件名
while (true)
{
memset(strBuffer,0,sizeof(strBuffer));
memset(strURL,0,sizeof(strURL));
memset(strFullFileName,0,sizeof(strFullFileName));
memset(strFileName,0,sizeof(strFileName));
if (File.Fgets(strBuffer,1000)==false) break;
if (MatchFileName(strBuffer,"*PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG*")==false) continue;
// logfile.Write("%s",strBuffer);
// 解析出url和文件名
if (GetURL(strBuffer,strURL,strFileName)==false) continue;
// 文件已存在,不采集
snprintf(strFullFileName,300,"%s/%s",argv[3],strFileName);
if (access(strFullFileName,F_OK)==0) continue;
// 调用wget获取文件
logfile.Write("download %s ",strFileName);
memset(strwgetclient,0,sizeof(strwgetclient));
snprintf(strwgetclient,500,"wget \"%s\" -o %s/wgetrain24.log -O %s",strURL,argv[2],strFullFileName);
system(strwgetclient);
if (access(strFullFileName,F_OK)==0) logfile.WriteEx("ok.\n");
else logfile.WriteEx("failed.\n");
}
File.CloseAndRemove();
sleep(60);
}
return 0;
}
bool GetURL(char *strBuffer,char *strURL,char *strFileName)
{
char *start,*end;
start=end=0;
if ((start=strstr(strBuffer,"http"))==0) return false;
if ((end=strstr(start,"\""))==0) return false; //找双引号
strncpy(strURL,start,end-start);
strcpy(strFileName,strstr(strURL,"PWCP"));
return true;
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("wgetclient exit.\n");
exit(0);
}
3.非结构化数据存储:blob,pzhrain24file
// 本程序演示如何把磁盘文件的文本文件写入Oracle的BLOB字段中。
//filetoblob.cpp,实时生成的不要存oracle的blob字段
#include "_ooci.h"
int main(int argc,char *argv[])
{
// 数据库连接池
connection conn;
// 连接数据库,返回值0-成功,其它-失败
// 失败代码在conn.m_cda.rc中,失败描述在conn.m_cda.message中。
if (conn.connecttodb("scott/tiger@snorcl11g_198","Simplified Chinese_China.ZHS16GBK") != 0)
{
printf("connect database %s failed.\n%s\n","scott/tiger@orcl",conn.m_cda.message); return -1;
}
// SQL语言操作类
sqlstatement stmt(&conn);
// 为了方便演示,把goods表中的记录全删掉,再插入一条用于测试的数据。
// 不需要判断返回值
stmt.prepare("\
BEGIN\
delete from goods;\
insert into goods(id,name,pic) values(1,'商品名称',empty_blob());\
END;");
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;
}
// 使用游标从goods表中提取id为1的pic字段
// 注意了,同一个sqlstatement可以多次使用
// 但是,如果它的sql改变了,就要重新prepare和bindin或bindout变量
stmt.prepare("select pic from goods where id=1 for update");
stmt.bindblob();
// 执行SQL语句,一定要判断返回值,0-成功,其它-失败。
if (stmt.execute() != 0)
{
printf("stmt.execute() failed.\n%s\n%s\n",stmt.m_sql,stmt.m_cda.message); return -1;
}
// 获取一条记录,一定要判断返回值,0-成功,1403-无记录,其它-失败。
if (stmt.next() != 0) return 0;
// 把磁盘文件pic_in.jpg的内容写入BLOB字段,一定要判断返回值,0-成功,其它-失败。
if (stmt.filetolob((char *)"pic_in.jpg") != 0)
{
printf("stmt.filetolob() failed.\n%s\n",stmt.m_cda.message); return -1;
}
// 提交事务
conn.commit();
return 0;
}
// pzhrain24file.cpp
#include "_public.h"
#include "_ooci.h"
#include "_shqx.h"
CLogFile logfile;
CDir Dir;
// 处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5);
connection conn;
void EXIT(int sig);
int main(int argc,char *argv[])
{
if (argc!=7)
{
printf("\n本程序用于处理全国逐小时雨量实况图片文件。\n\n");
printf("/htidc/shqx/bin/pzhrain24file logfilename connstr srcpathname dstpathname tname timetvl\n");
printf("例如:/htidc/shqx/bin/pzhrain24file /log/shqx/pzhrain24file.log shqx/pwdidc@snorcl11g_198 /data/wfile/zhrain24 /qxfile/zhrain24 T_ZHRAIN24 30\n");
printf("logfilename 本程序运行的日志文件名。\n");
printf("connstr 数据库的连接参数。\n");
printf("srcpathname 原始文件存放的目录,文件命名如PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20191101070000000.JPG。\n");
printf("dstpathname 目标文件存放的目录,文件按yyyy/mm/dd组织目录,重命名为zhrain24_yyyymmddhh24miss.jpg。\n");
printf("tname 数据存放的表名。\n");
printf("timetvl 本程序运行的时间间隔,单位:秒。\n");
return -1;
}
// 关闭全部的信号和输入输出
CloseIOAndSignal();
// 处理程序退出的信号
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[1],"a+")==false)
{
printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
}
logfile.Write("程序启动。\n");
while (true)
{
// logfile.Write("开始扫描目录。\n");
// 扫描数据文件存放的目录,只匹配"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG"
if (Dir.OpenDir(argv[3],"PWCP_TWC_WEAP_SFER_ER1_TWC_L88_P9_20*.JPG",1000,true,true)==false)
{
logfile.Write("Dir.OpenDir(%s) failed.\n",argv[3]); sleep(atoi(argv[6])); continue;
}
// 逐个处理目录中的数据文件
while (true)
{
if (Dir.ReadDir()==false) break;
// 处理数据文件
if (_pzhrain24file(argv[2],argv[4],argv[5])==false)
{
logfile.WriteEx("失败。\n"); continue;
}
}
// 断开与数据库的连接
if (conn.m_state==1) conn.disconnect();
sleep(atoi(argv[6]));
}
return 0;
}
void EXIT(int sig)
{
logfile.Write("程序退出,sig=%d\n\n",sig);
exit(0);
}
/////////////////////////////////////////////////处理数据文件
bool _pzhrain24file(char *strargv2,char *strargv4,char *strargv5)
{
char strddatetime[21]; // 文件的数据时间,格式yyyymmddhh24miss
memset(strddatetime,0,sizeof(strddatetime));
strncpy(strddatetime,strstr(Dir.m_FileName,"20"),14);
//搜索文件名PWCP_TWC…中20,后面取14位,重命名为zhrain24_%s.jpg
char strdstfilename[301]; // 目标文件名,不带路径
memset(strdstfilename,0,sizeof(strdstfilename));
snprintf(strdstfilename,300,"zhrain24_%s.jpg",strddatetime);
char strdstfilepath[301]; // 目标文件存放的目录
memset(strdstfilepath,0,sizeof(strdstfilepath));
snprintf(strdstfilepath,300,"%s/",strargv4);
strncat(strdstfilepath,strddatetime,4); strcat(strdstfilepath,"/"); // 年的子目录
strncat(strdstfilepath,strddatetime+4,2); strcat(strdstfilepath,"/"); // 月的子目录
strncat(strdstfilepath,strddatetime+6,2); strcat(strdstfilepath,"/"); // 日的子目录
char strfulldstfilename[301]; // 目标文件名,全路径
memset(strfulldstfilename,0,sizeof(strfulldstfilename));
snprintf(strfulldstfilename,300,"%s%s",strdstfilepath,strdstfilename);
// 如果文件已处理(目标文件已存在),直接返回成功。
if (access(strfulldstfilename,F_OK) == 0) return true;
if (conn.m_state==0)
{
if (conn.connecttodb(strargv2,"Simplified Chinese_China.ZHS16GBK")!=0)
{
logfile.Write("connect database(%s) failed.\n%s\n",strargv2,conn.m_cda.message); return false;
}
// logfile.Write("连接数据库成功。\n");
}
// 把源文件复制到目标文件
if (COPY(Dir.m_FullFileName,strfulldstfilename)==false)
{
logfile.Write("复制文件COPY(%s,%s)...failed.\n",Dir.m_FullFileName,strfulldstfilename); return false;
}
// 把非结构化数据文件写入oracle数据库的表中
if (FileToTable(&conn,&logfile,strargv5,strfulldstfilename,strddatetime)!=0)
{
logfile.Write("把文件%s存入%s...failed.\n",strfulldstfilename,strargv5); return false;
}
logfile.Write("把文件%s存入%s...ok.\n",strfulldstfilename,strargv5);
return true;
}
如下是目标文件夹并做了重命名,如上程序中COPY函数,是复制并不删除,因为采集文件是增量采集,删除了又重新下载
数据库客户端服务端性能不够用文件传输系统
4.数据管理子系统:数据字典表,分表
Oracle数据字典:
是一组表和视图的结构(就像仓库里有什么,每种有多少,哪些空间可用等日记)。自己创建的表,oracle会将这表的信息自动写入系统表,系统表上再创建一视图供客户查看
。数据字典中的表是不能被访问修改(自己用的),但是可以访问数据字典的视图就可以知道自己创建的表的详细信息
数据字典除了以下三类还有
V$_
表示性能或参数设置相关数据比如数据库的字符集,会话数,进程数等等。tab也是视图,tab里面表,视图,同义词都可以查到。若是select * from dba_tables;必须是dba用户才可以查
如下是常用的数据字典表
数据量大了会出现性能和数据管理,迁移,备份问题。
假如有1亿数据量,不能用exp导出,一个文件有几百G导出要一天
。索引设计要合理,不然table scan跑不动。一亿以下的数据单表存放,一亿以上的数据考虑分表
,十亿虽然比一亿容量大十倍,但性能不会
下降十倍,因为索引(比如按首字母找名字)
如下并行数据库分布式查询可解决多表性能低的问题
写一些程序实现
数据管理
,数据删除(deletables.cpp)或放历史表等等。三种表数据结构一样,迁移数据可写成一个通用功能模块程序。若是不同表,只知道表名去查询数据字典得到数据结构
如下hsmtable.cpp(和deletetable.cpp像)数据源表就是要把数据从哪个表迁移出来,列名从数据字典取出,把列拼成一个字符串,获取列名后把数据rowid从源表查出来,生成插入目的表sql,删除源表sql
//hsmtable.cpp
#include "_public.h"
#include "_ooci.h"
char logfilename[301];
char connstr[101];
char srctname[51];
char dsttname[51];
char where[1024];
char hourstr[101];
char localhour[21];
int maxcount=1;
connection conn;
CLogFile logfile;
void EXIT(int sig);
// 获取dsttname表全部的列
char strColumnStr[2048];
bool GetColumnStr();
// 显示程序的帮助
void _help(char *argv[]);
bool _hsmtables();
int main(int argc,char *argv[])
{
if (argc != 2) { _help(argv); return -1; }
memset(logfilename,0,sizeof(logfilename));
memset(connstr,0,sizeof(connstr));
memset(srctname,0,sizeof(srctname));
memset(dsttname,0,sizeof(dsttname));
memset(where,0,sizeof(where));
memset(hourstr,0,sizeof(hourstr));
GetXMLBuffer(argv[1],"logfilename",logfilename,300);
GetXMLBuffer(argv[1],"connstr",connstr,100);
GetXMLBuffer(argv[1],"srctname",srctname,50);
GetXMLBuffer(argv[1],"dsttname",dsttname,50);
GetXMLBuffer(argv[1],"where",where,1000);
GetXMLBuffer(argv[1],"maxcount",&maxcount);
GetXMLBuffer(argv[1],"hourstr",hourstr,2000);
if (strlen(logfilename) == 0) { printf("logfilename is null.\n"); return -1; }
if (strlen(connstr) == 0) { printf("connstr is null.\n"); return -1; }
if (strlen(srctname) == 0) { printf("srctname is null.\n"); return -1; }
if (strlen(dsttname) == 0) { printf("dsttname is null.\n"); return -1; }
if (strlen(where) == 0) { printf("where is null.\n"); return -1; }
if ( (maxcount<1) || (maxcount>1000) ) { printf("maxcount %d is invalid,should in 1-1000.\n",maxcount); return -1; }
if (strlen(hourstr) == 0) { printf("hourstr is null.\n"); return -1; }
// 关闭全部的信号和输入输出
CloseIOAndSignal();
// 处理程序退出的信号
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件
if (logfile.Open(logfilename,"a+") == false)
{
printf("logfile.Open(%s) failed.\n",logfilename); return -1;
}
while (true)
{
// 判断当前时间是否在启动时间之内
memset(localhour,0,sizeof(localhour));
LocalTime(localhour,"hh24");
if (strstr(hourstr,localhour)==0) { sleep(60); continue; }
// 连接数据库
if (conn.connecttodb(connstr,"Simplified Chinese_China.ZHS16GBK") != 0)
{
logfile.Write("connect database %s failed.\n",connstr); sleep(60); continue;
}
logfile.Write("from table %s to %s.\n",srctname,dsttname);
if (_hsmtables() == false) logfile.Write("_hsmtables failed.\n");
conn.disconnect();
sleep(60);
}
return 0;
}
void EXIT(int sig)
{
printf("程序退出,sig=%d\n\n",sig);
exit(0);
}
// 显示程序的帮助
void _help(char *argv[])
{
printf("\nUsing:/htidc/public/bin/hsmtables \"/log/shqx/hsmtables_SURFDATA.log shqx/pwdidc@snorcl11g_198 T_SURFDATA T_SURFDATA_HIS where ddatetime500 23,01,02,03,04,05,06 \"\n\n");
printf("这是一个工具程序,用于清理表中的数据。\n");
printf("/log/shqx/hsmtables_SURFDATA.log 本程序运行日志文件名。\n");
printf("szidc/pwdidc@SZQX_10.153.97.251 目的数据库的连接参数。\n");
printf("T_SURFDATA 数据源表名。\n");
printf("T_SURFDATA_HIS 目的数据表名。\n");
printf("where ddatetime 待迁移数据的条件。\n");
printf("500 单次执行数据迁移的记录数,取值在1-1000之间。\n");
printf("23,01,02,03,04,05,06 本程序启动的时次,小时,时次之间用半角的逗号分隔开。\n\n");
return;
}
bool _hsmtables()
{
// 获取dsttname表全部的列
if (GetColumnStr() == false) return false;
int ccount=0;
char strrowid[51],strrowidn[maxcount][51];
// 把数据的rowid从srctname表中查出来
sqlstatement selstmt(&conn);
selstmt.prepare("select rowid from %s %s",srctname,where);
selstmt.bindout(1, strrowid,50);
if (selstmt.execute() != 0)
{
logfile.Write("%s failed.\n%s\n",selstmt.m_sql,selstmt.m_cda.message); return false;
}
// 生成插入dsttname表和删除srctname表的SQL
int ii=0;
char strInsertSQL[10241],strDeleteSQL[10241];
memset(strInsertSQL,0,sizeof(strInsertSQL));
memset(strDeleteSQL,0,sizeof(strDeleteSQL));
sprintf(strInsertSQL,"insert into %s(%s) select %s from %s where rowid in (",dsttname,strColumnStr,strColumnStr,srctname);
//%s(%s)里面的%s是字段名,字段顺序没必要保持一致,所有把列名从数据字典取出,目的表字段比原表少,只备份一部分字段
sprintf(strDeleteSQL,"delete from %s where rowid in (",srctname);
char strtemp[11];
for (ii=0; ii0) sprintf(strtemp,",:%d",ii+1);
strcat(strInsertSQL,strtemp);
strcat(strDeleteSQL,strtemp);
}
strcat(strInsertSQL,")");
strcat(strDeleteSQL,")");
sqlstatement insstmt(&conn);
insstmt.prepare(strInsertSQL);
sqlstatement delstmt(&conn);
delstmt.prepare(strDeleteSQL);
for (ii=0; ii1) strcat(strColumnStr,",");
strcat(strColumnStr,column_name);
}
if (stmt.m_cda.rpc==0) { logfile.Write("表%s不存在。\n",dsttname); return false; }
return true;
}
$sqlplus shqx/pwdidc,如下数据都是一小时前生成,需要将后台生成数据脚本启动
如下是数据迁移程序如何兼容其他数据库,oracle有rowid,mysql和pg都没有rowid但有keyid,取列名时每个数据库的数据字典都不同
不管id字段是数字还是字符或字符串,我们都可用字符绑定
如下不同数据库查数据字典得到全部列
5.监控告警子系统:分区,mount,fdisk
我们处理后台数据也会有一个web系统,管理参数,展示数据,查询数据等界面(不能让用户去登录plsqldeveloper查询)
如下是数据的监控,数据少于某数量显示红色
如下系统告警,监控参数和日志
数据有延时,统计每隔时次如全国气象站表数据量多少,再用其他程序分析下数据大报是否正常
以下是怎么分区安装centos,df -h 显示G。如下日志等等都在sda中即raid1即2个300G中
以下是收集磁盘空间信息,现在插入一个U盘,fdisk -l可以看到U盘。df可看到%使用率
每个服务器上运行一个收集磁盘空间的小程序,收集到磁盘空间后生成xml文件存放在本地目录,通过文件传输系统或ftp将文件传给数据处理服务器(因为有的服务器不安装oracle客户端,只可安装文件传输客户端),统一保存到数据库。
//diskinfo.cpp,写入xml文件中再入库
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
int main(int argc,char *argv[])
{
if (argc != 4)
{
printf("\n");
printf("Using:./diskinfo hostname logfilename outputpath\n");
printf("Example:/htidc/public/bin/diskinfo 118.89.50.198 /tmp/htidc/log/diskinfo.log /tmp/htidc/monclient\n\n");
printf("此程序调用df命名,把本服务器的磁盘使用率信息写入xml文件。\n");
printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
printf("logfilename是本程序的日志文件名。\n");
printf("outputpath是输出的xml文件存放的目录。\n");
printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");
return -1;
}
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[2],"a+") == false)
{
printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
}
FILE *fp=0;
if ( (fp=popen("df -k --block-size=1M","r")) == NULL )
{
logfile.Write("popen(df -k --block-size=1M) failed.\n"); return false;
}
char strXMLFileName[301],strLocalTime[21];
memset(strXMLFileName,0,sizeof(strXMLFileName));
memset(strLocalTime,0,sizeof(strLocalTime));
LocalTime(strLocalTime,"yyyymmddhh24miss");
snprintf(strXMLFileName,300,"%s/diskinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
CFile XMLFile;
if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
{
logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); pclose(fp); return -1;
}
XMLFile.Fprintf("\n");
CCmdStr CmdStr;
char strBuffer[1024],strLine[500];
while (true)
{
memset(strBuffer,0,sizeof(strBuffer));
if (FGETS(fp,strBuffer,500) == false) break;
// 如果没有找到“%”,就再读取一行,与strBuffer拼起来
if (strstr(strBuffer,"%") == 0)
{
memset(strLine,0,sizeof(strLine));
if (FGETS(fp,strLine,500) == false) break;
strcat(strBuffer," "); strcat(strBuffer,strLine);
}
// 删除字符串前后的空格和换行符
DeleteLRChar(strBuffer,' '); DeleteLRChar(strBuffer,'\n');
// 把字符串中间的多个空格全部转换为一个空格
UpdateStr(strBuffer," "," ");
// 把全内容全部转换为小写
ToLower(strBuffer);
// 除了磁盘信息,还有可能是内存,SMB等其它文件,都丢弃掉
if (strncmp(strBuffer,"/dev",4) != 0) continue;
CmdStr.SplitToCmd(strBuffer," ");
if (CmdStr.CmdCount() != 6) continue;
char strusep[21];
memset(strusep,0,sizeof(strusep));
strcpy(strusep,CmdStr.m_vCmdStr[4].c_str());
UpdateStr(strusep,"%","");
char strLocalTime[21];
memset(strLocalTime,0,sizeof(strLocalTime));
LocalTime(strLocalTime,"yyyymmddhh24miss");
XMLFile.Fprintf(\
"%s "\
"%s "\
"%s "\
"%0.02f "\
"%0.02f "\
"%0.02f "\
"%0.02f "\
"%s \n",
argv[1],
strLocalTime,
CmdStr.m_vCmdStr[0].c_str(),
atof(CmdStr.m_vCmdStr[1].c_str())/1024.0,
atof(CmdStr.m_vCmdStr[2].c_str())/1024.0,
atof(CmdStr.m_vCmdStr[3].c_str())/1024.0,
(atof(CmdStr.m_vCmdStr[2].c_str())/atof(CmdStr.m_vCmdStr[1].c_str()))*100.0,
CmdStr.m_vCmdStr[5].c_str());
}
XMLFile.Fprintf("\n");
pclose(fp);
XMLFile.CloseAndRename();
logfile.Write("create %s ok.\n",strXMLFileName);
exit(0);
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("diskinfo exit.\n");
exit(0);
}
以下为收集CPU和内存信息,top命令显示如下,zombie表示僵尸,q退出,cpu用到20%算忙了
ps -ef |grep htidc,cpuinfo.cpp思路是定义一个数据结构,三个结构体
变量
,加载cpu信息到结构体里,睡60s,再继续加载cpu信息到结构体里,再将两结构体成员相减,就可以知道一分钟内cpu情况,采用的是一分钟信息。vi /proc/stat如下
//cpuinfo.cpp
#include "_public.h"
void EXIT(int sig);
CLogFile logfile;
struct st_cpuinfo
{
double user;
double sys;
double wait;
double nice;
double idle;
double irq;
double softirq;
double total;
};
struct st_cpuinfo stcpuinfo1,stcpuinfo2,stcpuinfo3;
bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo);
int main(int argc,char *argv[])
{
if (argc != 4)
{
printf("\n");
printf("Using:./cpuinfo hostname logfilename outputpath\n");
printf("Example:/htidc/public/bin/cpuinfo 118.89.50.198 /tmp/htidc/log/cpuinfo.log /tmp/htidc/monclient\n\n");
printf("此程序读取/proc/stat文件,把本服务器的CPU使用率信息写入xml文件。\n");
printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
printf("logfilename是本程序的日志文件名。\n");
printf("outputpath是输出的xml文件存放的目录。\n");
printf("此程序运行在需要监控的服务器上(本程序只适用Linux系统),采集后的xml文件由文件传输程序发送给数据处理服务程序入库。\n\n\n");
return -1;
}
//memset(strHostName,0,sizeof(strHostName));
//strncpy(strHostName,argv[2],20);
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[2],"a+") == false)
{
printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
}
memset(&stcpuinfo1,0,sizeof(struct st_cpuinfo));
memset(&stcpuinfo2,0,sizeof(struct st_cpuinfo));
memset(&stcpuinfo3,0,sizeof(struct st_cpuinfo));
if (LoadCPUInfo(stcpuinfo1) ==false) return -1;
sleep(60);
if (LoadCPUInfo(stcpuinfo2) ==false) return -1;
stcpuinfo3.user=stcpuinfo2.user-stcpuinfo1.user;
stcpuinfo3.sys=stcpuinfo2.sys-stcpuinfo1.sys;
stcpuinfo3.wait=stcpuinfo2.wait-stcpuinfo1.wait;
stcpuinfo3.nice=stcpuinfo2.nice-stcpuinfo1.nice;
stcpuinfo3.idle=stcpuinfo2.idle-stcpuinfo1.idle;
stcpuinfo3.irq=stcpuinfo2.irq-stcpuinfo1.irq;
stcpuinfo3.softirq=stcpuinfo2.softirq-stcpuinfo1.softirq;
stcpuinfo3.total=stcpuinfo3.user+stcpuinfo3.sys+stcpuinfo3.wait+stcpuinfo3.nice+stcpuinfo3.idle+stcpuinfo3.irq+stcpuinfo3.softirq;
char strLocalTime[21];
memset(strLocalTime,0,sizeof(strLocalTime));
LocalTime(strLocalTime,"yyyymmddhh24miss");
char strXMLFileName[301];
memset(strXMLFileName,0,sizeof(strXMLFileName));
snprintf(strXMLFileName,300,"%s/cpuinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
CFile XMLFile;
if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
{
logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;
}
XMLFile.Fprintf("\n");
XMLFile.Fprintf("%s %s %0.02f %0.02f %0.02f %0.02f %0.02f %0.02f \n",argv[1],strLocalTime,stcpuinfo3.user/stcpuinfo3.total*100.0,stcpuinfo3.sys/stcpuinfo3.total*100.0,stcpuinfo3.wait/stcpuinfo3.total*100.0,stcpuinfo3.nice/stcpuinfo3.total*100.0,stcpuinfo3.idle/stcpuinfo3.total*100.0,100.0-stcpuinfo3.nice/stcpuinfo3.total*100.0);
XMLFile.Fprintf("\n");
XMLFile.CloseAndRename();
logfile.Write("create %s ok.\n",strXMLFileName);
exit(0);
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("cpuinfo exit.\n");
exit(0);
}
bool LoadCPUInfo(struct st_cpuinfo &stcpuinfo)
{
CFile CPUFile;
if (CPUFile.Open("/proc/stat","r") == false )
{
logfile.Write("CPUFile.OpenForRead(/proc/stat) failed.\n"); return false;
}
CCmdStr CmdStr;
char strBuffer[1024];
while (true)
{
memset(strBuffer,0,sizeof(strBuffer));
if (CPUFile.FFGETS(strBuffer,500) == false) break;
// 删除字符串前后的空格
DeleteLRChar(strBuffer,' ');
// 把字符串中间的多个空格全部转换为一个空格
UpdateStr(strBuffer," "," ");
ToLower(strBuffer);
CmdStr.SplitToCmd(strBuffer," ");
if (strcmp(CmdStr.m_vCmdStr[0].c_str(),"cpu")==0)
{
stcpuinfo.user=atof(CmdStr.m_vCmdStr[1].c_str());
stcpuinfo.sys=atof(CmdStr.m_vCmdStr[2].c_str());
stcpuinfo.wait=atof(CmdStr.m_vCmdStr[3].c_str());
stcpuinfo.nice=atof(CmdStr.m_vCmdStr[4].c_str());
stcpuinfo.idle=atof(CmdStr.m_vCmdStr[5].c_str());
stcpuinfo.irq=atof(CmdStr.m_vCmdStr[6].c_str());
stcpuinfo.softirq=atof(CmdStr.m_vCmdStr[7].c_str());
return true;
}
}
logfile.Write("Read /proc/stat failed.\n");
return false;
}
vi /tmp/htdic/monclient/cpu*,如下是收集到的信息
收集内存信息#free -m,和top命令查看的内存是一样的,也在系统文件vi /proc/meminfo
以下是收集Oracle表空间信息,表空间就像磁盘空间一样,表空间的信息收集要去读取oracle的数据字典,如下sql是查询oracle表空间使用率,取出后写入xml文件里,vi tbspaceinfo.cpp,要dba权限。
select * from (
Select a.tablespace_name,
to_char(a.bytes/1024/1024,'99,999.999') total_bytes,
to_char(b.bytes/1024/1024,'99,999.999') free_bytes,
to_char(a.bytes/1024/1024 - b.bytes/1024/1024,'99,999.999') use_bytes,
to_char((1 - b.bytes/a.bytes)*100,'99.99') || '%' use
from (select tablespace_name,
sum(bytes) bytes
from dba_data_files
group by tablespace_name) a,
(select tablespace_name,
sum(bytes) bytes
from dba_free_space
group by tablespace_name) b
where a.tablespace_name = b.tablespace_name
union all
select c.tablespace_name,
to_char(c.bytes/1024/1024,'99,999.999') total_bytes,
to_char( (c.bytes-d.bytes_used)/1024/1024,'99,999.999') free_bytes,
to_char(d.bytes_used/1024/1024,'99,999.999') use_bytes,
to_char(d.bytes_used*100/c.bytes,'99.99') || '%' use
from
(select tablespace_name,sum(bytes) bytes
from dba_temp_files group by tablespace_name) c,
(select tablespace_name,sum(bytes_cached) bytes_used
from v$temp_extent_pool group by tablespace_name) d
where c.tablespace_name = d.tablespace_name
)
//tbspaceinfo.cpp
#include "_public.h"
#include "_ooci.h"
void EXIT(int sig);
struct st_TBSPACEINFO
{
long taskid;
char nodip[31];
char tablespace[101];
double total;
double used;
double available;
double usep;
double alarmvalue;
int alarmsts;
char crttime[21];
int rsts;
};
struct st_TBSPACEINFO stTBSPACEINFO;
CLogFile logfile;
connection conn;
int main(int argc,char *argv[])
{
if (argc != 5)
{
printf("\n");
printf("Using:./tbspaceinfo hostname logfilename outputpath username/password@tnsnames\n");
printf("Example:/htidc/public/bin/tbspaceinfo 10.153.98.13 /tmp/htidc/log/tbspaceinfo_10.153.98.13.log /tmp/htidc/monclient shqx/pwdidc@SZQX_10.153.98.13\n\n");
printf("此程序连接远程数据库,把远程数据库表空间使用率信息写入xml文件。\n");
printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
printf("logfilename是本程序的日志文件名。\n");
printf("outputpath是输出的xml文件存放的目录。\n");
printf("username/password@tnsnames为待监控的远程数据库的用户名/密码@连接名。\n");
printf("此程序运行在数据中心应用程序的服务器上。\n\n\n");
return -1;
}
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[2],"a+") == false)
{
printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
}
if (conn.connecttodb(argv[4],"Simplified Chinese_China.ZHS16GBK") != 0)
{
logfile.Write("conn.connecttodb(%s) failed.\n",argv[4]); return -1;
}
sqlstatement stmt;
stmt.connect(&conn);
stmt.prepare("\
select f.tablespace_name,a.total,u.used,f.free,(u.used/a.total)*100 from\
(select tablespace_name,sum(bytes/(1024*1024*1024)) total from DBA_DATA_FILES\
group by tablespace_name) a,\
(select tablespace_name,round(sum(bytes/(1024*1024*1024))) used from DBA_EXTENTS\
group by tablespace_name) u,\
(select tablespace_name,round(sum(bytes/(1024*1024*1024))) free from DBA_FREE_SPACE\
group by tablespace_name) f\
where a.tablespace_name = f.tablespace_name\
and a.tablespace_name = u.tablespace_name\
and f.tablespace_name in (select tablespace_name from DBA_TABLESPACES where contents='PERMANENT')");
stmt.bindout(1,stTBSPACEINFO.tablespace,100);
stmt.bindout(2,&stTBSPACEINFO.total);
stmt.bindout(3,&stTBSPACEINFO.used);
stmt.bindout(4,&stTBSPACEINFO.available);
stmt.bindout(5,&stTBSPACEINFO.usep);
if (stmt.execute() != 0)
{
logfile.Write("select DBA_DATA_FILES,DBA_EXTENTS,DBA_FREE_SPACE failed.\n%s\n",stmt.m_cda.message); return -1;
}
char strLocalTime[21];
memset(strLocalTime,0,sizeof(strLocalTime));
LocalTime(strLocalTime,"yyyymmddhh24miss");
char strXMLFileName[301];
memset(strXMLFileName,0,sizeof(strXMLFileName));
snprintf(strXMLFileName,300,"%s/tbspaceinfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
CFile XMLFile;
if (XMLFile.OpenForRename(strXMLFileName,"w+") == false )
{
logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); return -1;
}
XMLFile.Fprintf("\n");
while (true)
{
memset(&stTBSPACEINFO,0,sizeof(stTBSPACEINFO));
if (stmt.next() != 0) break;
XMLFile.Fprintf(\
"%s "\
"%s "\
"%s "\
"%0.02f "\
"%0.02f "\
"%0.02f "\
"%0.02f \n",
argv[1],
strLocalTime,
stTBSPACEINFO.tablespace,
stTBSPACEINFO.total,
stTBSPACEINFO.used,
stTBSPACEINFO.available,
stTBSPACEINFO.usep);
}
XMLFile.Fprintf("\n");
XMLFile.CloseAndRename();
logfile.Write("生成文件%s.\n",strXMLFileName);
exit(0);
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("tbspaceinfo exit.\n");
exit(0);
}
为表空间增加数据文件有两种方式:
固定大小和自动增长
(自动增长对监控意义不大,使用率永远98%,99%)
以下是收集Oracle会话信息,我们用客户端通oracle的监听连上oracle数据库,oracle数据库会启动一个进程向会话提供服务,是多进程的服务端,每增加一连接(进程),需要消耗系统资源(内存,socket连接)。如下一共有6个客户端连上,SQL>exit退出一个就剩下5个。ps -ef |grep oracle可查看oracle数据库系统进程。
如下两个sql是查询数据字典查到进程和上面一样,
第一个sql是查全部包括系统进程
。如下表有一个OSUSER表示连上来的客户端操作系统名字,还可以通过SQL_ID查到连上来的客户端sql语句操作。还可以指定oracle用户的会话数
// dbsessioninfo.cpp
#include "qxmon.h"
void EXIT(int sig);
struct st_DBSESSIONINFO stDBSESSIONINFO;
CLogFile logfile;
connection conndst;
CProgramActive ProgramActive;
int main(int argc,char *argv[])
{
if (argc != 5)
{
printf("\n");
printf("Using:./dbsessioninfo hostname logfilename outputpath username/password@tnsnames\n");
printf("Example:/htidc/htidc/bin/procctl 300 /htidc/qxmon/bin/dbsessioninfo 10.153.98.13 /log/qxmon/dbsessioninfo_10.153.98.13.log /qxdata/qxmon/qxmonclient szidc/pwdidc@SZQX_10.153.98.13\n\n");
printf("此程序连接远程数据库,把远程数据库会话信息写入XML文件。\n");
printf("hostname是本服务器的主机名,为了方便识别,也可以用IP。\n");
printf("logfilename是本程序的日志文件名。\n");
printf("outputpath是输出的XML文件存放的目录。\n");
printf("username/password@tnsnames为待监控的远程数据库的用户名/密码@连接名。\n");
printf("此程序运行在监控平台的服务器上。\n\n\n");
return -1;
}
// 关闭全部的信号和输入输出
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程
// 但请不要用 "kill -9 +进程号" 强行终止
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[2],"a+") == FALSE)
{
printf("logfile.Open(%s) failed.\n",argv[2]); return -1;
}
ProgramActive.SetProgramInfo(&logfile,"alarmserver",300);
if (conndst.connecttodb(argv[4])!=0)
{
logfile.Write("connmon.connecttodb(%s) failed.\n",argv[4]); EXIT(-1);
}
sqlstatement stmt;
stmt.connect(&conndst);
stmt.prepare("select count(*) from V$SESSION where type!='BACKGROUND' and status!='KILLED'");
stmt.bindout(1,&stDBSESSIONINFO.total);
if (stmt.execute() != 0)
{
logfile.Write("select V$SESSION failed.\n%s\n",stmt.cda.message); EXIT(-1);
}
char strLocalTime[21];
memset(strLocalTime,0,sizeof(strLocalTime));
LocalTime(strLocalTime,"yyyymmddhh24miss");
char strXMLFileName[301];
memset(strXMLFileName,0,sizeof(strXMLFileName));
snprintf(strXMLFileName,300,"%s/dbsessioninfo_%s_%s.xml",argv[3],strLocalTime,argv[1]);
CFile XMLFile;
if (XMLFile.OpenForRename(strXMLFileName,"w+") == FALSE )
{
logfile.Write("XMLFile.OpenForRename(%s) failed.\n",strXMLFileName); EXIT(-1);
}
XMLFile.Fprintf("\n");
while (TRUE)
{
memset(&stDBSESSIONINFO,0,sizeof(stDBSESSIONINFO));
if (stmt.next() != 0) break;
XMLFile.Fprintf(\
"%s "\
"%s "\
"%d \n",
argv[1],
strLocalTime,
stDBSESSIONINFO.total);
}
XMLFile.Fprintf("\n");
XMLFile.CloseAndRename();
logfile.Write("生成文件%s.\n",strXMLFileName);
exit(0);
}
void EXIT(int sig)
{
if (sig > 0) signal(sig,SIG_IGN);
logfile.Write("catching the signal(%d).\n",sig);
logfile.Write("dbsessioninfo exit.\n");
exit(0);
}
oracle里有一个系统参数
processes
(最大允许的会话总数)。_ooci.h中connection类连接上一个后进程及processes+1,sqlstatement类实例化并指定数据库连接也会消耗一种资源即open_cursors
参数。oracle有缓存机制存sql,下次用到则不进行语法分析,所以oracle需大内存,存的越多,缓存命中率越高。
收集到了信息放入oracle表,再写一个程序去判断是否告警。告警了的话形成一段文字如哪个服务器ip的cpu使用率超了多少或哪个分区磁盘空间超了多少。告警信息通知维护人员两个方案实现:1.将手机号和短信内容通过接口(数据库表这种接口,http,文件)给短信平台。2.无短信平台,写一个程序分析收到的信息,形成一段文字,调用邮件功能(web服务器:tomcat,java发邮件)