Niepowodzenie tworzenia indeksu MySQL w tabeli jest pełne

10

AKTUALIZACJA: tl; dr: Problem polegał na tym, że MySQL używa TMPDIRpodczas tworzenia indeksów. A moje TMPDIRzabrakło miejsca na dysku.

Oryginalny Q:

Próbuję dodać indeks do tabeli InnoDB i uzyskać table is full error. Mam wystarczającą ilość miejsca na dysku, a konfiguracja MySQL ma plik na tabelę = 1. Dane w tabeli wynoszą 85 GB i zakładam, że indeks wyniesie około 20 GB - 30 GB i mam znacznie więcej miejsca na dysku niż to. Używam również ext3, więc nie sądzę, aby był problem z limitem rozmiaru pliku z punktu widzenia systemu operacyjnego.

Zalogowany błąd wygląda następująco:

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Co to powoduje i jak mogę to rozwiązać?

Utwórz tabelę:

`CREATE TABLE `my_table` (
 `uid_from` bigint(11) NOT NULL,
 `uid_to` bigint(11) NOT NULL,
 `counter` int(11) NOT NULL,
 `updated` date NOT NULL,
 PRIMARY KEY (`uid_to`,`uid_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8`

Ponieważ dane mają pojemność 87,4 GB i szacuję, że jest około 1,5B wierszy.

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Variable_name   Value
tmpdir  /tmp

[root@web ~]# df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   24G   14G  64% /
Noam
źródło
1
„InnoDB: sprawdź, czy Twój system operacyjny i system plików obsługują pliki tego rozmiaru. InnoDB: sprawdź także, czy dysk nie jest zapełniony lub czy limit miejsca został przekroczony.” - sprawdziłeś to? Czy używasz ext2? ext3? XFS? Sprawdziłeś kwoty dla użytkownika?
Philᵀᴹ
@Phil Sprawdziłem oba. Używam ext3 i jestem prawie pewien, że nie ma problemu z miejscem na dysku. Przeczucie, że ma to coś wspólnego z dziennikiem osiągającym limit lub coś takiego, ale tak naprawdę nie ma pojęcia, jak to sprawdzić i naprawić.
Noam
Zastanawiam się, co to jest plik „(scalanie)”?
akuzminsky
@akuzminsky hmm Nie mam pojęcia. Uruchomiłem tylko nowe tworzenie indeksu z PHPMyAdmin.
Noam
+1 dla TMPDIR, to był problem również na moim serwerze.
Chad E.

Odpowiedzi:

8

Nie daj się zwieść komunikatowi o błędzie The table 'my_table' is full. Ten rzadki scenariusz nie ma absolutnie nic wspólnego z przestrzenią dyskową. Ten pełny stan tabeli ma związek z wewnętrzną instalacją hydrauliczną InnoDB.

Najpierw spójrz na ten schemat architektury InnoDB

Architektura InnoDB

Należy pamiętać, że systemowy obszar tabel (plik ibdata) ma tylko 128 segmentów wycofania i 1023 miejsc wycofania na segment wycofania. Ogranicza to wielkość zdolności wycofywania transakcji. Innymi słowy, jeśli pojedynczy segment wycofania potrzebuje więcej niż 1023 miejsc do obsługi transakcji, transakcja spełni ten table is fullwarunek.

Pomyśl o restauracji Red Lobster w New Jersey . Może mieć pojemność 200 osób. Jeśli restauracja jest pełna, kolejka ludzi może wyjść na zewnątrz i czekać. Jeśli ludzie na linii zniecierpliwią się, mogą wyjść, ponieważ restauracja jest pełna. Oczywiście rozwiązaniem nie byłoby powiększenie New Jersey (lub zwiększenie przestrzeni dyskowej). Rozwiązaniem byłoby powiększenie restauracji Red Lobster. W ten sposób możesz zwiększyć pojemność siedzeń, powiedzmy, do 240. Mimo to linia może utworzyć się na zewnątrz, jeśli ponad 240 osób zdecyduje się przyjechać do Red Lobster.

Aby dać przykład, miałem klienta z 2 TB systemowego obszaru tabel i wyłączono plik innodb_file_per_table . (346G dla ibdata1, reset dla ibdata2). Uruchomiłem to zapytanie

SELECT SUM(data_length+index+length) InnoDBDataIndexSpace
FROM information_schema.tables WHERE engine='InnoDB';

Następnie odejmowałem InnoDBDataIndexSpace od sumy rozmiarów plików dla ibdata1 i ibdata2. Mam dwie rzeczy, które mnie zszokowały

  • W systemowym obszarze tabel pozostało 106 GB.
  • Mam ten sam Table is Fullwarunek

Oznacza to, że 106 GB używało wewnętrznej instalacji hydraulicznej InnoDB. Klient korzystał wówczas z ext3.

Rozwiązaniem było dla mnie dodanie ibdata3. Omówiłem to w moim starym poście Jak rozwiązać „Tabela ... jest pełna” za pomocą „innodb_file_per_table”?

Omówiłem to również w innych postach

Pamiętaj, że ten warunek może się zdarzyć, nawet jeśli włączono opcję innodb_file_per_table . W jaki sposób? Cofnięcia i dzienniki cofania są źródłem niekontrolowanych skoków wzrostu dla ibdata1 .

TWOJA RZECZYWISTA PYTANIE

Ponieważ dodajesz indeks do tabeli i dostajesz Table is Full, tabela musi być ogromna i nie może zmieścić się w jednym segmencie wycofywania. Musisz wykonać następujące czynności:

KROK 01

Pobierz dane do pliku zrzutu

mysqldump --no-create-info mydb mytable > table_data.sql

KROK 02

Zaloguj się do MySQL i uruchom to

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

KROK 03

Załaduj tabelę z dodatkowym indeksem z danymi

mysql -Dmydb < table_data.sql

To wszystko.

Widzisz, problem polega na tym, że ALTER TABLE spróbuje wstrzyknąć wszystkie wiersze w twojej ogromnej tabeli jako pojedynczą transakcję. Użycie mysqldump spowoduje wstawienie danych do tabeli (teraz z nowym indeksem) tysiące wierszy na raz, nie wszystkie wiersze w pojedynczej transakcji.

Nie martw się o what if this doesn't work?Oryginalna tabela zostanie nazwana mytable_oldw przypadku czegokolwiek. może służyć jako kopia zapasowa. Możesz usunąć kopię zapasową, gdy wiesz, że nowy stół działa dla Ciebie.

Spróbuj !!!

AKTUALIZACJA 16.06.2014, 11:13 EDT

Jeśli martwisz się o większy zrzut danych, po prostu zgzipuj.

Możesz wykonać te same kroki, ale w następujący sposób

KROK 01

Pobierz dane do pliku zrzutu

mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz

KROK 02

Zaloguj się do MySQL i uruchom to

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

KROK 03

Załaduj tabelę z dodatkowym indeksem z danymi

gzip -d < table_data.sql.gz | mysql -Dmydb

lub

gunzip < table_data.sql.gz | mysql -Dmydb

AKTUALIZACJA 16.06.2014, 12:55 EDT

Właśnie pomyślałem o innym aspekcie dotyczącym tej kwestii.

Ponieważ wykonujesz DDL, a nie DML, możliwe, że nie jest to wewnętrzna instalacja hydrauliczna InnoDB. Ponieważ DDL nie może przywrócić programu InnoDB , problemem musi być zewnętrzna instalacja hydrauliczna. Gdzie zatkana jest ta zewnętrzna hydraulika? Podejrzewam, że folder temp dla systemu operacyjnego. Dlaczego?

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Widzisz disk quota exceeded? Gdzie jest narzucany ten przydział dysku?

Uruchom to zapytanie

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Powiedziałeś, że to /var/lib/mysqltmp

Teraz uruchom to w systemie operacyjnym

df -h /var/lib/mysqltmp

Coś mi mówi, że /var/lib/mysqltmpzabrakło miejsca W końcu masz tylko 14G za darmo. W oczach DDL (w celu utworzenia indeksu) wycofanie nastąpiło nie w pliku ibdata1, ale w tmpdirlokalizacji. Jeśli /var/lib/mysqltmpnigdzie nie jest zamontowany, dane tymczasowe są zapisywane na partycji głównej. Jeśli /var/lib/mysqltmpgdzieś jest zamontowany, to to mocowanie jest wypełniane danymi wiersza. W obu przypadkach nie ma wystarczającej ilości miejsca na uzupełnienie DDL.

Masz tutaj dwie opcje

OPCJA 1

Możesz także utworzyć duży dysk (być może ze 100 + GB) i zamontować /var/lib/mysqltmpna tym dużym dysku.

OPCJA 2

Moje trzyetapowe sugestie powinny nadal działać, nawet przy ograniczonej przestrzeni dyskowej

KOMENTARZ

Szkoda, że ​​komunikat o błędzie mówi

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.

Oznacza to, że system operacyjny jest w porządku. Nie istnieje również żaden powiązany numer błędu dla tej sytuacji.

Jest jeszcze jedna rzecz, którą powinieneś wiedzieć. Dokumentacja MySQL mówi, że szybkie tworzenie indeksu dla InnoDB wciąż trafia na dysk :

Podczas tworzenia indeksu pliki są zapisywane w katalogu tymczasowym ($ TMPDIR w systemie Unix,% TEMP% w systemie Windows lub wartość zmiennej konfiguracyjnej --tmpdir). Każdy plik tymczasowy jest wystarczająco duży, aby pomieścić jedną kolumnę tworzącą nowy indeks, a każdy z nich jest usuwany, gdy tylko zostanie scalony z indeksem końcowym.

Ze względu na ograniczenie MySQL, tabela jest kopiowana zamiast używania „Szybkiego tworzenia indeksu” podczas tworzenia indeksu na TABELI TYMCZASOWEJ. Zgłoszono to jako błąd MySQL # 39833 .

AKTUALIZACJA 16.06.2014, 13:58 EDT

[mysqld]
datadir                         = /mnt/cbsvolume1/var/lib/mysql
tmpdir                          = /mnt/cbsvolume1/var/lib/mysql

Upewnij się, że /mnt/cbsvolume1/var/lib/mysqlmasz 100G lub więcej za darmo

RolandoMySQLDBA
źródło
Zajęło mi kilka tygodni, aby zbudować tabelę przede wszystkim ze zrzutu (dane 85 GB). Czy jest jakiś inny sposób niż zrobienie tego od nowa? Czy poleciłbyś może podzielić tabelę na partycje?
Noam
Partycjonowanie nie pomoże, ponieważ ALTER TABLE, aby dodać indeks, nadal próbowałby załadować wszystko w jednej transakcji. Pamiętaj, że walczysz z architekturą InnoDB, a nie z przestrzenią dyskową. Moja odpowiedź powinna ci pomóc w tym przypadku, ponieważ mysqldump wstrzykuje kilka tysięcy wierszy na WSTAW, a nie wstawkę typu „wszystko albo nic” do tabeli tymczasowej z możliwością wycofania.
RolandoMySQLDBA
Ale utworzenie tej tabeli zajmie (ponownie) wieczność. Jakikolwiek sposób na uruchomienie indeksu tworzenia przy wyłączeniu opcji wycofywania?
Noam
Tworzenie indeksu to DDL, a nie DML. Nie można zmienić zachowania DDL. Dlatego mój post używa technik DML.
RolandoMySQLDBA
BTW, ile wierszy ma tabela ???
RolandoMySQLDBA