Odpowiedzi:
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.
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).
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.
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!
Możesz chcieć:
W zależności od tego, jak dobrze twoje łuski serwera (jego ostatecznym OK z PostgreSQl
, Oracle
i MSSQL
), zrobić coś powyżej z wielu wątków i wielu połączeń.
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.
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 :)
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.
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ć?
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.