我们有后台作业,一次从一批1000条记录中取出特定表中的记录。 此作业以间隔30分钟运行。 现在, 这些记录包含电子邮件(密钥)和原因(值)。 问题是我们必须针对数据仓库查找这些记录(过滤类型的东西 - 从仓库获取最近180天的数据)。 由于调用数据仓库的时间非常昂贵(大约45分钟)。 所以,现有的情况是这样的。 我们检查磁盘上的平面文件。如果它不存在。我们打电话给数据仓库,获取记录(大小范围从 20万到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.");
}
答案 0 :(得分:2)
我强烈建议略微更改您的基础架构。 IIYC你正在查找文件中的某些内容以及地图中的某些内容。使用文件很痛苦并且将所有内容加载到内存会导致OOME。使用内存映射文件可以做得更好,这样可以快速简单地访问。
有Chronicle-Map为堆外存储的数据提供Map
接口。实际上,数据驻留在磁盘上并根据需要占用主存储器。你需要制作你的钥匙和价值Serializable
(你已经做过的AFAIK)或使用另一种方式(可能更快)。
它不是数据库,它只是一个ConcurrentMap
,这使得使用它变得非常简单。整个安装只是添加一行compile "net.openhft:chronicle-map:3.14.5"
到build.gradle
(或几个maven行)。
还有其他选择,但Chronicle-Map就是我尝试过的(刚开始使用它,但到目前为止一切都运行良好)。
还有Chronicle-Queue,如果你需要批处理,但我强烈怀疑你是否需要它,因为你受到磁盘而不是主存的限制。
答案 1 :(得分:1)
这是批处理类代码的典型用例。您可以引入一个新列,例如'isProcessed',其值为'false'。阅读说1-100行(其中id&gt; = 1&amp;&amp; id&lt; = 100),处理它们并将该标记标记为true。然后阅读说下一个100,依此类推。在作业结束时,再次将所有标志标记为false(重置)。但随着时间的推移,在这样的自定义框架上维护和开发功能可能变得困难。有开源替代品。
Spring批处理是这些用例的一个非常好的框架,可以考虑:https://docs.spring.io/spring-batch/trunk/reference/html/spring-batch-intro.html。
还有Easy batch:https://github.com/j-easy/easy-batch,它不需要'Spring framework'知识
但如果它是一个巨大的数据集并且将来会继续增长,请考虑转向“大数据”技术堆栈