簡體   English   中英

如何在特定場景中避免OutOfMemory問題

[英]How to avoid OutOfMemory issues in particular scenario

我們有后台工作,一次從1000個記錄的批次中取特定表中的記錄。 這項工作間隔30分鍾 現在,這些記錄有電子郵件(密鑰)和原因(值)。 問題是我們必須針對數據倉庫查找這些記錄(過濾類型的東西 - 從倉庫獲取最近180天的數據)。 由於調用數據倉庫的時間非常昂貴(大約45分鍾)。 所以,現有的情況是這樣的。 我們檢查磁盤上的平面文件。 如果它不存在。 我們調用數據倉庫,獲取記錄(大小范圍從0.2百萬到25萬 )並將這些記錄寫在磁盤上。 在后續調用中,我們只從平面文件中查找。 - 將整個文件加載到內存中並進行內存中搜索和過濾。 這導致了OutOfMemory問題很多次。

所以,我們修改了這樣的邏輯。 我們以4個相等的間隔修改了數據倉庫調用,並使用ObjectOutputStream將每個結果再次存儲到文件中。現在,在后續調用中,我們將數據加載到內存中,間隔為0-30天,31-60天等等。 但是,這也沒有幫助。 專家們能否建議解決這個問題的理想方法是什么? 高級團隊中有人建議使用CouchDB存儲和查詢數據。 但是,乍一看,我更願意,如果現有基礎設施有任何好的解決方案可用嗎? 如果沒有,那么可以考慮使用其他工具。

截至目前為止的過濾代碼。

private void filterRecords(List<Record> filterRecords) {
long start = System.currentTimeMillis();
logger.error("In filter Records - start (ms) : "+start);
Map<String, String> invalidDataSet=new HashMap<String, String>(5000);
try{
    boolean isValidTempResultFile = isValidTempResultFile();
    //1. fetch invalid leads data from DWHS only if existing file is 7 days older.

    String[] intervals = {"0","45","90","135"};

    if(!isValidTempResultFile){
        logger.error("#CCBLDM isValidTempResultFile false");
        for(String interval : intervals){
            invalidDataSet.clear();
             // This call takes approx 45 minutes to fetch the data
            getInvalidLeadsFromDWHS(invalidDataSet, interval, start);
            filterDataSet(invalidDataSet, filterRecords);
        }
    }
    else{
        //Set data from temporary file in interval to avoid heap space issue
        logger.error("#CCBLDM isValidTempResultFile true");
        intervals = new String[]{"1","2","3","4"};
        for(String interval : intervals){
            // Here GC won't happen at immediately causing OOM issue.
            invalidDataSet.clear();
            // Keeps 45 days of data in memory at a time
            setInvalidDataSetFromTempFile(invalidDataSet, interval);
            //2. mark current record as incorrect if it belongs to invalid email list
            filterDataSet(invalidDataSet, filterRecords);
        }
    }
}catch(Exception filterExc){
    Scheduler.log.error("Exception occurred while Filtering Records", filterExc);
}finally{
    invalidDataSet.clear();
    invalidDataSet = null;
}

long end = System.currentTimeMillis();
logger.error("Total time taken to filter all records ::: ["+(end-start)+"] ms.");
}

我強烈建議稍微改變你的基礎設施 IIYC你正在查找文件中的某些內容以及地圖中的某些內容。 使用文件很痛苦並且將所有內容加載到內存會導致OOME。 您可以使用內存映射文件做得更好,這樣可以快速簡單地訪問。

有一個Chronicle-Map為堆外存儲的數據提供Map接口。 實際上,數據駐留在磁盤上並根據需要占用主存儲器。 您需要使您的鍵和值可Serializable (您已經使用過AFAIK)或使用其他方式(可能更快)。

它不是數據庫,它只是一個ConcurrentMap ,它使得使用它變得非常簡單。 整個安裝只是添加一行像compile "net.openhft:chronicle-map:3.14.5"build.gradle (或幾個maven行)。

還有其他選擇,但是Chronicle-Map就是我嘗試過的(剛開始使用它,但到目前為止一切都很完美)。

還有Chronicle-Queue,如果你需要批處理,但我強烈懷疑你是否需要它,因為你受到磁盤而不是主內存的限制。

這是批處理類代碼的典型用例。 您可以引入一個新列,例如'isProcessed',其值為'false'。 讀取1-100行(其中id> = 1 && id <= 100),處理它們並將該標記標記為true。 然后閱讀說下一個100,依此類推。 在作業結束時,再次將所有標志標記為false(重置)。 但隨着時間的推移,在這樣的自定義框架上維護和開發功能可能變得困難。 有開源替代品。

Spring批處理是這些用例的一個非常好的框架,可以考慮: https//docs.spring.io/spring-batch/trunk/reference/html/spring-batch-intro.html

還有Easy批處理: https//github.com/j-easy/easy-batch ,它不需要'Spring framework'知識

但如果它是一個巨大的數據集並且將來會繼續增長,請考慮轉向“大數據”技術堆棧

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM