Ada banyak masalah kinerja di sini jika Anda perlu melakukannya jutaan kali.
-
Anda sedang mempersiapkan pernyataan SQL yang sama berulang-ulang, jutaan kali. Akan lebih baik jika mempersiapkannya sekali dan mengeksekusinya jutaan kali.
-
Anda memutuskan sambungan dari database pada setiap panggilan fungsi setelah satu kueri. Itu berarti Anda perlu menghubungkan kembali setiap kali dan informasi yang disimpan dalam cache akan dibuang. Jangan lakukan itu, biarkan tetap terhubung.
-
Anda melakukan setelah setiap baris. Ini akan memperlambat segalanya. Sebagai gantinya, lakukan setelah melakukan batch.
-
Select + update atau insert mungkin bisa dilakukan sebagai upsert tunggal.
-
Bahwa Anda memasukkan begitu banyak ke dalam tabel sementara mungkin merupakan masalah kinerja.
-
Jika tabel memiliki terlalu banyak indeks yang dapat memperlambat penyisipan. Terkadang yang terbaik adalah menghapus indeks, melakukan pembaruan massal, dan membuatnya kembali.
-
Karena Anda memasukkan nilai langsung ke SQL, SQL Anda terbuka untuk serangan injeksi SQL .
Sebaliknya...
- Gunakan pernyataan yang telah disiapkan dan ikat parameter
- Biarkan database tetap terhubung
- Lakukan pembaruan secara massal
- Hanya komit di akhir rangkaian pembaruan
- Lakukan semua perhitungan di
UPDATE
daripadaSELECT + math + UPDATE
. - Gunakan "UPSERT" sebagai ganti
SELECT
laluUPDATE
atauINSERT
Pertama, pernyataan yang disiapkan. Ini memungkinkan MySQL mengkompilasi pernyataan sekali dan kemudian menggunakannya kembali. Idenya adalah Anda menulis pernyataan dengan placeholder untuk nilai.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Kemudian Anda mengeksekusinya dengan nilai sebagai argumen, bukan sebagai bagian dari string.
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)
)
Hal ini memungkinkan database untuk men-cache pernyataan yang telah disiapkan dan tidak perlu mengkompilasi ulang setiap kali. Itu juga menghindari serangan injeksi SQL di mana penyerang pintar dapat membuat nilai yang sebenarnya lebih SQL seperti " MORE SQL HERE "
. Ini adalah lubang keamanan yang sangat, sangat, sangat umum.
Catatan, Anda mungkin perlu menggunakan milik MySQL Pustaka basis data Python untuk mendapatkan pernyataan yang disiapkan dengan benar . Jangan terlalu khawatir, menggunakan pernyataan yang sudah disiapkan bukanlah masalah kinerja terbesar Anda.
Selanjutnya, yang pada dasarnya Anda lakukan adalah menambahkan ke baris yang sudah ada, atau jika tidak ada baris yang ada, menyisipkan yang baru. Ini dapat dilakukan lebih efisien dalam satu pernyataan dengan UPSERT
, gabungan INSERT
dan UPDATE
. MySQL memilikinya sebagai INSERT ... ON DUPLICATE KEY UPDATE
.
Untuk melihat bagaimana ini dilakukan, kita dapat menulis SELECT then UPDATE
sebagai satu UPDATE
. Perhitungan dilakukan dalam 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
INSERT Anda tetap sama...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Gabungkan keduanya menjadi satu INSERT ON DUPLICATE KEY UPDATE.
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)
Ini tergantung pada apa kunci tabel didefinisikan sebagai. Jika Anda memiliki unique( profile_id, landing_page, keyword )
maka itu harus bekerja sama dengan kode Anda.
Bahkan jika Anda tidak dapat melakukan upsert, Anda dapat menghilangkan SELECT
dengan mencoba UPDATE
, memeriksa apakah itu memperbarui sesuatu, dan jika tidak melakukan INSERT
.
Lakukan pembaruan secara massal. Alih-alih memanggil subrutin yang melakukan satu pembaruan dan komit, berikan daftar besar hal-hal yang akan diperbarui dan kerjakan dalam satu lingkaran. Anda bahkan dapat memanfaatkan executemany
untuk menjalankan pernyataan yang sama dengan beberapa nilai. Kemudian komit.
Anda mungkin dapat melakukan UPSERT
dalam jumlah besar. INSERT
dapat mengambil beberapa baris sekaligus. Misalnya, ini menyisipkan tiga baris.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Anda mungkin dapat melakukan hal yang sama dengan INSERT ON DUPLICATE KEY UPDATE
Anda mengurangi jumlah overhead untuk berbicara dengan database. Lihat postingan ini untuk contoh
(di PHP, tapi Anda harus bisa beradaptasi).
Pengorbanan ini mengembalikan ID dari baris yang disisipkan terakhir, tetapi itu adalah jedanya.