使用python,我应该在数组中缓存大数据并一次写入文件吗?

时间:2013-11-07 15:19:32

标签: python io gevent

我一直都有gevent驱动的抓取工具下载页面。爬虫采用生产者 - 消费者模式,我用这样的数据来提供队列{method:get,url:xxxx,other_info:yyyy}。

现在我想将一些响应汇总到文件中。问题是,我不能只在每个请求结束时打开和写入,而且成本高昂且数据的顺序不正确。

我假设可能是我应该编号所有请求,按顺序缓存响应,打开greenlet循环和汇编文件,伪代码可能是这样的:

max_chunk=1000
data=[]
def wait_and_assemble_file(): # a loop
    while True:
        if len(data)==28:
            f= open('test.txt','a')
            for d in data:
                f.write(d)
            f.close()
        gevent.sleep(0)

def after_request(response, index): # Execute after every request ends
    data[index]=response  # every response is about 5-25k

有更好的解决方案吗?有数千个并发请求,我怀疑内存使用可能会增长得太快,或者一次循环太多,或意外情况。

更新

上面的代码只是演示数据缓存和文件写入的方式。在实际情况中,可能有一百个循环运行等待缓存完成并写入不同的文件。

UPDATE2

@IT Ninja建议使用队列系统,所以我使用Redis编写替代方案:

def after_request(response, session_id, total_block_count ,index): # Execute after every request ends
    redis.lpush(session_id, msgpack.packb({'index':index, 'content':response}))  # save data to redid

    redis.incr(session_id+':count')
    if redis.get(session_id+':count') == total_block_count: # which means all data blocks are prepared
        save(session_name)


def save(session_name):
  data_array=[]
  texts = redis.lrange(session_name,0,-1)
  redis.delete(session_name)
  redis.delete(session_name+':count')
  for t in texts:
    _d = msgpack.unpackb(t)
    index = _d['index']
    content = _d['content']
    data_array[index]=content

  r= open(session_name+'.txt','w')
  [r.write(i) for i in data_array]
  r.close()

看起来好一点,但我怀疑在Redis中保存大数据是个好主意,希望有更多的建议!

2 个答案:

答案 0 :(得分:1)

使用队列系统可以更好地处理这样的事情,而不是每个线程都有自己的文件处理程序。这是因为由于每个线程都有自己的处理程序,因此在编写此文件时可能会遇到竞争条件。

就资源而言,除了磁盘写入之外,这不应该消耗太多资源,假设传递给文件的信息不是非常大(Python真的很好)。如果这确实存在问题,那么以块的形式读取内存(并按比例写入块)可以大大减少此问题,只要这可用作文件上传的选项。

答案 1 :(得分:0)

这取决于数据的大小。如果它非常大,它可以减慢具有内存中所有结构的程序。

如果内存不是问题,则应将结构保留在内存中,而不是始终从文件中读取。使用concurrents请求再次打开文件不是一个好的解决方案。