Jak przekonwertować 66 862 521 tabel wierszy z MyISAM do InnoDB bez przechodzenia w tryb offline przez kilka godzin?

18

czy jest możliwe (i jak) przekonwertować ogromną tabelę MyISAM na InnoDB bez przełączania aplikacji w tryb offline. Wymaga wstawienia kilku wierszy do tej tabeli co sekundę, ale można ją zawiesić na około 2 minuty.

Oczywiście ALTER TABLE ... silnik = innodb nie będzie działać. Dlatego planowałem utworzyć nową tabelę z silnikiem innodb i skopiować do niej zawartość. Na koniec zawieś wątek dziennika aplikacji i ZMIEŃ NAZWĘ TABELI.

Niestety nawet kopiowanie w małych partiach po 100 wierszy generuje znaczne opóźnienie po pewnym czasie.

Edycja : Istniejące wiersze nigdy się nie zmieniają, ta tabela służy do rejestrowania.

Hendrik Brummermann
źródło
3
To pytanie dotyczy zminimalizowania czasu rozmowy. Nie obchodzi mnie, czy rozmowa potrwa kilka dni, czy tygodni. Ale musi działać w tle, nie wymagając przestoju aplikacji i bez powodowania zauważalnego opóźnienia.
Hendrik Brummermann

Odpowiedzi:

15

Utwórz konfigurację Master-Master w następujący sposób:

  • Utwórz drugiego wzorca, MasterB
  • MasterB działa jako niewolnik logTable
  • Utwórz logTable_newjako innodb
  • Uruchom INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) na MasterB, który wysyła replikację do MasterA
  • Kiedy logTable_newna MasterA zakończy synchronizację, zamień tabele
Derek Downey
źródło
10

Biorąc pod uwagę ograniczenie:

Nie obchodzi mnie, czy rozmowa potrwa kilka dni, czy tygodni. Ale musi działać w tle, nie wymagając przestoju aplikacji i bez powodowania zauważalnego opóźnienia

Podczas rejestrowania, jeśli masz dobry sposób, aby ustawić znacznik, abyś mógł powiedzieć, od czego zaczynasz proces, abyś mógł ponownie zastosować wszystkie dzienniki lub zapisać dzienniki w pliku tekstowym, aby możesz później je spożywać LOAD DATA INFILE

Część problemu polega na tym, że pisanie mniejszymi partiami oznacza, że ​​indeksy muszą być przeliczane w kółko; lepiej jest uruchomić to wszystko naraz, ale może to powodować pewne „zauważalne” opóźnienie w systemie .. ale nie musisz tego robić na serwerze produkcyjnym.

  1. Zatrzymaj rejestrowanie lub ustaw znacznik, aby później ponownie zastosować dzienniki.
  2. Skopiuj tabelę MyISM do innego systemu
  3. W drugim systemie utwórz tabelę InnoDB pod inną nazwą i dokonaj migracji danych (może być nawet szybszy zrzut i użycie LOAD DATA INFILE)
  4. Skopiuj tabelę InnoDB z powrotem do oryginalnego systemu
  5. Ustaw kolejny znacznik rejestrowania.
  6. Ponownie zastosuj wszystkie dzienniki do nowej tabeli pomiędzy dwoma ostatnimi znacznikami.
  7. (powtórz kroki 5 i 6, jeśli krok nr 6 trwał dłużej niż minutę, dopóki nie upłynie kilka sekund)
  8. Zamień tabele (zmień nazwę starego na Table_BACKUP, nowy pod nazwą starego)
  9. Łap dzienniki od ostatniego znacznika.
Joe
źródło
9

Niestety nawet kopiowanie w małych partiach po 100 wierszy generuje znaczne opóźnienie po pewnym czasie.

Czy dodajesz jakieś opóźnienie między każdą partią, czy po prostu dodajesz aktualizacje i uruchamiasz każdą partię bezpośrednio po poprzedniej?

Jeśli tak, spróbuj skryptować konwersję w swoim ulubionym języku za pomocą czegoś takiego:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

Powinno to zapewnić, że konwersja nie zajmie więcej niż mniej więcej połowy pojemności twojego serwera, nawet uwzględniając różnice w obciążeniu nałożone, ponieważ użycie systemu zmienia się z czasem.

Lub jeśli chcesz używać tyle czasu, jak to możliwe, gdy usługa jest stosunkowo bezczynny, ale wycofać (potencjalnie zatrzymując się na dość długi czas), kiedy potrzeby baz popracować dla jego użytkowników, wymienić sleep for as long as the update tooksię if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Będzie to oznaczać, że może spokojnie płynąć naprzód, ale całkowicie zatrzyma się, gdy serwer jest zajęty wykonywaniem normalnego obciążenia. Określenie obciążenia będzie zależeć od systemu operacyjnego - w Linuksie i podobnej wartości 1-minutowa średnia wartość obciążenia /proc/loadavglub wynik uptimepowinien zrobić. <lower measure>i <upper measure>może mieć tę samą wartość, chociaż zwykle w takich kontrolkach występuje różnica, więc proces nie rozpoczyna się od razu, a następnie natychmiast zatrzymuje się z powodu własnego restartu mającego wpływ na miarę obciążenia.

Oczywiście nie działałoby to w przypadku tabel, w których stare wiersze mogą zostać zmodyfikowane, ale działałyby dobrze w przypadku tabeli dziennika takiej jak ta, którą opisujesz.

W tym przypadku będziesz chciał zignorować zwykłą mądrość tworzenia indeksów po zapełnieniu nowej tabeli. Chociaż jest to rzeczywiście bardziej wydajne, jeśli chcesz, aby rzeczy były tak szybkie, jak to możliwe (wpływ na resztę systemu niech będzie przeklęty), w tym przypadku nie chcesz dużego nadmiaru obciążenia na końcu procesu, ponieważ indeksy są całkowicie tworzone za jednym razem, ponieważ jest to proces, którego nie można wstrzymać, gdy wszystko jest zajęte.

David Spillett
źródło
4

Czy coś takiego mogłoby działać?

  1. Wstrzymaj rejestrowanie (aby $auto_incrementtabela mytable zmian się nie zmieniła).
  2. Zanotuj $auto_incrementwartość za pomocą SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Włącz ponownie rejestrowanie. Tabela Innodb zacznie się teraz zapełniać.
  7. INSERT INTO mytable SELECT * FROM mytable_old.

Możesz wykonać krok 7 w partiach lub w jednej instrukcji, ponieważ nie powinno to blokować normalnego logowania.

Riedsio
źródło
nadal byłby blokowany z powodu sposobu, w jaki innodb obsługuje auto_increment. domyślnie innodb przyjmuje blokadę na poziomie tabeli podczas wstawiania do kolumny auto_increment i zwalnia blokadę po zakończeniu wstawiania.
ovais.tariq