2010-10-15 4 views
12

저는 MySQLdb를 사용하여 MySQL 데이터베이스에 액세스하는 Python 프로그램을 개발 중입니다. 어떤 상황에서는 많은 행에서 INSERT 또는 REPLACE 명령을 실행해야합니다. 나는 현재 이렇게하고있다 :Python MySQLdb에서 executemany가 느린 이유는 무엇입니까?

db.execute("REPLACE INTO " + table + " (" + ",".join(cols) + ") VALUES" + 
    ",".join(["(" + ",".join(["%s"] * len(cols)) + ")"] * len(data)), 
    [row[col] for row in data for col in cols]) 

괜찮 았지만 일종의 어색하다. 나는 그것을 더 쉽게 읽을 수 있는지 궁금해서 executemany 명령에 대해 알아 냈습니다. 내 코드를 다음과 같이 바 꾸었습니다 :

db.executemany("REPLACE INTO " + table + " (" + ",".join(cols) + ") " + 
    "VALUES(" + ",".join(["%s"] * len(cols)) + ")", 
    [tuple(row[col] for col in cols) for row in data]) 

여전히 효과가 있었지만 느리게 실행되었습니다. 내 테스트에서 상대적으로 작은 데이터 세트 (약 100-200 행)의 경우 약 6 배 느리게 실행되었습니다. 큰 데이터 세트 (약 13,000 개의 행, 내가 처리 할 것으로 예상되는 최대치)의 경우, 이것은 약 50 배 더 느리게 실행됩니다. 왜이 일을하는거야?

코드를 단순화하고 싶지만 성능이 크게 떨어지는 것을 원하지 않습니다. 누구든지 더 빨리 할 수있는 방법을 알고 있습니까?

저는 파이썬 2.7과 MySQLdb 1.2.3을 사용하고 있습니다. 나는 setinputsizes 함수로 꼼꼼하게 시도했지만 아무 것도하지 않는 것처럼 보였다. 나는 MySQLdb 소스 코드를 보았고 아무 것도해서는 안된다.

+0

삽입/교체 할 행 수는 얼마나됩니까? 두 번째 명령문은 mysql에 넘겨주기 전에 메모리에 거대한리스트를 생성한다. – nosklo

+1

최대 13,000 개의 행을 바꿀 예정입니다. 목록을 만드는 것이 병목이라고 생각하지 않습니다. 목록을 작성했지만 DB 커서에 전달하지 않으면 거의 시간이 걸리지 않습니다. –

+0

(질문에 답할 수는 없지만 ...)'INSERT ... ON DUPLICATE KEY UPDATE ... '는 대체로'REPLACE ...'보다 낫습니다. –

답변

19

쿼리에서 '값'이라는 단어를 소문자로 바꾸십시오 - 이것은 MySQL-python 1.2.3의 버그/회귀 변수 인 것으로 보입니다.

MySQL-python의 executemany() 구현은 VALUES 절을 정규 표현식과 일치시키고 각 데이터 행의 값 목록을 복제하기 때문에 첫 번째 방법과 완전히 동일한 쿼리를 실행하게됩니다.

불행히도 정규식은 해당 릴리스에서 대/소문자를 구분하지 않는 플래그를 잃어 버렸습니다 (트렁크 r622에 고정되었지만 1.2 분기로 결코 백 포트되지 않았 음). 따라서 데이터를 반복 실행하고 행당 쿼리를 실행하는 속도가 저하됩니다.

+0

나는 그것을 시험해 보았다! 소문자로 된 "값"을 사용하면 executemany의 실행 속도가 빨라지거나 때로는 조금 빠릅니다. –

+1

1.2.3 정규식은 ON DUPLICATE KEY UPDATE 쿼리의 인수와 함께 작동하지 않습니다 (정규 표현식은 첫 번째 인수와 만 일치 함). 따라서 소문자 값이 혼란을 야기 할 수 있습니다 (execute()로 작업하기 때문에) "not 모든 인수는 문자열 형식화 중에 변환됩니다. " 이를 방지하려면 쿼리의 ON DUPLICATE KEY 부분에있는 인수 대신 VALUES() 형식을 사용하십시오. –

+0

[1.2.4] (https://github.com/farcepest/MySQLdb1/blob/MySQLdb-1.2.4/MySQLdb/cursors.py#L43)에서 수정되었습니다. – saaj

1

첫 번째 예는 생성되어 데이터베이스로 전송되는 단일 (큰) 문입니다.

두 번째 예제는 단일 행을 삽입/대체하지만 여러 번 실행되는 훨씬 간단한 문입니다. 각 명령은 별도로 데이터베이스로 보내 지므로 클라이언트에서 서버로, 그리고 삽입 된 모든 행에 대해 소요 시간을 지불해야합니다. 두 번째 예제의 성능이 떨어지는 주된 원인은 명령들 사이에 도입 된이 추가 대기 시간 때문이라고 생각합니다.

+0

그건 내가 의심하는 것입니다.나는 아마도 executemany 기능이 하나의 질의로 모든 명령을 전송할 정도로 정교하다고 생각했지만, 그렇게 생각하지는 않습니다. –

1

executeManypyodbc과 마찬가지로 ceodbc에서 느리게 발생하며 많은 버그가 포함되어 있습니다.

대신 execute을 사용하고 간단한 문자열 형식을 사용하여 수동으로 SQL 쿼리를 구성하십시오.

transaction = "TRANSACTION BEGIN {0} COMMIT TRANSACTION 

bulkRequest = "" 
for i in range(0, 100) 
    bulkRequest = bulkRequest + "INSERT INTO ...... {0} {1} {2}" 

ceodbc.execute(transaction.format(bulkRequest)) 

현재 구현은 빠르고 간단합니다.

관련 문제