2013-12-13 2 views
0

Я выполняю задачи хэширования данных с 11 параллельными процессами, и результаты каждого вычисления регистрируются в таблице InnoDB базы данных MySQL, используя ORM SQLAlchemy. Однако время обработки больше ожидаемого. Если я профилирую выполнение одного из этих параллельных процессов, я вижу, что около 30% времени тратится на метод expire класса InstanceState, который вызывается ... 292 957 736 раз!LOTS вызова метода expire класса InstanceState SQLAlchemy

Вычисление выполняет цикл с 17106 итерациями, а одно завершение выполняется для каждой итерации. В профиле я вижу, что метод фиксации называется 17,868, который, кажется, находится в хорошем порядке (дополнительный код 761, вероятно, принадлежит к другим частям окружающего кода). Однако мне не совсем понятно, что делает этот метод истечения срока действия и почему его следует назвать так много раз. Он называется КАЖДЫЕ строки таблицы при каждой фиксации или что? Это немного похоже на то, что если 17,106^2 == 292,615,236 ... Является ли это нормальным? Существуют ли какие-либо рецепты или советы о том, как лучше делать что-то в этой ситуации? Точный код немного сложнее [это в __computeForEvent(...) method of this file], но, то SQLAlchemy часть концептуально эквивалентно следующему:

for i in range(17106): 
    propagations = [] 
    for i in range(19): 
     propagations.append(Propagation(...)) 
    session.add_all(propagations) 
    session.commit() 

где Размножение является базовым подклассу. Любые советы о том, как ускорить работу и избежать этого взрыва истекающих (...) звонков, будут очень оценены.

ответ

0

292M звонки на expire() указывают на то, что в памяти появляется много объектов, когда вызывается commit(), что на самом деле невероятно огромно.

Непосредственным способом Никс эти истекают вызовы только повернуть expire_on_commit Ложь:

sess = Session(expire_on_commit=False) 

более тонкий способ обойти эту проблему, но это потребует немного больше заботы, чтобы просто не держать на все эти объекты в памяти, если бы мы сделали:

for i in range(17106): 
    session.add_all([Propagation() for i in range(19)]) 
    session.commit() 

если список распространения() объекты не были сильно ссылки без опорных циклов, предполагая, что CPython они будут мусора в точке де-ссылки, и не подлежит вызов истечения срока действия в commit().

Еще одна стратегия может заключаться в том, чтобы просто задержать фиксацию() до тех пор, пока цикл не будет выполнен, вместо этого используйте flush() для обработки каждого набора элементов за раз. таким образом, большинство объектов будет мусором, собранным к моменту достижения commit().

expire_on_commit остается самым прямым способом решить эту проблему.

+1

Я не тестировал все ваши предложения, но я закончил рефакторинг кода, используя только сбросы в основном коде. Я переместил логику фиксации/отката снаружи, на уровне «вызывающего». Это изменение обеспечило лучший контроль над состоянием базы данных (т. Е. Избежать несогласованных состояний в отношении моего конкретного приложения) и устранить проблему с истечением срока действия. –

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