2013-12-19 6 views
1

У меня довольно стандартный многопроцессорный скрипт, который обрабатывает 2 миллиона записей в таблице базы данных. Прежде чем я даже закончу работу в work_queue, использование памяти превышает 12 ГБ и сбой. Есть ли лучший способ разработать это?Производительность многопроцессорности Python и psycopg2 с большим набором данных

import math 
import psycopg2 
from psycopg2.extras import DictCursor 
from multiprocessing import Process, Manager 

from config import DB 

connection = psycopg2.connect(DB) 
cursor = connection.cursor(cursor_factory=DictCursor) 

def worker(worker_queue, progress): 
    for row in iter(worker_queue.get, None): 
     # Do work 
     progress.put(1) 


if __name__ == "__main__": 
    total, done = 0, 0 

    cursor.execute("SELECT * from table") 

    manager = Manager() 
    worker_queue = manager.Queue() 
    progress = manager.Queue() 

    for row in cursor: 
     worker_queue.put(row) 
     total += 1 

    workers = [Process(target=worker, args=(worker_queue, progress)) for i in range(50)] 

    for each in workers: 
     each.start() 

    for i in iter(progress.get, None): 
     done += 1 

     remaining = total - done 

     if remaining == 0: 
      print 'Done' 
     elif ((remaining % (10 ** int(math.log10(remaining)))) == 0): 
      print str(remaining) + ' remaining' 
+1

Насколько велики ваши данные? Я имею в виду каждую строку. 12Gb более 2 миллионов - это 6.2Kb за строку, которая, кажется, слишком дорога. Если это не так. Возможно, 'select *' не то, что вы хотите - попробуйте уменьшить только до столбцов, которые вы фактически используете. Если это не сработает, возможно, вам придется выполнять эту работу партиями. Другая проблема - «DictCursor». Это, вероятно, удваивает использование вашей памяти, поскольку она превращает кортежи в словари (она должна хранить имена столбцов в каждом dict). Использовать по умолчанию cursor_factory (работа с кортежами). В случае, если имена столбцов имеют значение, равное значению, вы уменьшите использование памяти x2, возможно, больше. – freakish

+1

Обратите внимание, что, например, логическое значение 'True' использует 28b (по крайней мере, в Python3.3), в то время как строка' my_column' 58, которая в два раза больше. Таким образом, вы эффективно задействуете использование памяти с таким булевым столбцом при использовании dicts. – freakish

+0

Да, вот и все! Каждая строка имеет пару сотен полей, и я использовал 20 из них. Выключил DictCursor и только выбрал поля, которые я использовал. Использование памяти превысило 150 мб. – nathancahill

ответ

2

Две вещи, которые стоит отметить

1) Не используйте select *. Для этого есть две причины: во-первых, вы загружаете больше данных, которые вам, вероятно, понадобятся. Во-вторых, вы не контролируете порядок данных (это будет важно после перехода к пункту 2))

2) Не используйте DictCursor. Он превращает каждую строку в dict, который ест лотов памяти (поскольку вы эффективно дублируете имена столбцов в каждом dict). Вместо этого используйте по умолчанию cursor_factory. Теперь, чтобы узнать порядок полей, возвращаемых в кортежах, вы должны указать этот порядок в своем запросе select.

Это должно позаботиться о вашей проблеме. Если это не так, вам нужно выполнить работу над меньшим набором данных.

Смежные вопросы