El comando DELETE no se completa en la tabla de 30,000,000 filas


22

Heredé una base de datos y estoy buscando limpiarla y acelerarla. Tengo una tabla que contiene 30,000,000 filas, muchas de las cuales son datos basura insertados debido a un error en nombre de nuestro programador. Antes de agregar nuevos índices más optimizados, convertí la tabla de MyISAM a InnoDB y estoy buscando eliminar muchas de las filas que contienen datos basura.

La base de datos es MySQL 5.0 y tengo acceso de root al servidor. Primero estaba ejecutando estos comandos a través de Adminer y luego phpMyAdmin, ambos con los mismos resultados.

El comando que estoy ejecutando es,

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

Básicamente, elimine cualquier cosa en esta columna que comience con un guión -.

Funciona durante aproximadamente 3-5 minutos y luego, cuando veo la lista de procesos, desaparece.

Entonces corro

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

y devuelve millones de filas.

¿Por qué no se completa mi declaración de eliminación?

PD: Soy consciente de lo desactualizado que está MySQL 5.0. Estoy trabajando para mover la base de datos a MySQL 5.6 w InnoDB (tal vez MariaDB 10 w XtraDB) pero hasta que eso suceda, espero responder esto con la base de datos tal como está.

-

Editar eliminado, ver mi respuesta.

Respuestas:


24

Por favor, mire la Arquitectura de InnoDB (imagen de Percona CTO Vadim Tkachenko)

Fontanería InnoDB

Las filas que está eliminando se escriben en los registros de deshacer. El archivo ibdata1 debería estar creciendo en este momento mientras dure la eliminación. De acuerdo con mysqlperformanceblog.comReasons for run-away main Innodb Tablespace :

  • Muchos cambios transaccionales
  • Transacciones muy largas
  • Hilo de purga rezagado

En su caso, la razón # 1 ocuparía un segmento de reversión junto con algo del espacio para deshacer, ya que está eliminando filas. Esas filas deben quedar en ibdata1 hasta que finalice la eliminación. Ese espacio se descarta lógicamente pero el espacio en disco no se reduce.

Necesitas matar esa eliminación ahora mismo. Una vez que elimine la consulta de eliminación, revertirá las filas eliminadas.

Haces esto en su lugar:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Podría haber hecho esto contra la versión MyISAM de la tabla primero. Luego, conviértalo a InnoDB.


21

Creo que podríamos haber complicado demasiado la respuesta requerida en mi caso . No tengo dudas de que tanto Roland como Rick James están en lo correcto al crear una tabla temporal, inyectando solo filas que pasan el filtro, NOT LIKE '-%'pero la solución para mí fue "más fácil" porque había un error importante del que no estaba al tanto hasta ahora. que me disculpo

Ejecuté la consulta en el mysqlmensaje interactivo y noté el mensaje de error,

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

Al buscar el error en Google, descubrí que la solución era aumentar a innodb_buffer_pool_sizetravés del /etc/my.cnfarchivo y reiniciar el demonio mysql. Para mi servidor estaba configurado por defecto 8My lo aumenté a 1G(el servidor tiene 32GB y esta es la única tabla que actualmente es InnoDB).

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

Luego pude ejecutar el comando y eliminar 23 millones de registros en ~ 27 minutos.

Para aquellos curiosos en lo que innodb_buffer_pool_sizedebe establecerse, tome nota de la cantidad de RAM que tiene y luego eche un vistazo a este hilo que ofrece una estimación sugerida en GB.


12

La sugerencia de Roland se puede acelerar haciendo ambas cosas a la vez:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

Pero aquí hay un blog que explica cómo hacer DELETEs grandes en trozos, en lugar de tomarlo para siempre: http://mysql.rjweb.org/doc.php/deletebig La esencia es caminar por la mesa a través del PK, haciendo 1K filas a la vez. (Por supuesto, hay más detalles a tener en cuenta).

Y este blog aborda posibles problemas en la conversión a InnoDB: http://mysql.rjweb.org/doc.php/myisam2innodb


5

Mi primer instinto sería hacer eliminaciones múltiples más pequeñas limitando el número de resultados de la consulta y ejecutando la consulta varias veces:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

Un inconveniente de este enfoque: cada eliminación llevará más y más tiempo. Esto se debe a que necesita omitir más y más filas que no coinciden con WHERE.
Rick James

Es cierto, pero si este proceso no ocurre con demasiada frecuencia, varios escaneos completos de tablas no deberían ser tan malos como el problema original que se está resolviendo, que es que la consulta nunca se completa debido al tamaño de registro de deshacer.
kristianp

Punto valido. (Yo haría el LIMITmás bajo; digamos 10000.)
Rick James

4

La solución más sencilla es simplemente no hacer eso: hacer una eliminación más pequeña, que puede procesarse más fácilmente.

En este caso, habría recomendado probar eliminaciones secuenciales del formulario:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

Tal vez podrías hacer algo como esto:

  • Agregue un nuevo campo llamado deleted.
  • Haz una actualización como UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'.
  • Establezca cronpara eliminar esto por la noche.

La actualización puede tardar tanto como la eliminación.
Rick James
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.