it-swarm.com.de

Wie kann ich die DELETE FROM-Leistung bei großen InnoDB-Tabellen verbessern?

Ich habe eine ziemlich große InnoDB-Tabelle, die ungefähr 10 Millionen Zeilen enthält (und es wird erwartet, dass sie 20-mal so groß wird). Jede Zeile ist nicht so groß (durchschnittlich 131 B), aber von Zeit zu Zeit muss ich einen Teil davon löschen, und das dauert ewig. Dies ist die Tabellenstruktur:

 CREATE TABLE `problematic_table` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `taxid` int(10) unsigned NOT NULL,
    `blastdb_path` varchar(255) NOT NULL,
    `query` char(32) NOT NULL,
    `target` int(10) unsigned NOT NULL,
    `score` double NOT NULL,
    `evalue` varchar(100) NOT NULL,
    `log_evalue` double NOT NULL DEFAULT '-999',
    `start` int(10) unsigned DEFAULT NULL,
    `end` int(10) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    KEY `taxid` (`taxid`),
    KEY `query` (`query`),
    KEY `target` (`target`),
    KEY `log_evalue` (`log_evalue`)
) ENGINE=InnoDB AUTO_INCREMENT=7888676 DEFAULT CHARSET=latin1;

Abfragen, die große Teile aus der Tabelle löschen, sehen einfach so aus:

DELETE FROM problematic_table WHERE problematic_table.taxid = '57';

Eine solche Abfrage dauerte fast eine Stunde. Ich kann mir vorstellen, dass der Overhead beim Umschreiben des Index diese Abfragen sehr langsam macht.

Ich entwickle eine Anwendung, die auf vorhandenen Datenbanken ausgeführt wird. Ich habe höchstwahrscheinlich keine Kontrolle über Servervariablen, es sei denn, ich mache Änderungen an diesen obligatorisch (was ich lieber nicht möchte). Ich fürchte, Vorschläge, die diese ändern, sind von geringem Wert.

Ich habe versucht, die Zeilen, die ich nicht löschen möchte, in eine temporäre Tabelle zu INSERT ... SELECT und den Rest einfach wegzulassen, aber da sich das Verhältnis von Löschen zu Halten in Richtung Halten ändert, ist dies nicht mehr sinnvoll Lösung.

Dies ist eine Tabelle, in der in Zukunft häufig INSERTs und SELECTs, aber keine UPDATEs angezeigt werden. Grundsätzlich handelt es sich um eine Protokollierungs- und Referenztabelle, die von Zeit zu Zeit Teile ihres Inhalts löschen muss.

Könnte ich meine Indizes für diese Tabelle verbessern, indem ich ihre Länge einschränke? Würde ein Wechsel zu MyISAM helfen, das DISABLE KEYS bei Transaktionen unterstützt? Was könnte ich noch versuchen, um die Leistung von DELETE zu verbessern?

Bearbeiten: Eine solche Löschung würde in der Größenordnung von etwa einer Million Zeilen liegen.

14
mpe

Diese Lösung kann nach Fertigstellung eine bessere Leistung bieten, die Implementierung kann jedoch einige Zeit in Anspruch nehmen.

Eine neue BITname__-Spalte kann hinzugefügt werden. Standardmäßig ist TRUEfür "aktiv" und FALSEfür "inaktiv" voreingestellt. Wenn dies nicht ausreicht, können Sie TINYINTmit 256 möglichen Werten verwenden.

Das Hinzufügen dieser neuen Spalte wird wahrscheinlich lange dauern, aber sobald sie abgeschlossen ist, sollten Ihre Aktualisierungen viel schneller sein, solange Sie sie von PRIMARYausführen, wie Sie es mit Ihren Löschvorgängen tun, und diese neue Spalte nicht indizieren.

Der Grund, warum InnoDB in einer so massiven Tabelle wie Ihrer so lange zu DELETEbraucht, liegt am Cluster-Index. Sie ordnet Ihre Tabelle physisch an, basierend auf Ihrem PRIMARYname__, zuerst UNIQUEname__, den sie findet, oder was immer sie als geeigneten Ersatz bestimmen kann, wenn sie PRIMARYoder UNIQUEnicht finden kann Festplatte für Geschwindigkeit und Defragmentierung. Es ist also nicht der DELETEname__, der so lange dauert; Es ist die physische Neuordnung, nachdem diese Zeile entfernt wurde.

Wenn Sie eine Spalte mit fester Breite erstellen und diese aktualisieren, anstatt sie zu löschen, müssen Sie Ihre große Tabelle nicht neu ordnen, da der von einer Zeile und Tabelle selbst belegte Speicherplatz konstant ist.

Während der Geschäftszeiten können einzelne DELETEverwendet werden, um die unnötigen Zeilen zu entfernen. Dieser Vorgang ist zwar langsam, aber insgesamt viel schneller als das Löschen einzelner Zeilen.

12
user1382306

Ich hatte ein ähnliches Szenario mit einer Tabelle mit 2 Millionen Zeilen und einer Löschanweisung, die etwa 100.000 Zeilen löschen sollte - dies dauerte etwa 10 Minuten.

Nachdem ich die Konfiguration überprüft hatte, stellte ich fest, dass MySQL Server standardmäßig mit innodb_buffer_pool_size = 8 MB (!) Ausgeführt wurde.

Nach dem Neustart mit innodb_buffer_pool_size = 1,5 GB dauerte das gleiche Szenario 10 Sekunden.

Es scheint also eine Abhängigkeit zu sein, ob "Umordnung der Tabelle" in buffer_pool passen kann oder nicht.

23
vdd

Ich löste ein ähnliches Problem mithilfe einer gespeicherten Prozedur, wodurch die Leistung um einen Faktor von mehreren Tausend verbessert wurde.

Meine Tabelle hatte 33 Millionen Zeilen und mehrere Indizes und ich wollte 10 KB Zeilen löschen. Meine Datenbank war in Azure ohne Kontrolle über innodb_buffer_pool_size. 

Zur Vereinfachung habe ich eine Tabelle tmp_id mit nur einem primären id-Feld erstellt:

CREATE TABLE `tmp_id` (
    `id` bigint(20) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
)

Ich wählte die Menge der IDs, die ich löschen wollte, in tmp_id aus und lief delete from my_table where id in (select id from tmp_id); Dies wurde nicht innerhalb von 12 Stunden abgeschlossen. Ich versuchte es mit nur einer einzigen ID in tmp_id und es dauerte 25 Minuten. Da delete from my_table where id = 1234 in wenigen Millisekunden abgeschlossen wurde, entschied ich mich, dies in einer Prozedur zu tun:

CREATE PROCEDURE `delete_ids_in_tmp`()
BEGIN
    declare finished integer default 0;
    declare v_id bigint(20);
    declare cur1 cursor for select id from tmp_id;
    declare continue handler for not found set finished=1;    
    open cur1;
    igmLoop: loop
        fetch cur1 into v_id;
        if finished = 1 then leave igmLoop; end if;
        delete from problematic_table where id = v_id;
    end loop igmLoop;
    close cur1;
END

Nun löschte call delete_ids_in_tmp(); alle 10.000 Zeilen in weniger als einer Minute.

0
Jan Larsen