Тук има куп проблеми с производителността, ако трябва да направите това милиони пъти.
-
Подготвяте едно и също SQL изявление отново и отново, милиони пъти. Би било по-добре да го подготвите веднъж и да го изпълните милиони пъти.
-
Прекъсвате връзката с базата данни при всяко извикване на функция след една заявка. Това означава, че трябва да се свързвате отново всеки път и всяка кеширана информация се изхвърля. Не правете това, оставете го свързано.
-
Вие се ангажирате след всеки ред. Това ще забави нещата. Вместо това, ангажирайте след извършване на партида.
-
Изборът + актуализация или вмъкване вероятно може да се извърши като еднократно прехвърляне.
-
Това, че вмъквате толкова много във временна таблица, вероятно е проблем с производителността.
-
Ако таблицата има твърде много индекси, които могат да забавят вмъкването. Понякога е най-добре да махнете индексите, да направите голяма пакетна актуализация и да ги създадете отново.
-
Тъй като поставяте стойности директно във вашия SQL, вашият SQL е отворен за атака с инжектиране на SQL .
Вместо това...
- Използвайте подготвени изрази и параметри за свързване
- Оставете базата данни свързана
- Правете групови актуализации
- Отвържете само в края на серия от актуализации
- Направете цялата математика в
UPDATE
вместоSELECT + math + UPDATE
. - Използвайте „UPSERT“ вместо
SELECT
след товаUPDATE
илиINSERT
Първо, подготвени изявления. Те позволяват на MySQL да компилира изявлението веднъж и след това да го използва повторно. Идеята е да напишете изявление с заместители за стойностите.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
След това го изпълнявате със стойностите като аргументи, а не като част от низа.
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)
)
Това позволява на базата данни да кешира подготвения израз и да не се налага да го компилира всеки път. Той също така избягва атака с инжектиране на SQL, при която умен нападател може да създаде стойност, която всъщност е повече SQL като " MORE SQL HERE "
. Това е много, много, много често срещана дупка в сигурността.
Имайте предвид, че може да се наложи да използвате собствения MySQL Библиотека на база данни на Python, за да получите истински подготвени изявления . Не се тревожете твърде много, използването на подготвени оператори не е най-големият ви проблем с производителността.
След това това, което основно правите, е добавяне към съществуващ ред или ако няма съществуващ ред, вмъкване на нов. Това може да се направи по-ефективно с един израз с UPSERT
, комбиниран INSERT
и UPDATE
. MySQL го има като INSERT ... ON DUPLICATE KEY UPDATE
.
За да видим как става това, можем да напишем вашия SELECT then UPDATE
като единична UPDATE
. Изчисленията се извършват в 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 остава същият...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Комбинирайте ги в едно INSERT FOR 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)
Това зависи от това какво са дефинирани ключовете на таблицата. Ако имате unique( profile_id, landing_page, keyword )
тогава трябва да работи по същия начин като вашия код.
Дори и да не можете да направите upsert, можете да премахнете SELECT
като опитате UPDATE
, проверявайки дали е актуализирал нещо и дали не е направил INSERT
.
Правете актуализациите на едро. Вместо да извиквате подпрограма, която извършва една актуализация и извършва ангажимент, предайте й голям списък с неща, които трябва да бъдат актуализирани, и работете върху тях в цикъл. Можете дори да се възползвате от executemany
за да изпълните едно и също изявление с множество стойности. След това се ангажирайте.
Може да успеете да направите UPSERT
в насипно състояние. INSERT
може да заема няколко реда наведнъж. Например, това вмъква три реда.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Вероятно можете да направите същото с вашия INSERT ON DUPLICATE KEY UPDATE
намаляване на размера на режийните разходи за разговор с базата данни. Вижте тази публикация за пример
(в PHP, но трябва да можете да се адаптирате).
Това жертва връщането на идентификатора на последния вмъкнат ред, но това са прекъсванията.