Redis資料匯入工具優化過程總結

大CC發表於2019-05-12

Redis資料匯入工具優化過程總結

背景

使用C++開發了一個Redis資料匯入工具
從oracle中將所有表資料匯入到redis中;
不是單純的資料匯入,每條oracle中的原有記錄,需要經過業務邏輯處理,
並新增索引(redis集合);
工具完成後,效能是個瓶頸;

優化效果

使用了2個樣本資料測試:
樣本資料a表8763 條記錄;
b表940279 條記錄;

優化前,a表耗時11.417s;
優化後,a表耗時1.883s;

用到的工具

gprof, pstrace,time

使用time工具檢視每次執行的耗時,分別包含使用者時間和系統時間;
使用pstrace列印實時執行,查詢程式主要的系統呼叫,發現耗時點;
使用gprof統計程式的耗時彙總,集中精力優化最耗時的地方;
使用簡介:
1.對g++的所有編輯和連線選項都必須要加上-pg(第一天由於沒有在連線處加上-pg選項,導致無法出統計報告);
2.執行完程式後,本目錄會產生gmon.out檔案;
3.gprof redistool gmou.out > report,生成可讀檔案report,開啟report集中優化最耗時的函式;

優化過程

優化前11.417s:

time ./redistool im a a.csv
real    0m11.417s
user    0m6.035s
sys     0m4.782s (發現系統呼叫時間過長)

檔案記憶體對映

系統呼叫時間過長,主要是檔案讀寫,初步考慮是讀取檔案時,呼叫api次數過於頻繁;
讀取樣本採用的是檔案fgets一行行的讀取,採用檔案記憶體對映mmap後,可直接使用指標操作整個檔案記憶體快;

日誌開關提前

改進了檔案讀寫後,發現優化效果比較有限(提高了2s左右);fgets是C的檔案讀取庫函式,相比系統read(),是帶了緩衝區了,應該不會太慢(網上有人測試,檔案記憶體對映相比fgets()能快上一個數量級,感覺場景應該比較特殊);

之後通過pstrace工具發現log.dat開啟次數過多;原來是除錯日誌的開關寫到了後面,導致 除錯日誌都是會開啟日誌檔案open(“log.dat”);
將日誌開關提前;改進後,3.53s

time ./redistool im a a.csv
real    0m3.530s
user    0m2.890s
sys     0m0.212s

vector空間預先分配

後續通過gprof分析,某個函式的vector記憶體分配次數多,並有不少複製次數:
改進以下這行程式碼:

vector <string> vSegment;

使用靜態vector變數,並預先分配記憶體:

static vector <string> vSegment;
vSegment.clear();
static int nCount = 0;
if( 0 == nCount)
{
    vSegment.reserve(64);
}
++nCount;

優化後,提升至2.286s

real    0m2.286s
user    0m1.601s
sys     0m0.222s

同樣,另外一個類中的成員vector也使用預先分配空間(在建構函式中):

m_vtPipecmd.reserve(256);

優化後,提升至2.166s;

real    0m2.166s
user    0m1.396s
sys     0m0.204s

函式改寫 && 內聯

繼續執行程式,發現SqToolStrSplitByCh()函式消耗過大,改寫整個函式邏輯,並將改寫後的函式內聯:
優化後,提升至1.937s

real    0m1.937s
user    0m1.301s
sys     0m0.186s

去除除錯符和優化監測符號

最後,去掉debug和pg除錯符號後,最終效果為1.883s;

real    0m1.883s
user    0m1.239s
sys     0m0.191s

滿足生產要求

以上最後幾步看似毫秒級的提升,擴大到全表資料後,效果就很明顯了;
優化後,生產上a表為152w,匯入耗時大約326s(~6分鐘);
b表資料420w,匯入耗時大約1103s(~18分鐘)

推薦支援

如果你覺得本文對你有所幫助,請點選以下【推薦】按鈕, 讓更多人閱讀;

相關文章