Mysql
 sql >> база данни >  >> RDS >> Mysql

Ускорете MySQL Update/Insert Statement

Тук има куп проблеми с производителността, ако трябва да направите това милиони пъти.

  • Подготвяте едно и също 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, но трябва да можете да се адаптирате).

Това жертва връщането на идентификатора на последния вмъкнат ред, но това са прекъсванията.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Посоченият ключ беше твърде дълъг; максималната дължина на ключа е 767 байта Mysql грешка в Entity Framework 6

  2. MySQL вмъкване на данни от друга таблица

  3. Как мога да се свържа с MySQL база данни с помощта на Scala?

  4. Как да създам последователност в MySQL?

  5. Как мога да огранича потребител на MySQL до определени таблици