La création d'index MySQL échouant sur la table est pleine

10

MISE À JOUR: tl; dr: Le problème était que MySQL utilise le TMPDIRlors de la création d'index. Et mon TMPDIRétait celui qui manque d'espace disque.

Q d'origine:

J'essaie d'ajouter un index à une table InnoDB et d'obtenir un table is full error. J'ai suffisamment d'espace disque et la configuration MySQL a un fichier par table = 1. Les données de la table sont de 85 Go et je suppose que l'index sera d'environ 20 Go à 30 Go et j'ai beaucoup plus d'espace disque que cela. J'utilise également ext3, donc je ne pense pas qu'il y ait de problème avec la limite de taille de fichier du point de vue du système d'exploitation.

L'erreur enregistrée ressemble à ceci:

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

Quelle est la cause de cela et comment puis-je résoudre ce problème?

La table de création:

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

Tout comme les données sont de 87,4 Go et j'estime qu'il y a environ 1,5 milliard de lignes.

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
la source
1
"InnoDB: Vérifiez que votre système d'exploitation et votre système de fichiers prennent en charge les fichiers de cette taille. InnoDB: Vérifiez également que le disque n'est pas plein ou qu'un quota de disque a été dépassé." - avez-vous vérifié cela? Utilisez-vous ext2? ext3? xfs? Vérifié les quotas pour l'utilisateur?
Philᵀᴹ
@Phil Vérifié les deux. J'utilise ext3 et je suis presque certain qu'il n'y a pas de problème d'espace disque. Hunch est que cela a quelque chose à voir avec un journal atteignant sa limite ou quelque chose, mais aucune idée de la façon de vérifier et de corriger.
Noam
Je me demande quel fichier "(fusionner)" est?
akuzminsky
@akuzminsky hmm je n'en ai aucune idée. J'ai uniquement exécuté une nouvelle création d'index à partir de PHPMyAdmin.
Noam
+1 pour TMPDIR, c'était aussi le problème sur mon serveur.
Chad E.

Réponses:

8

S'il vous plaît ne vous laissez pas berner par le message d'erreur The table 'my_table' is full. Ce scénario rare n'a absolument rien à voir avec l'espace disque. Cette table pleine condition a à voir avec la plomberie interne d'InnoDB.

Tout d'abord, jetez un œil à ce schéma de l'architecture InnoDB

Architecture InnoDB

Veuillez noter que l'espace disque logique système (le fichier ibdata) n'a que 128 segments de restauration et 1023 emplacements de restauration par segment de restauration. Cela limite la taille de la capacité de restauration d'une transaction. En d'autres termes, si un segment de restauration unique a besoin de plus de 1 023 emplacements pour prendre en charge une transaction, la transaction atteindra cette table is fullcondition.

Pensez au restaurant Red Lobster dans le New Jersey . Il peut avoir une capacité de 200 personnes. Si le restaurant est plein, une file de personnes peut sortir pour attendre. Si les gens sur la ligne s'impatientent, ils peuvent partir car le restaurant est plein. Évidemment, la solution ne serait pas d'agrandir le New Jersey (ou d'obtenir plus d'espace disque). La solution serait d'agrandir le restaurant Red Lobster. De cette façon, vous pouvez augmenter la capacité d'accueil à, disons, 240. Même avec cela, une ligne peut se former à l'extérieur si plus de 240 personnes décident de venir à Red Lobster.

Juste pour vous donner un exemple, j'avais un client avec 2 To d'espace disque logique système et innodb_file_per_table était désactivé. (346G pour ibdata1, la réinitialisation pour ibdata2). J'ai exécuté cette requête

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

Ensuite, j'ai soustrait InnoDBDataIndexSpace de la somme des tailles de fichier pour ibdata1 et ibdata2. J'ai deux choses qui m'ont choqué

  • Il restait 106 Go à l'intérieur de l'espace disque logique du système.
  • J'ai cette même Table is Fullcondition

Cela signifie que le 106 Go était utilisé pour la plomberie interne d'InnoDB. Le client utilisait ext3 à l'époque.

La solution pour moi a été d'ajouter ibdata3. J'en ai discuté dans mon ancien article Comment résoudre "La table ... est pleine" avec "innodb_file_per_table"?

J'en ai également discuté dans d'autres articles

Gardez à l'esprit que cette condition peut se produire même si innodb_file_per_table a été activé. Comment? Les annulations et les journaux d'annulation sont à l'origine de pics incontrôlés liés à la croissance d'ibdata1 .

VOTRE QUESTION RÉELLE

Étant donné que vous ajoutez un index à une table et que vous l'obtenez Table is Full, la table doit être énorme et ne peut pas tenir dans un seul segment de restauration. Vous devez procéder comme suit:

ÉTAPE 01

Récupérez les données dans un fichier de vidage

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

ÉTAPE 02

Connectez-vous à MySQL et exécutez ceci

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;

ÉTAPE 03

Charger la table avec l'index supplémentaire avec les données

mysql -Dmydb < table_data.sql

C'est tout.

Vous voyez, le problème est que ALTER TABLE essaiera d'injecter toutes les lignes de votre immense table en une seule transaction. L'utilisation de mysqldump insérera les données dans la table (maintenant avec un nouvel index) des milliers de lignes à la fois, pas toutes les lignes dans une seule transaction.

Ne vous inquiétez pas what if this doesn't work?La table d'origine sera nommée mytable_olden cas de problème. il peut servir de sauvegarde. Vous pouvez supprimer la sauvegarde lorsque vous savez que la nouvelle table fonctionne pour vous.

Essaie !!!

MISE À JOUR 2014-06-16 11:13 EDT

Si vous êtes préoccupé par le vidage des données plus volumineuses, il suffit de le compresser.

Vous pouvez faire les mêmes étapes, mais comme suit

ÉTAPE 01

Récupérez les données dans un fichier de vidage

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

ÉTAPE 02

Connectez-vous à MySQL et exécutez ceci

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;

ÉTAPE 03

Charger la table avec l'index supplémentaire avec les données

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

ou

gunzip < table_data.sql.gz | mysql -Dmydb

MISE À JOUR 2014-06-16 12:55 EDT

Je viens de penser à un autre aspect de cette question.

Puisque vous faites du DDL et non du DML, il est possible que ce ne soit pas de la plomberie interne InnoDB. Étant donné que DDL ne peut pas revenir en arrière pour InnoDB , le problème doit être la plomberie externe. Où cette plomberie externe est-elle obstruée? Je soupçonne le dossier temporaire pour le système d'exploitation. Pourquoi?

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

Voir le disk quota exceeded? Où ce quota de disque est-il imposé?

Exécutez cette requête

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Tu as dit que c'était /var/lib/mysqltmp

Maintenant, exécutez cela dans le système d'exploitation

df -h /var/lib/mysqltmp

Quelque chose me dit que l'espace /var/lib/mysqltmpétait épuisé Après tout, vous n'avez que 14G libre. Aux yeux du DDL (pour créer l'index), un rollback s'est produit, non pas dans le fichier ibdata1, mais à l' tmpdiremplacement. S'il /var/lib/mysqltmpn'est monté nulle part, les données temporaires sont écrites dans la partition racine. Si /var/lib/mysqltmpest monté quelque part, alors ce montage est rempli de données de ligne. Dans les deux cas, il n'y a pas assez de place pour terminer la DDL.

Vous avez deux options ici

OPTION 1

Vous pouvez également créer un grand disque (peut-être avec 100 + Go) et monter /var/lib/mysqltmpsur ce grand disque.

OPTION 2

Mes suggestions en 3 étapes devraient toujours fonctionner, même avec l'espace disque limité dont vous disposez.

COMMENTAIRE

Dommage que le message d'erreur que vous avez publié indique

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

Cela signifie que le système d'exploitation est très bien. Il n'existe également aucun numéro d'erreur associé à cette situation.

Il y a une autre chose que vous devez savoir. La documentation MySQL indique que la création rapide d'index pour InnoDB va toujours sur le disque :

Lors de la création de l'index, les fichiers sont écrits dans le répertoire temporaire ($ TMPDIR sous Unix,% TEMP% sous Windows ou la valeur de la variable de configuration --tmpdir). Chaque fichier temporaire est suffisamment volumineux pour contenir une colonne qui constitue le nouvel index, et chacun est supprimé dès qu'il est fusionné dans l'index final.

En raison d'une limitation de MySQL, la table est copiée, plutôt que d'utiliser «Création rapide d'index» lorsque vous créez un index sur une TABLE TEMPORAIRE. Cela a été signalé comme bogue MySQL # 39833 .

MISE À JOUR 2014-06-16 13:58 EDT

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

Veuillez vous assurer que /mnt/cbsvolume1/var/lib/mysql100G ou plus sont gratuits

RolandoMySQLDBA
la source
Cela m'a pris des semaines pour construire la table à partir de la décharge (données de 85 Go). Y a-t-il un autre moyen que de recommencer? Recommanderiez-vous peut-être de partitionner la table?
Noam
Le partitionnement n'aidera pas car ALTER TABLE pour ajouter l'index tentera toujours de tout charger en une seule transaction. N'oubliez pas que vous combattez l'architecture InnoDB, pas l'espace disque. Ma réponse devrait vous aider dans ce cas puisque le mysqldump injecte quelques milliers de lignes par INSERT, pas un insert tout ou rien dans une table temporaire avec la possibilité d'un rollback.
RolandoMySQLDBA
Mais la création de cette table prendra (encore) une éternité. Est-il possible d'exécuter un index de création tout en désactivant l'option de restauration?
Noam
La création d'un index est DDL et non DML. Vous ne pouvez pas modifier le comportement DDL. C'est pourquoi mon article utilise des techniques DML.
RolandoMySQLDBA
BTW combien de lignes la table a-t-elle ???
RolandoMySQLDBA