web-dev-qa-db-fra.com

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

MISE À JOUR: tl; dr: Le problème était que MySQL utilise le TMPDIR lors de la création d'index. Et mon TMPDIR était celui qui manquait 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% /
10
Noam

Veuillez ne pas vous laisser 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

InnoDB Architecture

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 nécessite plus de 1 023 emplacements pour prendre en charge une transaction, la transaction atteindra ce table is full état.

Pensez à le 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. De toute évidence, 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 eu ce même Table is Full état

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 était activé. Comment? Les annulations et les journaux d'annulation sont la source de pics incontrôlés est la croissance pour ibdata1 .

VOTRE QUESTION RÉELLE

Puisque vous ajoutez un index à une table et 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 pour what if this doesn't work? La table d'origine sera nommée mytable_old en 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. Puisque 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 pour /var/lib/mysqltmp était épuisé Après tout, vous n'avez que 14G gratuit. Aux yeux de la DDL (pour créer l'index), une restauration s'est produite, non pas dans le fichier ibdata1, mais à l'emplacement tmpdir. Si /var/lib/mysqltmp n'est monté nulle part, alors les données temporaires sont écrites dans la partition racine. Si /var/lib/mysqltmp est monté quelque part, puis 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/mysqltmp sur 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 MySQL Bug # 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/mysql a 100G ou plus gratuit

8
RolandoMySQLDBA