La creación del índice MySQL que falla en la tabla está llena

10

ACTUALIZACIÓN: tl; dr: El problema era que MySQL usa el TMPDIRal crear índices. Y mi TMPDIRfue el que se estaba quedando sin espacio en disco.

Q original:

Estoy tratando de agregar un índice a una tabla InnoDB y obtengo un table is full error. Tengo suficiente espacio en disco y la configuración de MySQL tiene un archivo por tabla = 1. Los datos de la tabla son de 85 GB y supongo que el índice será de alrededor de 20 GB a 30 GB y tengo mucho más espacio en disco que eso. También estoy usando ext3, así que no creo que haya ningún problema con el límite de tamaño de archivo desde el punto de vista del sistema operativo.

El error registrado se ve así:

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

¿Qué está causando esto y cómo puedo resolverlo?

La tabla de creación:

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

Como es, los datos son 87.4GB y calculo que hay alrededor de 1.5B filas.

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
fuente
1
"InnoDB: compruebe que su sistema operativo y su sistema de archivos admitan archivos de este tamaño. InnoDB: compruebe también que el disco no está lleno o que se ha excedido la cuota de disco". - ¿Lo has comprobado? ¿Estás usando ext2? ext3? xfs? ¿Verificó las cuotas para el usuario?
Philᵀᴹ
@Phil Marcó ambos. Estoy usando ext3 y estoy casi seguro de que no hay un problema con el espacio en disco. El presentimiento es que tiene algo que ver con un registro que alcanza su límite o algo así, pero realmente no tengo idea de cómo verificarlo y corregirlo.
Noam
Me pregunto qué es el archivo "(fusión)".
akuzminsky
@akuzminsky hmm no tengo idea. Solo ejecuté una nueva creación de índice desde PHPMyAdmin.
Noam
+1 para TMPDIR, ese también era el problema en mi servidor.
Chad E.

Respuestas:

8

No se deje engañar por el mensaje de error The table 'my_table' is full. Este raro escenario no tiene absolutamente nada que ver con el espacio en disco. Esta condición completa de la tabla tiene que ver con la fontanería interna de InnoDB.

Primero, eche un vistazo a este diagrama de la arquitectura InnoDB

Arquitectura InnoDB

Tenga en cuenta que el espacio de tabla del sistema (el archivo ibdata) solo tiene 128 segmentos de reversión y 1023 ranuras de reversión por segmento de reversión. Esto pone límites al tamaño de la capacidad de reversión de una transacción. En otras palabras, si un solo segmento de reversión necesita más de 1023 ranuras para admitir una transacción, la transacción alcanzará esa table is fullcondición.

Piense en el restaurante Red Lobster en Nueva Jersey . Puede tener una capacidad de 200 personas. Si el restaurante está lleno, una fila de personas puede salir a esperar. Si las personas en la fila se impacientan, pueden irse porque el restaurante está lleno. Obviamente, la solución no sería hacer que Nueva Jersey sea más grande (u obtener más espacio en disco). La solución sería agrandar el restaurante Red Lobster. De esa manera, puede aumentar la capacidad de asientos a, digamos, 240. Incluso con eso, se puede formar una fila afuera si más de 240 personas deciden venir a Red Lobster.

Solo para darle un ejemplo, tenía un cliente con 2TB de espacio de tabla del sistema y se deshabilitó innodb_file_per_table . (346G para ibdata1, el restablecimiento para ibdata2). Ejecuté esta consulta

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

Luego, resté InnoDBDataIndexSpace de la suma de los tamaños de archivo para ibdata1 e ibdata2. Tengo dos cosas que me sorprendieron

  • Quedaban 106 GB dentro del espacio de tabla del sistema.
  • Tengo esta misma Table is Fullcondición

Esto significa que el 106GB estaba utilizando para la fontanería interna de InnoDB. El cliente estaba usando ext3 en ese momento.

La solución para mí fue agregar ibdata3. Discutí esto en mi publicación anterior ¿Cómo resolver "La tabla ... está llena" con "innodb_file_per_table"?

He discutido esto en otras publicaciones también

Tenga en cuenta que esta condición puede ocurrir incluso si innodb_file_per_table estaba habilitado. ¿Cómo? Los retrocesos y los registros de deshacer son la fuente de picos incontrolados, es el crecimiento de ibdata1 .

TU PREGUNTA REAL

Como está agregando un índice a una tabla y obteniendo Table is Full, la tabla debe ser enorme y no puede caber dentro de un solo segmento de reversión. Debes hacer lo siguiente:

PASO 01

Obtener los datos en un archivo de volcado

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

PASO 02

Inicie sesión en MySQL y ejecute esto

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;

PASO 03

Cargue la tabla con el índice adicional con los datos.

mysql -Dmydb < table_data.sql

Eso es todo.

Vea, el problema es que ALTER TABLE intentará inyectar todas las filas en su tabla enorme como una sola transacción. El uso de mysqldump insertará los datos en la tabla (ahora con un nuevo índice) miles de filas a la vez, no todas las filas en una sola transacción.

No se preocupe por what if this doesn't work?la tabla original se nombrará mytable_olden caso de cualquier cosa. Puede servir como respaldo. Puede soltar la copia de seguridad cuando sepa que la nueva tabla funciona para usted.

Darle una oportunidad !!!

ACTUALIZACIÓN 2014-06-16 11:13 EDT

Si le preocupa el volcado de los datos más grandes, simplemente gzip.

Puedes hacer los mismos pasos, pero de la siguiente manera

PASO 01

Obtener los datos en un archivo de volcado

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

PASO 02

Inicie sesión en MySQL y ejecute esto

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;

PASO 03

Cargue la tabla con el índice adicional con los datos.

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

o

gunzip < table_data.sql.gz | mysql -Dmydb

ACTUALIZACIÓN 2014-06-16 12:55 EDT

Acabo de pensar en otro aspecto con respecto a este tema.

Como está haciendo DDL y no DML, es posible que esto no sea una tubería interna de InnoDB. Dado que DDL no puede revertir para InnoDB , el problema tiene que ser la plomería externa. ¿Dónde se está obstruyendo esta tubería externa? Sospecho que la carpeta temporal para el sistema operativo. ¿Por qué?

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

¿Ves el disk quota exceeded? ¿Dónde se impone esta cuota de disco?

Ejecute esta consulta

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Dijiste que es /var/lib/mysqltmp

Ahora, ejecuta esto en el sistema operativo

df -h /var/lib/mysqltmp

Algo me dice que el espacio se /var/lib/mysqltmpestaba acabando Después de todo, solo tienes 14G gratis. A los ojos del DDL (para crear el índice), ocurrió una reversión, no en el archivo ibdata1, sino en la tmpdirubicación. Si /var/lib/mysqltmpno está montado en ningún lugar, los datos temporales se escriben en la partición raíz. Si /var/lib/mysqltmpestá montado en algún lugar, entonces ese montaje se está llenando con datos de fila. En cualquier caso, no hay suficiente espacio para completar el DDL.

Tienes dos opciones aquí

OPCIÓN 1

También puede crear un disco grande (quizás con más de 100 GB) y montarlo /var/lib/mysqltmpen ese disco grande.

OPCION 2

Mis sugerencias de 3 pasos aún deberían funcionar, incluso con el espacio en disco limitado que tiene

COMENTARIO

Es una pena que el mensaje de error que publicaste dice

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

Esto significa que el sistema operativo está bien. Tampoco existe ningún número de error asociado para esta situación.

Hay otra cosa que debes saber. La documentación de MySQL dice que la creación rápida de índices para InnoDB todavía va al disco :

Durante la creación del índice, los archivos se escriben en el directorio temporal ($ TMPDIR en Unix,% TEMP% en Windows o el valor de la variable de configuración --tmpdir). Cada archivo temporal es lo suficientemente grande como para contener una columna que forma el nuevo índice, y cada uno se elimina tan pronto como se combina en el índice final.

Debido a una limitación de MySQL, la tabla se copia, en lugar de usar "Creación rápida de índice" cuando se crea un índice en una TABLA TEMPORAL. Esto ha sido reportado como MySQL Bug # 39833 .

ACTUALIZACIÓN 2014-06-16 13:58 EDT

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

Asegúrate de que /mnt/cbsvolume1/var/lib/mysqltenga 100G o más gratis

RolandoMySQLDBA
fuente
En primer lugar, me llevó semanas construir la tabla desde el vertedero (datos de 85 GB). ¿Hay alguna otra manera que no sea hacer esto de nuevo? ¿Recomendaría quizás dividir la tabla?
Noam
La partición no ayudará porque ALTER TABLE para agregar el índice aún intentaría cargar todo en una sola transacción. Recuerde, está luchando contra la arquitectura InnoDB, no contra el espacio en disco. Mi respuesta debería ayudarlo en este caso, ya que mysqldump inyecta unos pocos miles de filas por INSERT, no una inserción de todo o nada en una tabla temporal con la posibilidad de una reversión.
RolandoMySQLDBA
Pero crear esta tabla (nuevamente) tomará una eternidad. ¿Alguna forma de ejecutar un índice de creación mientras deshabilita la opción de reversión?
Noam
Crear un índice es DDL, no DML. No puede cambiar el comportamiento de DDL. Es por eso que mi publicación usa técnicas DML.
RolandoMySQLDBA
Por cierto, ¿cuántas filas tiene la tabla?
RolandoMySQLDBA