2013-12-13 4 views
0

11 가지 병렬 프로세스로 데이터 크런치 작업을 수행하고 각 계산 결과를 SQLAlchemy의 ORM을 사용하여 MySQL 데이터베이스의 InnoDB 테이블에 기록합니다. 그러나 처리 시간이 예상보다 큽니다. 이 병렬 프로세스 중 하나의 실행을 프로파일 링하면, 약 30 %의 시간이 InstanceState 클래스의 만료 메소드에 소비된다는 것을 알 수 있습니다 ... 292,957,736 번!SQLAlchemy의 InstanceState 클래스의 만료 메소드 호출이 많음

계산은 17,106 회 반복을 수행하고 각 반복마다 하나의 확약이 수행됩니다. 프로파일에서 나는 커밋 메소드가 17,868이라고 호칭하는 것을 볼 수 있습니다. 이것은 훌륭한 순서로 보입니다 (761 보충 커밋은 주변 코드의 다른 부분에서 온 것임). 그러나 만료 방법이 무엇인지, 왜 그렇게 많은 시간을 호출해야하는지에 대해서는 분명하지 않습니다. 매번 커밋 할 때마다 테이블의 모든 행에 호출됩니까? 그것은 17106^2 == 292,615,236이라면 그렇게 보입니다.이 행동은 정상입니까? 이런 상황에서 더 나은 일을하는 방법에 대한 요리법이나 조언이 있습니까? 전파 기지국 서브 클래스

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

을 : 정확한 코드가 조금 복잡 [그것이 __computeForEvent(...) method of this file이다] 단, SQLAlchemy의 부분이 개념적으로 등가이다. 일을 빠르게하고 만료 (...) 호출이 폭발하는 것을 피하는 방법에 대한 조언은 매우 감사하겠습니다.

답변

0

292M의 expire() 호출은 commit()이 호출 될 때 많은 객체가 메모리에 존재 함을 나타냅니다. 이는 사실 엄청나게 큰 숫자입니다. 이러한 전화를 만료 닉스하는

한 즉각적인 방법은 False로 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()을 지연시키는 것일 수 있습니다. 그런 식으로 대부분의 객체는 commit()에 도달 할 때까지 가비지 수집됩니다.

expire_on_commit은이 문제를 해결하는 가장 직접적인 방법으로 남아 있습니다.

+1

제안 사항을 모두 테스트하지는 않았지만 주 코드에서 플러시 만 사용하여 코드를 리팩토링하는 결과를 보였습니다. "호출자"수준에서 커밋/롤백 논리를 외부로 옮겼습니다. 이 변경으로 인해 데이터베이스 상태가 더 잘 제어되었습니다 (즉, 특정 애플리케이션과 관련하여 일관성없는 상태가 발생하지 않음). 만료 문제가 해결되었습니다. –