Zmagam się z hurtowym importowaniem dość dużego stołu InnoDB składającego się z około 10 milionów wierszy (lub 7 GB) (który jest dla mnie największym stołem, z jakim pracowałem do tej pory).
Przeprowadziłem badania, jak poprawić szybkość importowania Inno i na razie moja konfiguracja wygląda następująco:
/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8
import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;
Dane są dostarczane w CSV
pliku.
Obecnie testuję moje ustawienia za pomocą mniejszych „zrzutów testowych” z 2 milionami, 3 milionami… rzędów każdy i używam time import_script.sh
do porównania wydajności.
Wadą jest to, że otrzymuję tylko ogólny czas działania, więc muszę czekać na zakończenie pełnego importu, aby uzyskać wynik.
Moje dotychczasowe wyniki:
- 10 000 rzędów: <1 sekunda
- 100 000 rzędów: 10 sekund
- 300 000 rzędów: 40 sekund
- 2 miliony wierszy: 18 minut
- 3 miliony wierszy: 26 minut
- 4 miliony wierszy: (anulowane po 2 godzinach)
Wygląda na to, że nie ma rozwiązania „książki kucharskiej” i należy samodzielnie ustalić optymalną kombinację ustawień.
Oprócz sugestii na temat tego, co należy zmienić w mojej konfiguracji, doceniłbym również więcej informacji, w jaki sposób mógłbym lepiej porównać proces importowania / uzyskać lepszy wgląd w to, co się dzieje i gdzie może być wąskie gardło.
Próbowałem przeczytać dokumentację ustawień, które zmieniam, ale z drugiej strony nie jestem świadomy żadnych skutków ubocznych, a jeśli nawet obniżę wydajność przy źle dobranej wartości.
W tej chwili chciałbym wypróbować sugestię z czatu do użycia MyISAM
podczas importowania i później zmienić silnik tabeli.
Chciałbym tego spróbować, ale w tej chwili moje DROP TABLE
zapytanie również kończy się kilka godzin. (Co wydaje się kolejnym wskaźnikiem, moje ustawienie jest mniej niż optymalne).
Informacje dodatkowe:
Aktualnie używany komputer ma 8 GB pamięci RAM i hybrydowy dysk twardy Solid State w / 5400 RPM.
Chociaż staramy się również usunąć przestarzałe dane z omawianej tabeli, nadal potrzebuję dość szybkiego importu do
a) testowania automatic data cleanup feature
podczas programowania
ib) w przypadku awarii naszego serwera chcielibyśmy użyć drugiego serwera jako zamiennika (co wymaga -data danych, ostatni import zajął ponad 24 godziny)
mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
Table: monster
Create Table: CREATE TABLE `monster` (
`monster_id` int(11) NOT NULL AUTO_INCREMENT,
`ext_monster_id` int(11) NOT NULL DEFAULT '0',
`some_id` int(11) NOT NULL DEFAULT '0',
`email` varchar(250) NOT NULL,
`name` varchar(100) NOT NULL,
`address` varchar(100) NOT NULL,
`postcode` varchar(20) NOT NULL,
`city` varchar(100) NOT NULL,
`country` int(11) NOT NULL DEFAULT '0',
`address_hash` varchar(250) NOT NULL,
`lon` float(10,6) NOT NULL,
`lat` float(10,6) NOT NULL,
`ip_address` varchar(40) NOT NULL,
`cookie` int(11) NOT NULL DEFAULT '0',
`party_id` int(11) NOT NULL,
`status` int(11) NOT NULL DEFAULT '2',
`creation_date` datetime NOT NULL,
`someflag` tinyint(1) NOT NULL DEFAULT '0',
`someflag2` tinyint(4) NOT NULL,
`upload_id` int(11) NOT NULL DEFAULT '0',
`news1` tinyint(4) NOT NULL DEFAULT '0',
`news2` tinyint(4) NOT NULL,
`someother_id` int(11) NOT NULL DEFAULT '0',
`note` varchar(2500) NOT NULL,
`referer` text NOT NULL,
`subscription` int(11) DEFAULT '0',
`hash` varchar(32) DEFAULT NULL,
`thumbs1` int(11) NOT NULL DEFAULT '0',
`thumbs2` int(11) NOT NULL DEFAULT '0',
`thumbs3` int(11) NOT NULL DEFAULT '0',
`neighbours` tinyint(4) NOT NULL DEFAULT '0',
`relevance` int(11) NOT NULL,
PRIMARY KEY (`monster_id`),
KEY `party_id` (`party_id`),
KEY `creation_date` (`creation_date`),
KEY `email` (`email`(4)),
KEY `hash` (`hash`(8)),
KEY `address_hash` (`address_hash`(8)),
KEY `thumbs3` (`thumbs3`),
KEY `ext_monster_id` (`ext_monster_id`),
KEY `status` (`status`),
KEY `note` (`note`(4)),
KEY `postcode` (`postcode`),
KEY `some_id` (`some_id`),
KEY `cookie` (`cookie`),
KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
źródło
SHOW CREATE TABLE yourtable\G
aby pokazać nam strukturę tabeli tego 10-milionowego wiersza.innodb_doublewrite = 0
) powoduje, że instalacja MySQL nie jest bezpieczna w przypadku awarii: jeśli wystąpi awaria zasilania (nie awaria MySQL), dane mogą zostać po cichu uszkodzone.Odpowiedzi:
Po pierwsze, musisz wiedzieć, co robisz z InnoDB, gdy zaorasz miliony wierszy w tabeli InnoDB. Rzućmy okiem na architekturę InnoDB.
W lewym górnym rogu znajduje się ilustracja puli buforów InnoDB. Zauważ, że jest jej część poświęcona buforowi wstawiania. Co to robi Służy do migrowania zmian do indeksów wtórnych z puli buforów do bufora wstawiania w systemowym obszarze tabel (alias ibdata1). Domyślnie innodb_change_buffer_max_size jest ustawiony na 25. Oznacza to, że do 25% puli buforów można wykorzystać do przetwarzania indeksów wtórnych.
W twoim przypadku masz 6,935 GB na pulę buforów InnoDB. Do przetworzenia twoich indeksów dodatkowych zostanie wykorzystane maksymalnie 1,734 GB.
Teraz spójrz na swój stół. Masz 13 indeksów wtórnych. Każdy przetwarzany wiersz musi wygenerować dodatkowy wpis indeksu, połączyć go z kluczem podstawowym wiersza i wysłać jako parę z bufora wstawiania w puli buforów do bufora wstawiania w ibdata1. Zdarza się to 13 razy w każdym rzędzie. Pomnóż to przez 10 milionów, a niemal poczujesz wąskie gardło.
Nie zapominaj, że zaimportowanie 10 milionów wierszy w jednej transakcji zgromadzi wszystko w jednym segmencie wycofywania i wypełni przestrzeń UNDO w ibdata1.
PROPOZYCJE
SUGESTIA # 1
Moją pierwszą sugestią byłoby zaimportowanie tej dość dużej tabeli
SUGESTIA # 2
Pozbądź się duplikatów indeksów. W twoim przypadku masz
Oba indeksy zaczynają się od
party_id
, możesz zwiększyć przetwarzanie indeksu dodatkowego o co najmniej 7,6%, pozbywając się jednego indeksu z 13. Musisz w końcu uruchomićSUGESTIA # 3
Pozbądź się indeksów, których nie używasz. Przejrzyj kod aplikacji i sprawdź, czy zapytania korzystają ze wszystkich indeksów. Możesz przyjrzeć się wykorzystaniu pt-index-use, aby zasugerowało, które indeksy nie są używane.
SUGESTIA # 4
Powinieneś zwiększyć rozmiar innodb_log_buffer_size do 64M, ponieważ domyślnie jest to 8M. Większy bufor dziennika może zwiększyć wydajność operacji We / Wy zapisu InnoDB.
EPILOG
Wstawiając pierwsze dwie sugestie, wykonaj następujące czynności:
party_id
indeksuByć może poniższe mogą pomóc
Zaimportuj dane do
monster
. Następnie uruchom toSPRÓBUJ !!!
ALTERNATYWNY
Możesz utworzyć tabelę o nazwie
monster_csv
jako tabela MyISAM bez indeksów i wykonaj następujące czynności:Zaimportuj swoje dane do
monster_csv
. Następnie użyj mysqldump, aby utworzyć kolejny importPlik mysqldump
data.sql
rozszerzy polecenia INSERT, importując jednocześnie 10 000-20 000 wierszy.Teraz wystarczy załadować mysqldump
Na koniec pozbądź się tabeli MyISAM
źródło
monster
tabelę) w mniej niż 20 minut, gdy nie mam kluczy w tabelach InnoDB. Dodanie kluczy zajęło ok. kolejne 20 min. Powiedziałbym, że to prawie rozwiązuje mój problem w tym przypadku. Dziękuję Ci bardzo!Chciałem napisać komentarz (ponieważ nie jest to ostateczna odpowiedź), ale stał się on zbyt długi:
Dam ci kilka obszernych porad i możemy przejść do szczegółów każdego z nich, jeśli chcesz:
Pamiętaj, że niektóre z nich nie są bezpieczne ani wskazane w przypadku braku importu (normalne działanie).
źródło
SET SESSION tx_isolation='READ-UNCOMMITTED';
(przydatne tylko w przypadku importowania z kilkoma wątkami równolegle) i komentarza @ypercube na temat wstawiania w partie. Masz tutaj pełny przykład: mysqlperformanceblog.com/2008/07/03/... Upewnij się, że korzystasz ze wszystkich funkcji w najnowszych wersjach InnoDB: mysqlperformanceblog.com/2011/01/07/…Jak dotąd podano większość dobrych wskazówek, ale bez wielu wyjaśnień dotyczących najlepszych. Dam więcej szczegółów.
Po pierwsze, opóźnianie tworzenia indeksu jest dobre, z wystarczającą ilością szczegółów w innych odpowiedziach. Nie wrócę na to.
Większy plik dziennika InnoDB bardzo ci pomoże (jeśli używasz MySQL 5.6, ponieważ nie można go zwiększyć w MySQL 5.5). Wstawiasz 7 GB danych, zaleciłbym całkowity rozmiar dziennika wynoszący co najmniej 8 GB (zachowaj
innodb_log_files_in_group
domyślną wartość (2) i wybijajinnodb_log_file_size
4 GB). To 8 GB nie jest dokładne: powinien mieć co najmniej rozmiar importu w dzienniku REDO i prawdopodobnie dwukrotnie lub czterokrotnie ten rozmiar. Rozumowanie za wielkością dziennika InnoDB zwiększa to, że gdy dziennik będzie prawie pełny, InnoDB zacznie agresywnie opróżniać swoją pulę buforów na dysk, aby uniknąć zapełniania się dziennika (gdy dziennik jest pełny, InnoDB nie może wykonać zapisu w bazie danych, dopóki niektóre strony puli buforów są zapisywane na dysku).Pomoże Ci większy plik dziennika InnoDB, ale powinieneś również wstawić w kolejności klucza podstawowego (posortuj plik przed wstawieniem). Jeśli wstawisz w kolejności klucza podstawowego, InnoDB zapełni jedną stronę, a następnie następną i tak dalej. Jeśli nie wstawisz w kolejności klucza podstawowego, następna wstawka może skończyć się na stronie, która jest pełna i spowoduje „podział strony”. Podział strony będzie kosztowny dla InnoDB i spowolni import.
Masz już pulę buforów tak dużą, jak pozwala na to pamięć RAM, a jeśli twój stół nie mieści się w niej, niewiele możesz zrobić, oprócz kupowania większej ilości pamięci RAM. Ale jeśli tabela mieści się w puli buforów, ale jest większa niż 75% puli buforów, możesz spróbować zwiększyć
innodb_max_dirty_pages_pct
do 85 lub 95 podczas importu (wartość domyślna to 75). Ten parametr konfiguracyjny mówi InnoDB, aby zaczął agresywnie opróżniać pulę buforów, gdy procent brudnych stron osiągnie ten limit. Podbijając ten parametr (i jeśli masz szczęście, jeśli chodzi o rozmiar danych), możesz uniknąć agresywnego We / Wy podczas importu i opóźnić te We / Wy później.Może (i to jest przypuszczenie) importowanie danych w wielu małych transakcjach pomoże ci. Nie wiem dokładnie, jak budowany jest dziennik REDO, ale jeśli jest on buforowany w pamięci RAM (i na dysku, gdy potrzebna będzie zbyt duża ilość pamięci RAM), gdy transakcja postępuje, może dojść do niepotrzebnych operacji we / wy. Możesz spróbować: po posortowaniu pliku podziel go na wiele części (spróbuj z 16 MB i innymi rozmiarami) i zaimportuj je jeden po drugim. Pozwoliłoby to również kontrolować postęp importu. Jeśli nie chcesz, aby Twoje dane były częściowo widoczne dla innego czytnika podczas importowania, możesz zaimportować przy użyciu innej nazwy tabeli, później utworzyć indeksy, a następnie zmienić nazwę tabeli.
O twoim hybrydowym dysku SSD / 5400RPM nie wiem o nich i jak to zoptymalizować. 5400 RPM wygląda wolno dla bazy danych, ale być może SSD tego unika. Być może wypełniasz część dysku SSD dyskiem sekwencyjnymi zapisami w dzienniku REDO, a dysk SSD szkodzi wydajności. Nie wiem.
Złe wskazówki, których nie powinieneś wypróbowywać (bądź ostrożny) to: nie używaj wielowątkowości: bardzo trudno będzie zoptymalizować, aby uniknąć podziału strony w InnoDB. Jeśli chcesz korzystać z wielu wątków, wstaw w różnych tabelach (lub w różnych partycjach tej samej tabeli).
Jeśli zastanawiasz się nad wieloma wątkami, być może masz komputer z wieloma gniazdami (NUMA). W takim przypadku unikaj problemu szaleństwa wymiany MySQL .
Jeśli używasz MySQL 5.5, uaktualnij do MySQL 5.6: ma opcję zwiększenia rozmiaru dziennika REDO i ma lepsze algorytmy opróżniania puli buforów.
Powodzenia w imporcie.
źródło