Co jest szybsze: wiele pojedynczych WSTAWEK lub jeden wielorzędowy WSTAW?

184

Próbuję zoptymalizować jedną część mojego kodu, która wstawia dane do MySQL. Czy powinienem połączyć INSERT w łańcuchy, aby utworzyć jeden wielki INSERT w wielu wierszach, czy też kilka osobnych INSERTów jest szybszych?

dusoft
źródło

Odpowiedzi:

287

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

Czas wymagany do wstawienia wiersza zależy od następujących czynników, gdzie liczby wskazują przybliżone proporcje:

  • Łączenie: (3)
  • Wysyłanie zapytania do serwera: (2)
  • Analiza składniowa: (2)
  • Wstawianie wiersza: (1 × rozmiar rzędu)
  • Wstawianie indeksów: (1 × liczba indeksów)
  • Zamknięcie: (1)

Z tego powinno być oczywiste, że wysłanie jednej dużej instrukcji pozwoli ci zaoszczędzić na nakładzie wynoszącym 7 na instrukcję wstawiania, co przy dalszym czytaniu tekstu mówi również:

Jeśli wstawiasz wiele wierszy od tego samego klienta w tym samym czasie, użyj instrukcji INSERT z wieloma listami WARTOŚCI, aby wstawić kilka wierszy jednocześnie. Jest to znacznie szybsze (w niektórych przypadkach wielokrotnie szybsze) niż stosowanie oddzielnych instrukcji INSERT w jednym wierszu.

mbarkhau
źródło
27
Jak ta odpowiedź ma zastosowanie, jeśli wiele pojedynczych INSERTÓW znajduje się w tej samej transakcji z bazą danych?
Szczypta
2
Ile wierszy mogę wstawić jednocześnie za pomocą instrukcji single insert. czy mogę wstawiać 10000 wierszy jednocześnie?
Naresh Ramoliya 30.04.16
10
@ Pinch Korzystanie z transakcji podczas wykonywania ~ 1,5 tys. Aktualizacji (wstawianie / aktualizacje) skróciło czas operacji z ~ 1,5 sekundy do ~ 0,2 sekundy. Innymi słowy, sprawił, że był o 86% szybszy w porównaniu do płytek jednorzędowych. Cholera.
fgblomqvist
1
Uwaga: W MSSQL
wygląda
Co powiesz na użycie Prepared Statement do wstawiania powtarzalnych wielu pojedynczych wstawek?
priyabagus
151

Wiem, że odpowiadam na to pytanie prawie dwa i pół roku po tym, jak zostało zadane, ale chciałem tylko podać twarde dane z projektu, nad którym teraz pracuję, co pokazuje, że rzeczywiście wykonywanie wielu bloków VALUE na wkładkę jest DUŻO szybciej niż sekwencyjne instrukcje INSERT z pojedynczym blokiem VALUE

Kod, który napisałem dla tego testu porównawczego w języku C #, używa ODBC do odczytu danych ze źródła danych MSSQL (~ 19 000 wierszy, wszystkie są odczytywane przed rozpoczęciem zapisu), a także łącznika MySql .NET (Mysql.Data. *) Do WSTAW dane z pamięci do tabeli na serwerze MySQL za pomocą przygotowanych instrukcji. Został napisany w taki sposób, aby umożliwić mi dynamiczne dostosowywanie liczby bloków WARTOŚĆ na przygotowany WSTAW (tj. Wstawianie n wierszy jednocześnie, w których mogłem dostosować wartość n przed uruchomieniem.) Przeprowadziłem również test wiele razy dla każdego n.

Wykonanie pojedynczych bloków VALUE (np. 1 wiersz na raz) trwało 5,7 - 5,9 sekundy. Pozostałe wartości są następujące:

2 rzędy na raz: 3,5 - 3,5 sekundy
5 rzędów na raz: 2,2 - 2,2 sekundy
10 rzędów na raz: 1,7 - 1,7 sekundy
50 rzędów na raz: 1,17 - 1,18 sekundy
100 rzędów na raz: 1,1 - 1,4 sekundy
500 rzędów na raz: 1,1 - 1,2 sekundy
1000 rzędów na raz: 1,17 - 1,17 sekund

Tak więc, nawet samo połączenie 2 lub 3 zapisów razem zapewnia radykalną poprawę prędkości (czas działania skrócony o współczynnik n), aż dojdziesz do miejsca pomiędzy n = 5 i n = 10, w którym to momencie poprawa wyraźnie spada, a gdzieś w zakresie od n = 10 do n = 50 poprawa staje się znikoma.

Nadzieja, która pomaga ludziom zdecydować (a), czy użyć pomysłu wieloprzygotowania, i (b) ile bloków VALUE należy utworzyć na instrukcję (zakładając, że chcesz pracować z danymi, które mogą być wystarczająco duże, aby przesunąć zapytanie poza maksymalny rozmiar zapytania dla MySQL, który moim zdaniem ma domyślnie 16 MB w wielu miejscach, być może większy lub mniejszy w zależności od wartości parametru max_allowed_packet na serwerze).

Jon Kloske
źródło
1
Żądanie wyjaśnienia: jest to Twój czas „w sekundach na wiersz” lub „w sumie sekund”.
EngrStudent
3
Sekundy łącznie - więc sekundy w rzędzie są podzielone przez ~ 19 000 wierszy. Chociaż jest to niewielka liczba, więc być może rzędy na sekundę są lepszą miarą, jeśli szukasz łatwo porównywalnej liczby.
Jon Kloske
Nawiasem mówiąc, istnieje przykładowy kod .NET dla podejścia, które opisuję powyżej w tej pokrewnej odpowiedzi: stackoverflow.com/questions/25377357/…
Jon Kloske
18

Głównym czynnikiem będzie to, czy korzystasz z silnika transakcyjnego i czy włączono automatyczne zatwierdzanie.

Autocommit jest domyślnie włączony i prawdopodobnie chcesz go pozostawić; dlatego każda wstawka, którą wykonujesz, wykonuje własną transakcję. Oznacza to, że jeśli wykonasz jedną wstawkę na wiersz, będziesz zatwierdzać transakcję dla każdego wiersza.

Zakładając pojedynczy wątek, oznacza to, że serwer musi zsynchronizować niektóre dane z dyskiem dla KAŻDEGO WIERSZA. Musi poczekać, aż dane dotrą do trwałego miejsca przechowywania (mam nadzieję, że pamięć RAM zasilana z kontrolera RAID). Jest to z natury raczej powolne i prawdopodobnie stanie się czynnikiem ograniczającym w tych przypadkach.

Oczywiście zakładam, że używasz silnika transakcyjnego (zwykle innodb) ORAZ że nie poprawiłeś ustawień, aby zmniejszyć wytrzymałość.

Zakładam również, że używasz jednego wątku do wykonania tych wstawek. Używanie wielu wątków trochę marnuje rzeczy, ponieważ niektóre wersje MySQL mają zatwierdzenie grupy roboczej w innodb - oznacza to, że wiele wątków wykonujących własne zatwierdzenia może współużytkować pojedynczy zapis do dziennika transakcji, co jest dobre, ponieważ oznacza to mniej synchronizacji z trwałym magazynem .

Z drugiej strony wynik jest taki, że NAPRAWDĘ CHCESZ UŻYWAĆ wkładek wielorzędowych.

Jest limit, powyżej którego przynosi efekt przeciwny do zamierzonego, ale w większości przypadków jest to co najmniej 10 000 wierszy. Więc jeśli wysyłasz je do 1000 wierszy, prawdopodobnie jesteś bezpieczny.

Jeśli korzystasz z MyISAM, jest mnóstwo innych rzeczy, ale nie będę się nimi nudził. Pokój.

MarkR
źródło
1
Czy jest jakiś powód, dla którego staje się nieproduktywny po pewnym czasie? Widziałem to już wcześniej, ale nie byłem pewien, dlaczego.
Dhruv Gairola,
1
Czy wiesz, czy w ogóle jest sens wsadowego wstawiania MySQL podczas korzystania z transakcji . Zastanawiam się tylko, czy mogę zaoszczędzić sobie trudu generowania wielowartościowego polecenia SQL, jeśli moja podstawowa biblioteka (Java JDBC - mysql-connector-java-5.1.30) faktycznie się nie zobowiązuje, dopóki tego nie powiem.
RTF
@RTF Myślę, że będziesz musiał przeprowadzić mały test, aby określić to zachowanie w twojej sytuacji, ponieważ jest to zachowanie ściśle związane z implementacją, ale w wielu przypadkach tak transakcje powinny zapewnić podobny wzrost wydajności.
Jasmine Hegman
9

Wyślij jak najwięcej wkładek przez drut jednocześnie. Rzeczywista szybkość wstawiania powinna być taka sama, ale można zauważyć wzrost wydajności dzięki zmniejszeniu obciążenia sieci.

RC.
źródło
7

Zasadniczo im mniejsza liczba wywołań bazy danych, tym lepiej (co oznacza szybsze, bardziej wydajne), więc spróbuj kodować wstawki w taki sposób, aby minimalizować dostęp do bazy danych. Pamiętaj, chyba że korzystasz z puli połączeń, każdy dostęp do bazy danych musi utworzyć połączenie, wykonać sql, a następnie zerwać połączenie. Trochę narzutów!

ennuikiller
źródło
co się stanie, jeśli zostanie użyte trwałe połączenie?
dusoft,
6
Nadal jest nad głową. Sam czas transportu (do i z każdej osobnej wkładki) będzie szybko wyczuwalny, jeśli wykonasz tysiące wstawek.
RC.
4

Możesz chcieć:

  • Sprawdź, czy automatyczne zatwierdzanie jest wyłączone
  • Otwórz połączenie
  • Wyślij wiele partii wkładek w jednej transakcji (rozmiar około 4000-10000 wierszy? Widzisz)
  • Zamknij połączenie

W zależności od tego, jak dobrze twoje łuski serwera (jego ostatecznym OK z PostgreSQl, Oraclei MSSQL), zrobić coś powyżej z wielu wątków i wielu połączeń.

Antibarbie
źródło
3

Zasadniczo wiele wstawek będzie wolniejszych z powodu narzutu połączenia. Wykonanie wielu wkładek naraz obniży koszty ogólne na wkładkę.

W zależności od używanego języka można utworzyć partię w języku programowania / skryptów przed przejściem do bazy danych i dodać każdą wstawkę do partii. Następnie można wykonać dużą partię za pomocą jednej operacji łączenia. Oto przykład w Javie.

Chris Williams
źródło
3

MYSQL 5.5 Jedna instrukcja wstawiania SQL zajęła ~ 300 do ~ 450ms. podczas gdy poniższe statystyki dotyczą wbudowanych wielokrotnych instrukcji wstawiania.

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

Powiedziałbym, że inline jest już gotowy :)

A_01
źródło
0

To niedorzeczne, jak źle Mysql i MariaDB są zoptymalizowane, jeśli chodzi o wstawki. Testowałem mysql 5.7 i mariadb 10.3, nie ma na nich żadnej różnicy.

Przetestowałem to na serwerze z dyskami NVME, 70 000 IOPS, przepustowość 1,1 GB / s i to możliwe, że pełny dupleks (odczyt i zapis).
Serwer jest również serwerem o wysokiej wydajności.
Dał 20 GB pamięci RAM.
Baza danych jest całkowicie pusta.

Szybkość, którą otrzymałem, wynosiła 5000 wstawek na sekundę podczas wstawiania wielu wierszy (wypróbowałem to z 1 MB do 10 MB fragmentów danych)

Teraz wskazówka:
jeśli dodam kolejny wątek i wstawię do SAMYCH tabel, nagle otrzymam 2x5000 / s. Jeszcze jeden wątek i mam 15000 ogółem / sek

Zastanów się: podczas wykonywania JEDNEGO wstawiania wątków oznacza to, że możesz sekwencyjnie zapisywać na dysku (z wyjątkami dla indeksów). Podczas korzystania z wątków faktycznie obniża się możliwą wydajność, ponieważ musi ona teraz wykonywać znacznie więcej losowych dostępów. Ale sprawdzanie rzeczywistości pokazuje, że mysql jest tak źle zoptymalizowany, że wątki bardzo pomagają.

Rzeczywista wydajność możliwa przy takim serwerze wynosi prawdopodobnie miliony na sekundę, procesor jest bezczynny, dysk jest bezczynny.
Powodem jest dość wyraźnie, że mariadb podobnie jak mysql ma wewnętrzne opóźnienia.

Jan
źródło
@Craftables potrzebujesz zewnętrznego rozwoju, nie można tego zrobić w mysql. Wątki oznaczają, że korzystasz z wielu połączeń z serwerem, dzielisz zapytanie na wiele części (na przykład dzieląc je na parzyste części według klucza podstawowego). Dzięki tej metodzie udało mi się uzyskać nawet 10.000 razy większą wydajność na bardzo dużych tabelach. Zapytania, które działałyby przez 40 000 sekund, mogą zakończyć się w ciągu 2-3 minut JEŻELI korzystasz z wielu wątków, a mysql jest wysoce zoptymalizowany.
Jan
@John Interesujące i mogą mieć naprawdę fajne aplikacje ... ale ... Jeśli podzielisz zapytanie na wiele części, jak poradzisz sobie z transakcjami? Rozważ także następujący scenariusz: Tabela x ma kolumnę „parent_id”, która odnosi się do tego samego „id” tabeli. Gdzieś w danych masz INSERT INTO x ( id, parent_id) VALUES (1, NULL). Jeden z kolejnych zestawów wartości prowadzi do tego wiersza. Jeśli podzielisz się na części, a ten zestaw przejdzie do innej części, może zostać przetworzony przed pierwszą, co zakończy się niepowodzeniem całego procesu. Masz pomysł, jak sobie z tym poradzić?
zozo
@zozo jest to przydatne w przypadku masowych wstawek i zapytań zbiorczych. Transakcje i tak zepsułyby wydajność, ponieważ obejmują wiele buforów danych. Ale możesz także używać transakcji w wielowątkowych wstawkach lub zapytaniach.
John
-2

wiele wstawek jest szybszych, ale powinno być mniej. innym trikiem jest wyłączanie kontroli ograniczeń, co powoduje, że wstawki są znacznie szybsze. Nie ma znaczenia, czy twój stół to ma, czy nie. Na przykład przetestuj wyłączanie kluczy obcych i ciesz się prędkością:

SET FOREIGN_KEY_CHECKS=0;

offcourse powinieneś włączyć go ponownie po wstawieniu przez:

SET FOREIGN_KEY_CHECKS=1;

jest to powszechny sposób wstawiania dużych danych. integralność danych może się zepsuć, więc powinieneś się tym zająć przed wyłączeniem sprawdzania klucza obcego.

MSS
źródło
1
Nie mam pojęcia, dlaczego ppl głosowało za tym z dwóch powodów: 1. Nie ma to nic wspólnego z pytaniem 2. Jest to naprawdę zły pomysł (z kilkoma wyjątkami - takimi jak dumping lub zmiany temperatur strukturalnych - ale ogólnie zły). Kontrole istnieją z jakiegoś powodu: służą zapewnieniu spójności danych. Spowalniają pracę, ponieważ zapewniają, że nie wstawisz ani nie zmienisz w inny sposób danych, których nie powinieneś. Spróbuj zoptymalizować zapytania we właściwy sposób; w każdym krytycznym środowisku biznesowym oznaczałoby to śmierć aplikacji, ponieważ niezależnie od tego, jak ostrożny jesteś, w pewnym momencie zawiedzie.
zozo
1
może, ale ta opcja jest wyjątkowo skuteczna w imporcie dużych tabel i bardzo praktyczna i może dać niektórym ludziom pojęcie, jak mogą znacznie przyspieszyć wstawianie danych.
MSS