Utwórz indeks na ogromnej tabeli produkcyjnej MySQL bez blokowania tabeli

104

Muszę utworzyć indeks na ~ 5M wierszy tabeli MySQL. To jest stół produkcyjny i obawiam się, że będzie kompletny blok wszystkiego, jeśli uruchomię instrukcję CREATE INDEX ...

Czy istnieje sposób na utworzenie tego indeksu bez blokowania wstawień i selekcji?

Zastanawiam się tylko, czy nie muszę przerywać, tworzyć indeksu i restartować systemu!

n0cturnal
źródło
1
upewnij się, że Twoje myisam_sort_buffer_size i myisam_max_sort_file_size są wystarczająco duże.
Jon Black

Odpowiedzi:

130

Aktualizacja [2017]: MySQL 5.6 obsługuje aktualizacje indeksów online

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

W MySQL 5.6 i nowszych, tabela pozostaje dostępna dla operacji odczytu i zapisu podczas tworzenia lub usuwania indeksu. Instrukcja CREATE INDEX lub DROP INDEX kończy się dopiero po zakończeniu wszystkich transakcji, które uzyskują dostęp do tabeli, tak aby początkowy stan indeksu odzwierciedlał najnowszą zawartość tabeli. Wcześniej modyfikowanie tabeli podczas tworzenia lub usuwania indeksu powodowało zwykle zakleszczenie, które powodowało anulowanie instrukcji INSERT, UPDATE lub DELETE w tabeli.

[2015] Aktualizacja tabeli wskazuje na bloki zapisów w MySQL 5.5

Z powyższej odpowiedzi:

„Jeśli używasz wersji wyższej niż 5.1, indeksy są tworzone, gdy baza danych jest online. Nie martw się, nie przerywasz korzystania z systemu produkcyjnego”.

To jest **** FALSE **** (przynajmniej dla tabel MyISAM / InnoDB, których używa 99,999% ludzi. Wersja Clustered jest inna).

Wykonywanie operacji UPDATE na tabeli spowoduje ZABLOKOWANIE podczas tworzenia indeksu. MySQL jest naprawdę głupi w tym (i kilku innych rzeczach).

Skrypt testowy:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Mój serwer (InnoDB):

Server version: 5.5.25a Source distribution

Dane wyjściowe (zwróć uwagę, jak szóste bloki operacji przez ~ 400 ms potrzebne do zakończenia aktualizacji indeksu):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

W porównaniu z operacjami odczytu, które nie blokują (zamień komentarz linii w skrypcie):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Aktualizacja schematu MySQL bez przestojów

Tak więc, jest tylko jedna metoda, którą znam, aby zaktualizować schemat MySql i uniknąć przerwy w dostępności. Mistrzowie cyrkularni:

  • Master A ma uruchomioną bazę danych MySQL
  • Doprowadzić Master B do użytku i powtórzyć zapisy z Mastera A (B jest niewolnikiem A)
  • Wykonaj aktualizację schematu w Master B. Zostanie ona opóźniona podczas aktualizacji
  • Niech mistrz B dogoni. Niezmienny: zmiana schematu MUSI umożliwiać przetwarzanie poleceń replikowanych ze schematu obniżonej wersji. Zmiany indeksowania kwalifikują się. Zwykle kwalifikują się proste dodatki do kolumn. Usunąć kolumnę? prawdopodobnie nie.
  • ATOMICZNIE zamień wszystkich klientów z Master A na Master B. Jeśli chcesz być bezpieczny (zaufaj mi, masz), powinieneś upewnić się, że ostatni zapis do A zostanie zreplikowany do B PRZEDB dokonuje pierwszego zapisu. Jeśli zezwolisz na równoczesne zapisy do 2+ masterów, ... lepiej zrozumiesz replikację MySQL na poziomie DEEP lub udajesz się w świat bólu. Ekstremalny ból. Na przykład, czy masz kolumnę o nazwie AUTOINCREMENT ??? masz przerąbane (chyba że używasz parzystych liczb na jednym mistrzu, a kursów na drugim). NIE ufaj replikacji MySQL, że „postępuje właściwie”. To NIE jest mądre i nie uratuje cię. Jest to tylko trochę mniej bezpieczne niż kopiowanie binarnych dzienników transakcji z wiersza poleceń i ponowne ich ręczne odtwarzanie. Mimo to odłączenie wszystkich klientów od starego serwera głównego i przełączenie ich na nowego głównego można wykonać w ciągu kilku sekund, znacznie szybciej niż czekanie na wielogodzinną aktualizację schematu.
  • Teraz Mistrz B jest twoim nowym mistrzem. Masz nowy schemat. Życie jest dobre. Mieć piwo; najgorsze minęło.
  • Powtórz ten proces z mistrzem A, aktualizując jego schemat, aby stał się twoim nowym drugim mistrzem, gotowym do przejęcia władzy w przypadku, gdy twój główny mistrz (teraz mistrz B) utraci moc lub po prostu podniesie się i umrze wraz z tobą.

Prosty sposób na aktualizację schematu to nie jest. Wykonalne w poważnym środowisku produkcyjnym; Tak to jest. Proszę, proszę, proszę, jeśli istnieje łatwiejszy sposób na dodanie indeksu do tabeli MySQL bez blokowania zapisów, daj mi znać.

Googlowanie doprowadziło mnie do tego artykułu, w którym opisano podobną technikę. Co więcej, radzą pić w tym samym momencie procedury (zwróć uwagę, że napisałem swoją odpowiedź przed przeczytaniem artykułu)!

Percona's pt-online-schema-change

Artykuł I połączone powyżej opowiada o narzędzia, pt-online-schemat-change , że działa w następujący sposób:

  • Utwórz nową tabelę z taką samą strukturą jak oryginał.
  • Zaktualizuj schemat w nowej tabeli.
  • Dodaj wyzwalacz do oryginalnej tabeli, aby zmiany były zsynchronizowane z kopią
  • Kopiuj wiersze partiami z oryginalnej tabeli.
  • Usuń oryginalny stół z drogi i zastąp go nowym.
  • Upuść stary stół.

Sam nigdy nie próbowałem tego narzędzia. YMMV

RDS

Obecnie używam MySQL za pośrednictwem RDS firmy Amazon . To naprawdę sprytna usługa, która zarządza MySQL i zarządza nią, umożliwiając dodawanie nowych replik do odczytu za pomocą jednego przycisku i przejrzystą aktualizację bazy danych w różnych jednostkach SKU sprzętu. To naprawdę wygodne. Nie uzyskujesz SUPER dostępu do bazy danych, więc nie możesz wkręcać w replikację bezpośrednio (czy to błogosławieństwo czy przekleństwo?). Możesz jednak skorzystać z promocji Read Replica, aby wprowadzić zmiany w schemacie na urządzeniu podrzędnym tylko do odczytu, a następnie wypromować go na nowego mistrza. Dokładnie ta sama sztuczka, którą opisałem powyżej, tylko znacznie łatwiejsza do wykonania. Nadal nie robią zbyt wiele, aby pomóc ci w cięciu. Musisz ponownie skonfigurować i ponownie uruchomić aplikację.

Dave Dopson
źródło
3
pt-online-schema-change działa świetnie nawet w replikacji typu master-slave. Użyłem go do przeprowadzenia migracji na żywo na zajętej tabeli 20 milionów + rekordów w naszej produkcyjnej głównej bazie danych z 2 replikacjami podrzędnymi bez żadnych problemów i przestojów. Przygotowanie skryptu zajmuje trochę czasu i zwykle muszę utworzyć plik .sql zawierający surową zmianę SQL i plik .sh jako opakowanie, aby uruchomić ten sam SQL, ale w formacie fragmentu (bez ALTER TABLE). Możesz uruchomić wiele poleceń za pomocą pt-online-schema-change, łącząc je i oddzielając przecinkami.
Alex Le
-1; Nie wiem o starszych wersjach, ale wiem, że tworzenie indeksu nie blokuje współbieżnego DML w MySQL 5.6+ (dla którego istniał RC w momencie pisania tej odpowiedzi i który został oficjalnie wydany, gdy ta odpowiedź była trwała edytowane w maju 2013), ponieważ polegałem na tym, aby uruchamiać wielogodzinne tworzenie indeksów na tabelach produkcyjnych, jednocześnie akceptując wstawki. A gdy może mieć rację tworzenie indeksu blokowania DML w 5,5 i poniżej, opóźnienie sub-sekundowy wykazać tutaj jest nie do końca przekonujące.
Mark Amery,
@MarkAmery - zachowanie blokujące to zachowanie blokujące, a 400 ms to wieczność. Bloki MySQL 5.5 do aktualizacji indeksów. Zbuduj większą testową bazę danych, która będzie blokowana na sekundy, godziny lub dni. Napisałem ten post zanim MySQL 5.6 miał aktualizacje schematu online, więc moja oryginalna treść nie odzwierciedla tego faktu. Zaktualizowałem post, aby odzwierciedlić nowo dostępne informacje.
Dave Dopson
@DaveDopson, czy jesteś w 100% pewien, że blokowane są tylko operacje UPDATE?
toto_tico
Tak było w przypadku wersji, którą testowałem.
Dave Dopson
67

Jak pokazuje ten wpis na blogu , InnoDBALTER TABLE mechanizm został całkowicie przeprojektowany dla MySQL 5.6.

(Aby uzyskać ekskluzywny przegląd tego tematu, dokumentacja MySQL może zapewnić popołudniową lekturę).

Aby dodać indeks do tabeli bez blokady wynikającej z UPDATE/ INSERT, można użyć następującego formatu instrukcji:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;
Rysował
źródło
4
Ostrzeżenie
Alexander Torstling
16

Aktualizacja MySQL 5.6 (luty 2013): Możesz teraz wykonywać operacje odczytu i zapisu podczas tworzenia indeksu, nawet z tabelami InnoDB - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

W MySQL 5.6 i nowszych, tabela pozostaje dostępna dla operacji odczytu i zapisu podczas tworzenia lub usuwania indeksu. Instrukcja CREATE INDEX lub DROP INDEX kończy się dopiero po zakończeniu wszystkich transakcji, które uzyskują dostęp do tabeli, tak aby początkowy stan indeksu odzwierciedlał najnowszą zawartość tabeli. Wcześniej modyfikowanie tabeli podczas tworzenia lub usuwania indeksu powodowało zwykle zakleszczenie, które powodowało anulowanie instrukcji INSERT, UPDATE lub DELETE w tabeli.

i:

W MySQL 5.6 ta funkcja staje się bardziej ogólna: możesz czytać i zapisywać w tabelach podczas tworzenia indeksu, a wiele innych rodzajów operacji ALTER TABLE można wykonywać bez kopiowania tabeli, bez blokowania operacji DML lub obu. W związku z tym w MySQL 5.6 i nowszych wersjach zwykle nazywamy ten zestaw funkcji DDL online, a nie szybkim tworzeniem indeksu.

z http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

Eric Saboia
źródło
Więc jak można wyjaśnić analizę Dave'a?
Nikhil Sahu
1
@NikhilSahu Dave najwyraźniej nie testował na MySQL 5.6, ale na jakiejś starszej wersji. Zwróć uwagę, że 5.6 nie został jeszcze wydany w momencie, gdy Dave opublikował wstępną wersję swojej odpowiedzi.
Mark Amery,
+1. Moja analiza dotyczyła MySQL 5.5 (najnowsza dostępna w 2013). Aktualizuję odpowiedź, aby odzwierciedlić nowe możliwości MySQL 5.6.
Dave Dopson
3

pt-online-schema-change jest dobrym rozwiązaniem, jeśli naprawdę chcesz się upewnić, że migracja nie spowoduje wyłączenia witryny.

Jak napisałem w powyższym komentarzu, mam kilka doświadczeń z pt-online-schema-change w produkcji. Mamy naszą główną tabelę zawierającą ponad 20 milionów rekordów i główną -> 2 niewolników replikacji tylko do odczytu. Zrobiłem co najmniej kilkadziesiąt migracji z pt-online-schema-change od dodania nowej kolumny, zmiany zestawu znaków, do dodania kilku indeksów. Obsługujemy tony ruchu również w czasie migracji i nie mieliśmy żadnych problemów. Oczywiście przed uruchomieniem w środowisku produkcyjnym musiałbyś bardzo dokładnie przetestować wszystkie skrypty.

Próbowałem podzielić zmiany w jednym skrypcie, aby zmiana schematu pt-online musiała tylko raz skopiować dane. Zachowaj ostrożność przy zmianie nazwy kolumny, ponieważ utracisz swoje dane. Jednak dodanie indeksu powinno wystarczyć.

Alex Le
źródło
Nie zgadzam się z twoją bez zastrzeżeń rekomendacją pt-online-schema-change. Jest świetny, ale jest przesadą w wielu sytuacjach, w których funkcje DDL online MySQL 5.6 + już działają dobrze. Ma również ograniczenia (takie jak brak dobrej zabawy z wyzwalaczami) i podwaja ilość zapisów potrzebnych na wstawienie do oryginalnej tabeli, gdy trwa zmiana schematu. Obciąży to dysk znacznie bardziej niż zwykła zmiana schematu online, a więc może „obniżyć poziom witryny” w okolicznościach, w których samo uruchomienie schematu w prosty sposób działałoby dobrze.
Mark Amery,
Napisałem w oparciu o moje rzeczywiste doświadczenia ze zmianą schematu pt-online w tamtym czasie, więc nie jestem pewien, dlaczego nazwałbyś moją rekomendację „bez zastrzeżeń”. Mieliśmy co najmniej 1000+ odwiedzających witrynę w dowolnym momencie, kiedy wprowadzałem zmiany w schemacie, i oczywiście IO dysku było opodatkowane, ale nasza witryna nie uległa awarii. Dobre buforowanie również pomogło. Nie korzystałem z MySQL 5.6+ online DDL, ale z mojego doświadczenia wynika, że ​​pt-online-schema-change dobrze się spisał w naszym przypadku.
Alex Le
1
@AlexYe Yikes, miałem na myśli „bez zastrzeżeń” w sensie „bez zastrzeżeń”, a nie w sensie „dostarczony przez kogoś, kto nie ma uprawnień do komentowania” - ta druga interpretacja nie przyszła mi do głowy, dopóki nie zobaczyłem twojego komentarza i na pewno nie to co chciałem! tj. mówiłem, że chociaż pt-online-schema-changejest to przydatne narzędzie, jest bardzo wiele sytuacji, w których zwykłe internetowe DDL jest równie dobre i kilka, w których jest lepsze, więc wszelkie zalecenia dotyczące tego powinny być ostrożnie zastrzegane, a nie uniwersalne.
Mark Amery,