我将尝试简化并解释我的问题:
我正在尝试使用一个包含条目(1000000以上)的大型数据库(mysql),并重新索引另一个数据库中的所有内容(elasticsearch)。为了成功地做到这一点,我必须将条目分成块,向新数据库发送大小合理的请求,并且不要一次向内存加载太多(我的服务器上没有无限的内存)。
以下是三种不同版本的尝试解决方案,它们都会导致类似的内存泄漏(在脚本完成之前,使用量一直增长到1gb以上):
1.
function sync(start_id) {
get_messages_from_db(start_id, function(messages){
if (messages.length == 0) return undefined;
index_in_elastic_search(messages, function(){
next_start_id = message.last.id
setTimeout(function(){
sync(next_start_id)
}, 0);
});
});
}
sync(0)
2.
start_ids = [0, 100, 200... 1000000]
requests_to_sync = []
start_ids.forEach(function(id){
requests_to_sync.push(get_messages_from_db.bind(undefined, id))
});
function sync_requests(requests) {
if (requests.length == 0) return undefined;
requests.shift()(function(messages){
index_in_elastic_search(messages, function(){
setTimeout(function(){
sync_requests(requests);
},0);
});
});
}
sync_requests(requests_to_sync);
最后我尝试了节点异步库,因为我认为他们可能已经解决了这个问题
3.
start_ids = [0, 100, 200... 1000000]
async.series(start_ids, function(id, next){
get_messages(id, function(messages){
index_in_elastic_search(messages, function(){
next();
});
});
});
所以,是的,每种解决方案都是相似的。在实践中,它们执行所需的时间大致相同,并且都会导致内存泄漏。
我猜泄漏与每次迭代都没有正确清除对messages
的引用有关,可能与递归有关。
如果有人能解释为什么这个堆一直在增长,或者我如何解决这个问题。。。那太好了。
Dayum,100万条记录。所以,我猜垃圾收集器实际上从来没有机会做任何工作。我不像js垃圾收集工作原理的大师,但我知道一点。对于这样的事情,我会尽量避免生成函数。我一直认为函数创建是一个代价高昂的过程,无论是在内存还是cpu使用方面。所以…也许可以尝试一些更分散的东西,比如:
/* globals get_messages, index_in_elastic_search */
var state = {
curr_id: 0,
messages: null,
next_func: null
};
get_more_messages();
function get_more_messages() {
get_messages( state.curr_id, on_messages );
}
function on_messages(messages) {
if( messages.length ) {
delete state.messages; // this might help, maybe not
state.messages = messages; // this is probably sufficient to start garbage collection of the old messages
state.next_func = process_messages;
gc_love();
} else {
// maybe call state.done() here as a final callback
}
}
function process_messages() {
state.curr_id = state.messages[messages.length-1].id;
state.next_func = get_more_messages;
index_in_elastic_search(messages, gc_love ); // wait 20 ms and call get_more_messages
}
function gc_love() {
setTimeout(state.next_func, 20);
}
所以我们在这里只创建了4个函数和1个对象。如果你使用所有这些闭包,你会创建更多的函数,并为js范围创建更多的工作,这从来都不是一件好事。