问题:
2009年12月10日,在开发客户维系挽留系统时,进行Bi_Subscrb_Cdr基础数据表抽取初步测试时,发现效率奇低。
原因有以下可能:
1.数据库主机忙,导致上载数据到内存中时,速度过慢。
2.网络繁忙(网速确实很慢),由于当时是前台交互执行程序,但是这个应该影响不大。
3.程序所在主机忙,这个可能性更小。
4.程序本身的数据结构问题。
测试:
1.通过对其他程序测试,比较该程序以往的运行日志,排除数据库和程序主机及网络的主要影响因素。
2.对程序分析:批量数据结构缓冲加载数据二叉树查找时,在数据上载内存平衡二叉树中的时候,
有个数据排序插入链表的过程,这个对后续数据的处理可能会提供一定的帮助。
但是当外来数据要进行多字段累加操作时,再对数据进行排序插入链表时,就会很大程度上影响
数据上载二叉树时的效率...
部分源程序如下:
/*批量数据结构缓冲加载数据二叉树查找*/
int ESearchBiSubscrbCdrBinTree(void *pi,struct BiSubscrbCdrStruct **p,char sTableName[],char sCondition[])
{
static int iFirstFlag=TRUE;
BINTREE *ptHead=pEBiSubscrbCdrBinTree;
if(pi==NULL){
/*调用bintree_free_bi_subscrb_cdr函数释放内存*/
TravelBinTree(ptHead,bintree_free_bi_subscrb_cdr);
iFirstFlag=TRUE;
pEBiSubscrbCdrBinTree = NULL;
return TRUE;
}
if(iFirstFlag){
struct BiSubscrbCdrStruct *pBinTree;
struct BiSubscrbCdrStruct Temp;
struct BiSubscrbCdrStructIn TempIn;
bzero((void*)&TempIn,sizeof(struct BiSubscrbCdrStructIn));
if(strlen(sTableName)!=0){
strcpy(TempIn.sTableName,sTableName);
strcpy(TempIn.sCondition,sCondition);
}
else{
strcpy(TempIn.sTableName,"BI_SUBSCRB_CDR");
strcpy(TempIn.sCondition,"");
}
TempIn.iBufEmpty =TRUE;
TempIn.iFirstFlag = TRUE;
TravelBinTree(ptHead,bintree_free_bi_subscrb_cdr);
/*逐条获取BI_SUBSCRB_CDR 数据进行处理*/
while(EGetBiSubscrbCdrToStruct(&Temp,&TempIn)){
pBinTree=(struct BiSubscrbCdrStruct *)malloc(sizeof(struct BiSubscrbCdrStruct));
if(pBinTree==NULL){
printf("error Malloc BinTree for BiSubscrbCdrStruct./n");
exit(1);
}
memcpy(pBinTree,(void *)&Temp,sizeof(struct BiSubscrbCdrStruct));
/*比较外来节点和被比较节点大小并将外来数据加载到平衡二叉树中*/
AdjustInsertExt(&ptHead,pBinTree,bintree_sort_bi_subscrb_cdr,
bintree_insert_bi_subscrb_cdr);
}
pEBiSubscrbCdrBinTree=ptHead;
iFirstFlag=FALSE;
}
return SearchBinTree(ptHead,pi,bintree_search_bi_subscrb_cdr,(void **)p);
}
/*函数原形int (*pFunction)(void *,void*)*/
/*该函数用于SearchBinTree比较外来数据(参数1)和被比较节点数据大小*/
int bintree_search_bi_subscrb_cdr(void *pValue,void*pData)
{
struct BiSubscrbCdrStruct *pSource=(struct BiSubscrbCdrStruct *)pValue;
struct BiSubscrbCdrStruct *pTarget=(struct BiSubscrbCdrStruct *)pData;
/*数据比较部分*/
int res=0;
if((res=strcmp(pSource->sBillingcyclid,pTarget->sBillingcyclid))!=0) return res;
if((res=(pSource->iSubscrbid-pTarget->iSubscrbid))!=0) return res;
if((res=strcmp(pSource->sAreaid,pTarget->sAreaid))!=0) return res;
return res;
}
/*函数原形void (*pAssign)(BINTREE *,void *)*/
/*该函数用于将外来数据加载到平衡二叉树中*/
/*pHead指针指向的数据不为空*/
void bintree_insert_bi_subscrb_cdr(BINTREE *pHead,void *pData)
{
struct BiSubscrbCdrStruct *pValue=(struct BiSubscrbCdrStruct *)pData;
struct BiSubscrbCdrStruct **ppLkHead=(struct BiSubscrbCdrStruct **)&(pHead->pData);
/*直接插入链表头节点模式*/
/*{
InsertList((LIST**)(&(pHead->pData)),(LIST*)pData);
}*/
/*排序插入链表,内部调用链表排序函数,默认用*/
/*{
InsertListSort((LIST**)(&(pHead->pData)),(LIST*)pData,list_sort_bi_subscrb_cdr);
}*/
/*排序插入链表相同累加,内存是否用去可看是否调用,sort_sum_list*/
InsertListSortSum((LIST**)(&(pHead->pData)),
(LIST*)pData,list_sort_bi_subscrb_cdr,list_sum_bi_subscrb_cdr);
}
int list_sort_bi_subscrb_cdr(LIST *pValue,LIST*pHead)
{
struct BiSubscrbCdrStruct *pSource=(struct BiSubscrbCdrStruct *)pValue;
struct BiSubscrbCdrStruct *pTarget=(struct BiSubscrbCdrStruct *)pHead;
/*加入生成链表的外部数据与链表数据的比较代码*/
/*数据比较部分*/
int res=0;
/*按主键进行查找*/
if((res=strcmp(pSource->sBillingcyclid,pTarget->sBillingcyclid))!=0) return res;
if((res=(pSource->iSubscrbid-pTarget->iSubscrbid))!=0) return res;
if((res=strcmp(pSource->sAreaid,pTarget->sAreaid))!=0) return res;
return res;
}
void list_sum_bi_subscrb_cdr(LIST *pValue,LIST*pHead)
{
struct BiSubscrbCdrStruct *pSource=(struct BiSubscrbCdrStruct *)pValue;
struct BiSubscrbCdrStruct *pTarget=(struct BiSubscrbCdrStruct *)pHead;
/*对以下字段作SUM操作*/
pSource->iCallDura+=pTarget->iCallDura;
pSource->iMoDura+=pTarget->iMoDura;
pSource->iMtDura+=pTarget->iMtDura;
pSource->iTrsDura+=pTarget->iTrsDura;
pSource->iMoLandDura+=pTarget->iMoLandDura;
pSource->iMtLandDura+=pTarget->iMtLandDura;
pSource->iRoamDura+=pTarget->iRoamDura;
pSource->iMoLocalDura+=pTarget->iMoLocalDura;
pSource->iMtLocalDura+=pTarget->iMtLocalDura;
pSource->iCallCmccDura+=pTarget->iCallCmccDura;
pSource->iCallCtcDura+=pTarget->iCallCtcDura;
pSource->iCallCncDura+=pTarget->iCallCncDura;
pSource->iCallCnt+=pTarget->iCallCnt;
pSource->iMoCnt+=pTarget->iMoCnt;
pSource->iMtCnt+=pTarget->iMtCnt;
pSource->iTrsCnt+=pTarget->iTrsCnt;
pSource->iLandCnt+=pTarget->iLandCnt;
pSource->iRoamCnt+=pTarget->iRoamCnt;
pSource->iMoCucCnt+=pTarget->iMoCucCnt;
pSource->iMtCucCnt+=pTarget->iMtCucCnt;
pSource->iMoCmccCnt+=pTarget->iMoCmccCnt;
pSource->iMtCmccCnt+=pTarget->iMtCmccCnt;
pSource->iMoCtcCnt+=pTarget->iMoCtcCnt;
pSource->iMtCtcCnt+=pTarget->iMtCtcCnt;
pSource->iMoCncCnt+=pTarget->iMoCncCnt;
pSource->iMtCncCnt+=pTarget->iMtCncCnt;
pSource->iMoOthCnt+=pTarget->iMoOthCnt;
pSource->iMtOthCnt+=pTarget->iMtOthCnt;
pSource->iMo10010Cnt+=pTarget->iMo10010Cnt;
pSource->iMt10010Cnt+=pTarget->iMt10010Cnt;
pSource->iMo10086Cnt+=pTarget->iMo10086Cnt;
pSource->iMt10086Cnt+=pTarget->iMt10086Cnt;
pSource->iMo10000Cnt+=pTarget->iMo10000Cnt;
pSource->iMt10000Cnt+=pTarget->iMt10000Cnt;
pSource->iMo100060Cnt+=pTarget->iMo100060Cnt;
pSource->iMt100060Cnt+=pTarget->iMt100060Cnt;
pSource->iMoOthServCnt+=pTarget->iMoOthServCnt;
pSource->iMtOthServCnt+=pTarget->iMtOthServCnt;
pSource->iTrsInnerCnt+=pTarget->iTrsInnerCnt;
pSource->iTrsIntrCnt+=pTarget->iTrsIntrCnt;
pSource->iTrsCmccCnt+=pTarget->iTrsCmccCnt;
pSource->iTrsCtcPstnCnt+=pTarget->iTrsCtcPstnCnt;
pSource->iTrsCncPstnCnt+=pTarget->iTrsCncPstnCnt;
pSource->iTrsCtcPhsCnt+=pTarget->iTrsCtcPhsCnt;
pSource->iTrsCncPhsCnt+=pTarget->iTrsCncPhsCnt;
free(pTarget);
}
运行测试结果:
--原程序
--处理数据 4000000条记录
--启动时间 2009-12-11 10:50:24
--结束时间 2009-12-11 11:07:45
--统计结果 平均每秒处理数据 3887条,有效率逐渐降低现象(初始8333条/秒,处理到400w数据时2381条/秒)
改进1:先去掉排序插入链表函数功能
int list_sort_bi_subscrb_cdr(LIST *pValue,LIST*pHead)
{
return 0;
}
运行测试结果:
--去掉排序插入链表
--处理数据 3774982
--启动时间 2009-12-11 10:43:38
--结束时间 2009-12-11 10:44:53
--统计结果 平均每秒处理数据 50333条,无效率降低现象
改进2:再缩小上载数据范围,按单主键比较数据
int bintree_search_bi_subscrb_cdr(void *pValue,void*pData)
{
struct BiSubscrbCdrStruct *pSource=(struct BiSubscrbCdrStruct *)pValue;
struct BiSubscrbCdrStruct *pTarget=(struct BiSubscrbCdrStruct *)pData;
/*数据比较部分*/
int res=0;
/*if((res=strcmp(pSource->sBillingcyclid,pTarget->sBillingcyclid))!=0) return res;*/
if((res=(pSource->iSubscrbid-pTarget->iSubscrbid))!=0) return res;
/*if((res=strcmp(pSource->sAreaid,pTarget->sAreaid))!=0) return res;*/
return res;
}
运行测试结果:
--处理数据 4060334
--启动时间 2009-12-11 10:41:31
--结束时间 2009-12-11 10:42:31
--统计结果 平均每秒处理数据 67672,无效率降低现象
(注:测试时间颠倒的原因是由于程序改进后逐步回退测试的结果)
总结:
当大批量数据上载内存时,如果需要对大量字段进行SUM操作时,那么就要考虑数据处理的效率问题了。
当时在跑遍历语音表的时候,其他地市还好数据不多,平均500万左右,但是A地市有3000多万,B地市也有1000多万。
原程序在运行过程中,当处理数据超过600万时,速度明显下降。当处理到1000万以上时,每10万条数据差不多要6分钟。
如果按照这个效率的话,全省每个月近9000万条的数据量可能一两天都处理不完...
程序改进后,平均每10万数据1.5 秒,9000万条数据只要22.5 分钟就能处理完成,效率提高将近100倍以上!