Có một loạt các vấn đề về hiệu suất ở đây nếu bạn cần làm điều này hàng triệu lần.
-
Bạn đang chuẩn bị cùng một câu lệnh SQL lặp đi lặp lại hàng triệu lần. Sẽ tốt hơn nếu chuẩn bị một lần và thực thi hàng triệu lần.
-
Bạn đang ngắt kết nối khỏi cơ sở dữ liệu trên mọi lệnh gọi hàm sau một truy vấn duy nhất. Điều đó có nghĩa là bạn cần phải kết nối lại mỗi lần và mọi thông tin đã lưu trong bộ nhớ cache sẽ bị loại bỏ. Đừng làm vậy, hãy để nó kết nối.
-
Bạn đang cam kết sau mỗi hàng. Điều này sẽ làm mọi thứ chậm lại. Thay vào đó, hãy cam kết sau khi thực hiện một loạt.
-
Chọn + cập nhật hoặc chèn có thể được thực hiện như một lần nâng cấp duy nhất.
-
Việc bạn đang chèn quá nhiều vào bảng tạm thời có thể là một vấn đề về hiệu suất.
-
Nếu bảng có quá nhiều chỉ mục có thể làm chậm quá trình chèn. Đôi khi, tốt nhất là giảm chỉ mục, cập nhật hàng loạt lớn và tạo lại chúng.
-
Bởi vì bạn đang đặt các giá trị trực tiếp vào SQL của mình, SQL của bạn sẽ bị tấn công tấn công chèn SQL .
Thay vào đó ...
- Sử dụng các câu lệnh đã chuẩn bị và các tham số ràng buộc
- Để cơ sở dữ liệu được kết nối
- Cập nhật hàng loạt
- Chỉ cam kết khi kết thúc một đợt cập nhật
- Làm tất cả các phép toán trong
UPDATE
thay vào đó làSELECT + math + UPDATE
. - Sử dụng "UPSERT" thay vì
SELECT
rồi đếnUPDATE
hoặcINSERT
Trước hết, các báo cáo đã chuẩn bị. Chúng cho phép MySQL biên dịch câu lệnh một lần và sau đó sử dụng lại nó. Ý tưởng là bạn viết một câu lệnh với trình giữ chỗ cho các giá trị.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Sau đó, bạn thực thi điều đó với các giá trị là đối số, không phải là một phần của chuỗi.
self.cursor.execute(
'select id, position, impressions, clicks, ctr from temp where profile_id=%s and keyword=%s and landing_page=%s',
(profile_id, keyword, landing_page)
)
Điều này cho phép cơ sở dữ liệu lưu vào bộ nhớ cache câu lệnh đã chuẩn bị và không phải biên dịch lại nó mỗi lần. Nó cũng tránh một cuộc tấn công đưa vào SQL trong đó kẻ tấn công thông minh có thể tạo ra một giá trị thực sự giống SQL hơn như " MORE SQL HERE "
. Đây là một lỗ hổng bảo mật rất rất rất phổ biến.
Lưu ý, bạn có thể cần sử dụng MySQL của riêng Thư viện cơ sở dữ liệu Python để nhận các câu lệnh chuẩn bị thực sự . Đừng lo lắng về điều đó quá nhiều, việc sử dụng các báo cáo đã chuẩn bị không phải là vấn đề lớn nhất về hiệu suất của bạn.
Tiếp theo, những gì bạn đang làm về cơ bản là thêm vào một hàng hiện có hoặc nếu không có hàng hiện có, hãy chèn một hàng mới. Điều này có thể được thực hiện hiệu quả hơn trong một câu lệnh duy nhất với UPSERT
, một INSERT
kết hợp và UPDATE
. MySQL có nó là INSERT ... ON DUPLICATE KEY UPDATE
.
Để xem việc này được thực hiện như thế nào, chúng tôi có thể viết SELECT then UPDATE
dưới dạng một UPDATE
duy nhất . Các phép tính được thực hiện trong SQL.
update temp
set impressions = impressions + %s,
clicks = clicks + %s,
ctr = (ctr + %s / 2)
where profile_id=%s and
keyword=%s and
landing_page=%s
CHÈN của bạn vẫn như cũ ...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Kết hợp chúng thành một CHÈN CẬP NHẬT KHÓA DUPLICATE.
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
on duplicate key update
update temp
set impressions = impressions + %s,
clicks = clicks + %s,
ctr = (ctr + %s / 2)
Điều này phụ thuộc vào các khóa của bảng được định nghĩa là gì. Nếu bạn có unique( profile_id, landing_page, keyword )
thì nó sẽ hoạt động giống như mã của bạn.
Ngay cả khi bạn không thể thực hiện nâng cấp, bạn có thể loại bỏ SELECT
bằng cách thử UPDATE
, kiểm tra xem nó có cập nhật gì không và nếu nó không thực hiện INSERT
.
Cập nhật hàng loạt. Thay vì gọi một chương trình con thực hiện một lần cập nhật và cam kết, hãy chuyển cho nó một danh sách lớn những thứ cần được cập nhật và xử lý chúng trong một vòng lặp. Bạn thậm chí có thể tận dụng executemany
để chạy cùng một câu lệnh với nhiều giá trị. Sau đó cam kết.
Bạn có thể thực hiện UPSERT
với số lượng lớn. INSERT
có thể lấy nhiều hàng cùng một lúc. Ví dụ:điều này sẽ chèn ba hàng.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Bạn có thể làm tương tự với INSERT ON DUPLICATE KEY UPDATE
giảm số lượng chi phí để nói chuyện với cơ sở dữ liệu. Xem bài đăng này để biết ví dụ
(bằng PHP, nhưng bạn sẽ có thể thích ứng).
Điều này hy sinh trả lại ID của hàng được chèn cuối cùng, nhưng chúng là điểm ngắt.