1. 程式人生 > >批量資料結構緩衝載入資料二叉樹查詢的效率優化測試

批量資料結構緩衝載入資料二叉樹查詢的效率優化測試

問題:
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倍以上!