2010-10-15 36 views
12

Tôi đang phát triển một chương trình bằng Python truy cập cơ sở dữ liệu MySQL bằng MySQLdb. Trong một số trường hợp, tôi phải chạy lệnh INSERT hoặc REPLACE trên nhiều hàng. Tôi hiện đang làm như sau:Tại sao thực thi chậm trong Python MySQLdb?

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]) 

Nó hoạt động tốt, nhưng nó là loại khó xử. Tôi đã tự hỏi nếu tôi có thể làm cho nó dễ dàng hơn để đọc, và tôi phát hiện ra về lệnh executemany. Tôi đã thay đổi mã của mình để trông giống như sau:

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

Nó vẫn hoạt động nhưng chạy chậm hơn rất nhiều. Trong các thử nghiệm của tôi, đối với các tập dữ liệu tương đối nhỏ (khoảng 100-200 hàng), nó chạy chậm hơn khoảng 6 lần. Đối với các tập dữ liệu lớn (khoảng 13.000 hàng, lớn nhất tôi mong đợi để xử lý), nó chạy chậm hơn khoảng 50 lần. Tại sao nó làm điều này?

Tôi thực sự muốn đơn giản hóa mã của mình, nhưng tôi không muốn giảm hiệu suất lớn. Có ai biết về bất kỳ cách nào để làm cho nó nhanh hơn?

Tôi đang sử dụng Python 2.7 và MySQLdb 1.2.3. Tôi đã cố gắng tinkering với hàm setinputsizes, nhưng điều đó dường như không làm gì cả. Tôi đã xem mã nguồn MySQLdb và có vẻ như nó không nên làm gì cả.

+0

bạn chèn/thay thế bao nhiêu hàng? câu lệnh thứ hai của bạn tạo ra một danh sách lớn trong bộ nhớ trước khi cho nó vào mysql. – nosklo

+1

Tôi đang thay thế tối đa 13.000 hàng. Tôi không nghĩ rằng việc tạo danh sách là nút cổ chai. Nếu tôi tạo danh sách nhưng không chuyển nó vào con trỏ db, nó hầu như không mất chút thời gian nào cả. –

+0

(Sẽ không trả lời câu hỏi, nhưng ...) 'CHERTN ... VỀ CẬP NHẬT KHÓA CHÍNH ...' gần như luôn luôn tốt hơn 'REPLACE ...'. –

Trả lời

19

Hãy thử giảm từ 'giá trị' trong truy vấn của bạn - điều này dường như là một lỗi/hồi quy trong MySQL-python 1.2.3.

Việc triển khai thực thi của MySQL-python() khớp với mệnh đề VALUES với cụm từ thông dụng và sau đó chỉ sao chép danh sách giá trị cho mỗi hàng dữ liệu, vì vậy bạn sẽ thực hiện chính xác cùng một truy vấn như với phương pháp đầu tiên của mình.

Thật không may biểu thức chính quy mất cờ không phân biệt dạng chữ trong bản phát hành đó (sau đó được cố định trong thân r622 nhưng không bao giờ được quay trở lại nhánh 1,2) để nó giảm xuống lặp lại dữ liệu và kích hoạt truy vấn trên mỗi hàng.

+0

Tôi đã thử điều đó và nó hoạt động! Với "các giá trị" trong chữ thường, nó là về nhanh với executemany vì nó là với thực thi, hoặc đôi khi nhanh hơn một chút. –

+1

Lưu ý rằng regex 1.2.3 không hoạt động với các đối số trong các truy vấn ONPLING ON DUPLICATE KEY UPDATE (regex chỉ khớp với các đối số đầu tiên), vì vậy các giá trị thấp hơn có thể dẫn đến khó hiểu (vì chúng hoạt động với execute()) " tất cả các đối số được chuyển đổi trong khi định dạng chuỗi "lỗi. Để tránh, hãy sử dụng định dạng VALUES() chứ không phải là đối số trong phần TRÊN TỪ KHÓA của truy vấn. –

+0

Nó đã được sửa trong [1.2.4] (https://github.com/farcepest/MySQLdb1/blob/MySQLdb-1.2.4/MySQLdb/cursors.py#L43). – saaj

1

Ví dụ đầu tiên của bạn là một câu lệnh (lớn) được tạo và sau đó được gửi đến cơ sở dữ liệu.

Ví dụ thứ hai là một câu lệnh đơn giản hơn, chèn/thay thế một hàng duy nhất nhưng được thực thi nhiều lần. Mỗi lệnh được gửi đến cơ sở dữ liệu riêng biệt, do đó bạn phải trả thời gian quay vòng từ máy khách đến máy chủ và ngược lại cho mỗi hàng được chèn vào. Tôi sẽ nghĩ rằng độ trễ thêm được giới thiệu giữa các lệnh là lý do chính cho hiệu suất giảm của ví dụ thứ hai.

+0

Đó là những gì tôi nghi ngờ.Tôi nghĩ rằng có lẽ chức năng thực thi là đủ tinh vi để gửi các lệnh tất cả trong một truy vấn, nhưng nó không có vẻ như nó. –

1

Rất không khuyên bạn nên sử dụng executeMany trong pyodbc cũng như ceodbc cả chậm và chứa nhiều lỗi.

Thay vào đó, hãy xem xét sử dụng execute và tự tạo SQL truy vấn bằng định dạng chuỗi đơn giản.

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)) 

Triển khai hiện tại rất đơn giản nhanh chóng và đáng tin cậy.

Các vấn đề liên quan