Die fehlgeschlagene MySQL-Indexerstellung für die Tabelle ist voll

10

UPDATE: tl; dr: Das Problem war, dass MySQL das TMPDIRbeim Erstellen von Indizes verwendet. Und mir TMPDIRging der Speicherplatz aus.

Original Q:

Ich versuche, einer InnoDB-Tabelle einen Index hinzuzufügen und einen zu erhalten table is full error. Ich habe genügend Speicherplatz und die MySQL-Konfiguration hat eine Datei pro Tabelle = 1. Die Tabellendaten sind 85 GB und ich gehe davon aus, dass der Index zwischen 20 GB und 30 GB liegt und ich viel mehr Speicherplatz habe. Ich verwende auch ext3, damit ich aus Sicht des Betriebssystems keine Probleme mit der Dateigrößenbeschränkung habe.

Der protokollierte Fehler sieht folgendermaßen aus:

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

Was verursacht dies und wie kann ich es beheben?

Die Create-Tabelle:

`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`

Wie ist die Daten ist 87,4 GB und ich schätze, es gibt etwa 1,5 Milliarden Zeilen.

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
quelle
1
"InnoDB: Überprüfen Sie, ob Ihr Betriebssystem und Ihr Dateisystem Dateien dieser Größe unterstützen. InnoDB: Überprüfen Sie auch, ob die Festplatte nicht voll ist oder ein Festplattenkontingent überschritten wurde." - Hast du das überprüft? Verwenden Sie ext2? ext3? xfs? Überprüfen Sie die Quoten für den Benutzer?
Philᵀᴹ
@Phil Beide überprüft. Ich verwende ext3 und bin mir fast sicher, dass es kein Problem mit dem Speicherplatz gibt. Die Vermutung ist, dass es etwas damit zu tun hat, dass ein Protokoll sein Limit erreicht oder so, aber keine Ahnung, wie man es wirklich überprüft und repariert.
Noam
Ich frage mich, was Datei "(zusammenführen)" ist?
Akuzminsky
@akuzminsky hmm ich habe keine ahnung. Ich habe nur eine neue Indexerstellung von PHPMyAdmin ausgeführt.
Noam
+1 für TMPDIR, das war auch auf meinem Server das Problem.
Chad E.

Antworten:

8

Bitte lassen Sie sich von der Fehlermeldung nicht täuschen The table 'my_table' is full. Dieses seltene Szenario hat absolut nichts mit Speicherplatz zu tun. Dieser vollständige Zustand der Tabelle hat mit der internen Installation von InnoDB zu tun.

Schauen Sie sich zunächst dieses Diagramm der InnoDB-Architektur an

InnoDB-Architektur

Bitte beachten Sie, dass der Systemtabellenbereich (die Datei ibdata) nur 128 Rollback-Segmente und 1023 Rollback-Slots pro Rollback-Segment enthält. Dadurch wird die Größe der Rollback-Kapazität einer Transaktion begrenzt. Mit anderen Worten, wenn ein einzelnes Rollback-Segment mehr als 1023 Slots benötigt, um eine Transaktion zu unterstützen, trifft die Transaktion diese table is fullBedingung.

Denken Sie an das Restaurant Red Lobster in New Jersey . Es kann eine Kapazität von 200 Personen haben. Wenn das Restaurant voll ist, kann eine Reihe von Leuten nach draußen gehen, um zu warten. Wenn die Leute in der Leitung ungeduldig werden, können sie gehen, weil das Restaurant voll ist. Offensichtlich wäre die Lösung nicht, New Jersey größer zu machen (oder mehr Speicherplatz zu bekommen). Die Lösung wäre, das Red Lobster Restaurant größer zu machen. Auf diese Weise können Sie die Sitzplatzkapazität auf beispielsweise 240 erhöhen. Trotzdem kann sich draußen eine Linie bilden, wenn mehr als 240 Personen zu Red Lobster kommen.

Um Ihnen ein Beispiel zu geben, ich hatte einen Client mit 2 TB Systemtabellenbereich und innodb_file_per_table war deaktiviert. (346G für ibdata1, das Zurücksetzen für ibdata2). Ich habe diese Abfrage ausgeführt

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

Als nächstes habe ich InnoDBDataIndexSpace von der Summe der Dateigrößen für ibdata1 und ibdata2 abgezogen. Ich habe zwei Dinge, die mich schockierten

  • Im Systemtabellenbereich waren noch 106 GB vorhanden.
  • Ich habe den gleichen Table is FullZustand

Dies bedeutet, dass die 106 GB für die interne Installation von InnoDB verwendet wurden. Der Client verwendete zu diesem Zeitpunkt ext3.

Die Lösung für mich war, ibdata3 hinzuzufügen. Ich habe dies in meinem alten Beitrag besprochen. Wie löse ich "Die Tabelle ... ist voll" mit "innodb_file_per_table"?

Ich habe dies auch in anderen Beiträgen besprochen

Beachten Sie, dass diese Bedingung auch dann auftreten kann, wenn innodb_file_per_table aktiviert wurde. Wie? Rollbacks und Rückgängig-Protokolle sind die Ursache für unkontrollierte Spitzen. Dies ist das Wachstum von ibdata1 .

IHRE TATSÄCHLICHE FRAGE

Da Sie einer Tabelle einen Index hinzufügen und abrufen Table is Full, muss die Tabelle sehr groß sein und darf nicht in ein einzelnes Rollback-Segment passen. Sie müssen Folgendes tun:

SCHRITT 01

Holen Sie sich die Daten in eine Dump-Datei

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

SCHRITT 02

Melden Sie sich bei MySQL an und führen Sie dies aus

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;

SCHRITT 03

Laden Sie die Tabelle mit dem zusätzlichen Index mit den Daten

mysql -Dmydb < table_data.sql

Das ist alles.

Das Problem ist, dass die ALTER TABLE versucht, alle Zeilen in Ihrer riesigen Tabelle als einzelne Transaktion einzufügen. Wenn Sie mysqldump verwenden, werden die Daten Tausende von Zeilen gleichzeitig in die Tabelle (jetzt mit einem neuen Index) eingefügt, nicht alle Zeilen in einer einzelnen Transaktion.

Keine Sorge what if this doesn't work?Die Originaltabelle wird mytable_oldim Falle von irgendetwas benannt. Es kann als Backup dienen. Sie können die Sicherung löschen, wenn Sie wissen, dass die neue Tabelle für Sie funktioniert.

Versuche es !!!

UPDATE 2014-06-16 11:13 EDT

Wenn Sie sich Sorgen über den Speicherauszug der Daten machen, der größer ist, gzipen Sie ihn einfach.

Sie können die gleichen Schritte ausführen, jedoch wie folgt

SCHRITT 01

Holen Sie sich die Daten in eine Dump-Datei

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

SCHRITT 02

Melden Sie sich bei MySQL an und führen Sie dies aus

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;

SCHRITT 03

Laden Sie die Tabelle mit dem zusätzlichen Index mit den Daten

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

oder

gunzip < table_data.sql.gz | mysql -Dmydb

UPDATE 2014-06-16 12:55 EDT

Ich habe gerade an einen anderen Aspekt in Bezug auf dieses Thema gedacht.

Da Sie DDL und nicht DML verwenden, ist es möglich, dass dies keine InnoDB-interne Installation ist. Da DDL für InnoDB kein Rollback durchführen kann , muss das Problem eine externe Installation sein. Wo wird diese externe Installation verstopft? Ich vermute den temporären Ordner für das Betriebssystem. Warum?

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

Sehen Sie das disk quota exceeded? Wo wird dieses Festplattenkontingent festgelegt?

Führen Sie diese Abfrage aus

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Du hast gesagt, es ist /var/lib/mysqltmp

Führen Sie dies nun im Betriebssystem aus

df -h /var/lib/mysqltmp

Etwas sagt mir, dass der Speicherplatz für knapp /var/lib/mysqltmpwurde. Immerhin haben Sie nur 14G frei. In den Augen der DDL (um den Index zu erstellen) kam es zu einem Rollback, nicht in der Datei ibdata1, sondern am tmpdirSpeicherort. Wenn /var/lib/mysqltmpes nirgendwo gemountet ist, werden die temporären Daten in die Root-Partition geschrieben. Wenn /var/lib/mysqltmpes irgendwo gemountet ist, wird dieses Mount mit Zeilendaten gefüllt. In beiden Fällen ist nicht genügend Platz vorhanden, um die DDL abzuschließen.

Sie haben hier zwei Möglichkeiten

OPTION 1

Sie können auch eine große Festplatte (möglicherweise mit mehr als 100 GB) erstellen und /var/lib/mysqltmpauf dieser großen Festplatte bereitstellen.

OPTION 2

Meine 3-Schritt-Vorschläge sollten auch bei begrenztem Speicherplatz funktionieren

KOMMENTAR

Es ist eine Schande, dass die von Ihnen gepostete Fehlermeldung besagt

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

Dies bedeutet, dass das Betriebssystem in Ordnung ist. Es gibt auch keine zugehörige Fehlernummer für diese Situation.

Es gibt noch eine andere Sache, die Sie wissen sollten. Die MySQL-Dokumentation besagt, dass die schnelle Indexerstellung für InnoDB weiterhin auf der Festplatte gespeichert wird :

Während der Indexerstellung werden Dateien in das temporäre Verzeichnis geschrieben ($ TMPDIR unter Unix,% TEMP% unter Windows oder der Wert der Konfigurationsvariablen --tmpdir). Jede temporäre Datei ist groß genug, um eine Spalte zu enthalten, aus der der neue Index besteht, und jede wird entfernt, sobald sie mit dem endgültigen Index zusammengeführt wird.

Aufgrund einer Einschränkung von MySQL wird die Tabelle kopiert, anstatt "Schnelle Indexerstellung" zu verwenden, wenn Sie einen Index für eine TEMPORARY TABLE erstellen. Dies wurde als MySQL Bug # 39833 gemeldet .

UPDATE 2014-06-16 13:58 EDT

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

Bitte stellen Sie sicher, dass /mnt/cbsvolume1/var/lib/mysql100G oder mehr frei sind

RolandoMySQLDBA
quelle
Ich habe Wochen gebraucht, um die Tabelle überhaupt aus dem Dump (85 GB Daten) zu erstellen. Gibt es eine andere Möglichkeit, als dies noch einmal zu tun? Würden Sie vielleicht empfehlen, die Tabelle zu partitionieren?
Noam
Die Partitionierung hilft nicht, da die ALTER TABLE zum Hinzufügen des Index weiterhin versucht, alles in einer einzelnen Transaktion zu laden. Denken Sie daran, dass Sie gegen die InnoDB-Architektur kämpfen, nicht gegen den Speicherplatz. Meine Antwort sollte Ihnen in diesem Fall helfen, da der mysqldump ein paar tausend Zeilen pro INSERT einfügt, nicht ein Alles-oder-Nichts-Insert in eine temporäre Tabelle mit der Möglichkeit eines Rollbacks.
RolandoMySQLDBA
Das Erstellen dieser Tabelle wird jedoch (erneut) ewig dauern. Gibt es eine Möglichkeit, einen Erstellungsindex auszuführen, während die Rollback-Option deaktiviert wird?
Noam
Das Erstellen eines Index ist DDL, nicht DML. Sie können das DDL-Verhalten nicht ändern. Deshalb verwendet mein Beitrag DML-Techniken.
RolandoMySQLDBA
Übrigens, wie viele Zeilen hat die Tabelle ???
RolandoMySQLDBA