Jak dodać kolumnę do dużej tabeli w MySQL

13

Jestem programistą PHP, więc nie bądź surowy. Mam zrzut dużego stołu ~ 5,5 GB. Nasz premier postanowił utworzyć w nim nową kolumnę, aby wykonać nową funkcję. Tabela to InnoDB, więc co próbowałem:

  1. Zmień stół na ekranie z blokadą stołu. Zajęło ~ 30 godzin i nic. Więc po prostu to powstrzymałem. Najpierw popełniłem błąd, ponieważ nie zakończyłem wszystkich transakcji, ale po raz drugi nie było multilocka. Status był copy to tmp table.

  2. Ponieważ muszę także zastosować partycjonowanie dla tej tabeli, postanowiliśmy zrobić zrzut, zmienić nazwę i utworzyć tabelę o tej samej nazwie i nowej strukturze. Ale zrzut robi ścisłą kopię (przynajmniej nie znalazłem czegoś innego). Dodałem więc, aby zrzucić nową kolumnę sedi wysłać zapytanie. Ale zaczęły się dziwne błędy. Uważam, że było to spowodowane charsetem. Tabela w utf-8 i plik stał się potem us-ascii sed. Mam więc błędy (nieznane polecenie „\”) na 30% danych. To także zły sposób.

Jakie są inne opcje, aby to osiągnąć i zwiększyć wydajność (mogę to zrobić za pomocą skryptu php, ale zajmie to wieki). Jaka będzie wydajność INSERT SELECTw tym przypadku.

Dzięki za wszelkie zaliczki.

ineersa
źródło

Odpowiedzi:

12

Użyj MySQL Workbench . Możesz kliknąć tabelę prawym przyciskiem myszy i wybrać „Wyślij do edytora SQL” -> „Utwórz instrukcję”. W ten sposób żadne „właściwości” tabeli nie zostaną zapomniane o dodaniu (w tym CHARSETlub COLLATE).
Przy tak dużej ilości danych zaleciłbym oczyszczenie tabeli lub struktury danych, której używasz (przydatna jest dobra DBA). Jeśli nie jest to możliwe:

  • zmień nazwę table ( ALTER) i utwórz nowy za pomocą CREATEskryptu otrzymanego z Workbench. Możesz również rozszerzyć to zapytanie o nowe pole, którego potrzebujesz
  • BULK LOAD dane ze starej tabeli do nowej:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;
    

    W ten sposób unikniesz indeksowania / etc, aby uruchomić rekord po rekordzie. „Aktualizacja” tabeli nadal będzie powolna (ponieważ ilość danych jest ogromna), ale jest to najszybszy sposób, jaki mogę wymyślić.

    EDYCJA: Przeczytaj ten artykuł, aby uzyskać szczegółowe informacje na temat poleceń używanych w powyższym przykładowym zapytaniu;)

źródło
Moje opcje są w porządku. I dostałem SET NAMES utf8i. COLLATIONAle me idk, dlaczego 30% danych uległo później uszkodzeniu sed. Myślę, że ładowanie zbiorcze będzie najszybsze, ale może istnieje coś więcej, czego mi brakuje. Dziękuję Mark
ineersa
1
Uszkodzenie danych @ineersa może mieć wiele przyczyn: np. otworzyłeś plik za pomocą edytora, który nie obsługuje wszystkich znaków i zapisałeś go. Lub sposób, w jaki próbujesz zaimportować ze zrzutu, powoduje uszkodzenie danych (jest wadliwy i nie może poprawnie odczytać pliku). Lub ten sam facet może zidentyfikować część niektórych danych jako wyrażenie (np. „James \ robin” == "\ r” jako wyrażenie) lub polecenie itp. Dlatego nigdy nie zalecam używania zrzutu, nawet w przypadku narzędzia zrzutu danych binarnych tylko, nawet z dev.mysql.com/doc/refman/5.6/en/mysqldump.html (lub BCP dla MS SQL Server). Zbyt wiele razy się nie udaje ...
tak, próbowałem z hex-blob. to nie pomaga. Również ty po użyciu sed mysql identyfikuj \ 'jako polecenie w niektórych nazwach (nie we wszystkich). To dziwne i błędne. Spróbuję dziś wieczorem załadować masowo. Mam nadzieję, że zostanie to zrobione co najmniej za 10-15 godzin.
ineersa
@ineersa mam nadzieję, że tak będzie. możesz także spróbować dodać tylko część danych, powiedzmy 10%, aby zobaczyć, ile czasu to zajmuje - i oszacuj całą transakcję. Będzie to bardzo przybliżone oszacowanie, wszystko może się zwolnić, jeśli pamięci podręczne / pamięć / cokolwiek zostanie zapełnione / przeładowane.
1
Dzięki Mark. Działa niesamowicie. Jeszcze szybciej niż przywróć ze zrzutu. Zajęło to ~ 5 godzin.
ineersa
5

Twój pomysł sed jest przyzwoitą metodą, ale bez błędów lub polecenia, które wykonałeś, nie możemy ci pomóc.

Jednak dobrze znaną metodą wprowadzania zmian online do dużych tabel jest zmiana schematu pt-online . Uproszczone przeoczenie tego, co robi to narzędzie, zostało skopiowane z dokumentacji:

pt-online-schemat-zmiana-zmiany polega na utworzeniu pustej kopii tabeli do zmiany, zmodyfikowaniu jej zgodnie z potrzebami, a następnie skopiowaniu wierszy z oryginalnej tabeli do nowej tabeli. Po zakończeniu kopiowania odsuwa on oryginalną tabelę i zastępuje ją nową. Domyślnie upuszcza również oryginalny stół.

Ta metoda może również zająć trochę czasu, ale podczas procesu oryginalna tabela będzie w pełni użyteczna.

Derek Downey
źródło
Spróbuję załadować masowo później wieczorem. Jeśli to nie zadziała, prawdopodobnie będzie potrzebne to narzędzie. Błędy są spowodowane brakiem niektórych symboli po użyciu polecenia sed jako polecenia. Na przykład 'D\'agostini'spowoduje błąd unknown command '\''. Ale nie zawsze, jak w 30% przypadków. To dziwne i błędne. To samo dotyczy zrzutów hex-blob. Dziękuję Derek.
ineersa
4

alter table add column, algorithm=inplace, lock=none zmieni tabelę MySQL 5.6 bez kopiowania tabeli i bez blokowania.

Właśnie przetestowałem to wczoraj, masowo wstawiono 70 000 wierszy do tabeli podziału 7 000 280 wierszy, 10 000 wierszy dla każdej partycji, z 5-sekundowym uśpieniem między nimi, aby umożliwić inną przepustowość.

Rozpoczęto wstawianie mas, a następnie w osobnej sesji rozpoczęto alterpowyższą instrukcję online w MySQL Workbench, alterzakończono przed wstawkami, dodano dwie nowe kolumny i żadne wiersze nie wynikały ze zmiany, co oznacza, że ​​MySQL nie kopiował żadnych wierszy.

SAK
źródło
1
Dlaczego ta odpowiedź nie ma więcej głosów? Czy nie działa?
fguillen,
1

Obecnie najlepszą opcją zmiany ogromnych tabel jest prawdopodobnie https://github.com/github/gh-ost

gh-ost to bezproblemowe rozwiązanie migracji schematu online dla MySQL. Jest testowalny i zapewnia łatwą obsługę, dynamiczną kontrolę / rekonfigurację, audyt i wiele dodatkowych korzyści operacyjnych.

Program gh-ost wytwarza niewielkie obciążenie na systemie głównym podczas migracji, oddzielone od istniejącego obciążenia na migrowanej tabeli.

Został zaprojektowany w oparciu o lata doświadczeń z istniejącymi rozwiązaniami i zmienia paradygmat migracji tabel.

iJanki
źródło
1

Myślę, że Mydumper / Myloader jest dobrym narzędziem do takich operacji: Z każdym dniem jest coraz lepiej. Możesz korzystać z procesorów i ładować dane równolegle: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- funkcje użyteczności /

Udało mi się załadować setki gigabajtów tabel MySQL w ciągu kilku godzin.

Teraz, jeśli chodzi o dodawanie nowej kolumny, jest trudne, ponieważ MySQL kopiuje całą tabelę do TMPobszaru pamięci. ALTER TABLE...Chociaż MySQL 5.6 mówi, że może dokonywać zmian w schemacie online, nie udało mi się tego zrobić online dla ogromnych tabel bez blokady jak dotąd spór.

Kubilay
źródło
-2

właśnie miałem ten sam problem. Trochę obejścia:

UTWÓRZ TABELĘ new_table WYBIERZ * z oldtable;

USUŃ Z nowej tabeli

ALTER TABLE new_table DODAJ KOLUMNĘ new_column int (11);

INSERT INTO new_table wybierz *, 0 ze old_table

upuść tabelę old_table; zmień nazwę tabeli new_table TO old_table;

AirCoder
źródło
Dlaczego po prostu nie dodać klauzuli where do instrukcji create table, aby nie wybierała żadnych danych? Również obcięcie tabeli byłoby bardziej wydajne niż usunięcie danych
Joe W
dlaczego usunąć, kiedy trzeba wstawić później, jeszcze raz. Można zdefiniować wartość domyślną = 0 w samym DODAJ KOLUMNĘ.
user195280,