2016-01-11 17 views
6

Mam 7 powiązanych tabel i na jednej z tabel znajduje się kolumna znacznika czasu i chcę usunąć wszystkie wiersze starsze niż 30 dni. Są to BARDZO duże usunięcia. Mówię dziesiątki milionów rekordów. Jeśli usunę wszystkie te rekordy z głównej tabeli, muszę przejrzeć pozostałe 6 tabel i usunąć powiązane z nimi rekordy z tych tabel.MySQL - Big DELETE na wielu tabelach

Moje pytanie brzmi: jaki jest najlepszy sposób na zoptymalizowanie tego?

Myślę o użyciu PARTITION, ale tylko jedna tabela ma kolumnę znacznika czasu. Martwię się, że jeśli upuszczę starą partycję w głównej tabeli, powiązane rekordy będą nadal istniały w pozostałych 6 tabelach. Powiązane rekordy są powiązane z polami (sid, cid).

Dla kontekstu używam snortu i krowy, które są procesorami IDS.

używam MySQL 5.1.73, tabele MyISAM

Oto fragment z dzienników Czyszczenie:

StartTime,EndTime,TimeElapsed,AffectedRows 
Wed Jan 6 01:00:01 EST 2016,Wed Jan 6 01:45:11 EST 2016,45:10,2911807 
Thu Jan 7 01:00:02 EST 2016,Thu Jan 7 01:25:29 EST 2016,25:27,2230255 
Fri Jan 8 01:00:01 EST 2016,Fri Jan 8 01:24:18 EST 2016,24:17,1400470 
Sat Jan 9 01:00:02 EST 2016,Sat Jan 9 05:47:10 EST 2016,287:8,23360088 
Sun Jan 10 01:00:01 EST 2016,Sun Jan 10 10:06:16 EST 2016,546:15,44970072 
Mon Jan 11 01:00:01 EST 2016,Mon Jan 11 09:40:39 EST 2016,520:38,43948091 

To był mój stary skrypt porządki:

/usr/bin/mysql --defaults-extra-file=/old/.my.cnf snort_db >> /root/snortcleaner.log 2>&1 <<EOF 
use snort_db; 

DROP TRIGGER IF EXISTS delete_old; 

DELIMITER // 
CREATE TRIGGER delete_old AFTER DELETE ON event 
FOR EACH ROW 
BEGIN 
DELETE FROM data WHERE data.cid = old.cid AND data.sid = old.sid; 
DELETE FROM iphdr WHERE iphdr.cid = old.cid AND iphdr.sid = old.sid; 
DELETE FROM icmphdr WHERE icmphdr.cid = old.cid AND icmphdr.sid = old.sid; 
DELETE FROM tcphdr WHERE tcphdr.cid = old.cid AND tcphdr.sid = old.sid; 
DELETE FROM udphdr WHERE udphdr.cid = old.cid AND udphdr.sid = old.sid; 
DELETE FROM opt WHERE opt.cid = old.cid AND opt.sid = old.sid; 
END // 
DELIMITER ; 

EOF 

# Send the main MySQL command: Deletes all records betweeen the oldest  timestamp and 31 days from now() 
# Gets the oldest timestamp and ranges a deletion from that to 31 days before now(). If the oldest timestamp is more recent than 31 days, the following command returns 0 anyway. If it is older than 31 days, it will return them 
OLDEST_TIMESTAMP=$(mysql --defaults-extra-file=/old/.my.cnf -Dsnort_db -se "SELECT timestamp FROM event ORDER BY timestamp ASC LIMIT 1;") 
NUM_AFFECTED=$(mysql --defaults-extra-file=/old/.my.cnf -Dsnort_db -se "DELETE FROM event WHERE timestamp BETWEEN DATE_SUB('${OLDEST_TIMESTAMP}', INTERVAL 1 HOUR) AND DATE_SUB(NOW(), INTERVAL 31 DAY); SELECT ROW_COUNT();") 

Jest mój obecny skrypt porządkowy:

DELETE FROM event WHERE timestamp BETWEEN DATE_SUB('${OLDEST_TIMESTAMP}', INTERVAL 1 HOUR) AND DATE_SUB(NOW(), INTERVAL 31 DAY); 

DELETE FROM data USING data LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 
DELETE FROM iphdr USING iphdr LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 
DELETE FROM icmphdr USING icmphdr LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 
DELETE FROM tcphdr USING tcphdr LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 
DELETE FROM udphdr USING udphdr LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 
DELETE FROM opt USING opt LEFT OUTER JOIN event USING (sid,cid) WHERE event.sid IS NULL; 

Przełączam się między tymi dwoma, ponieważ nie wiem, która jest szybsza, ale w rzeczywistości obie są zbyt wolne.

+1

Czy są jakieś zagraniczne klucze z "innych" stołów do głównego stołu? – Bohemian

+0

InnoDB? MyISAM? definicje stołów? –

+0

Tak, istnieją klucze obce, ale pozostałe tabele mają taką samą liczbę wierszy. Są to tabele MyISAM. –

Odpowiedz

0

Spróbuj ustawić swoje klucze obce na kaskadę podczas usuwania, więc nie musisz tworzyć wyzwalacza i ręcznie dołączać i usuwać powiązane rekordy.

Poniższy przykład pokazuje, jak stworzyć związek, który kaskady Delete

CREATE TABLE parent (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB; CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE) ENGINE=INNODB; 

przykład od Mysql website

+0

Używam tabeli MyISAM, ale jeśli miałbym konwertować na tabelę InnoDB, czy usuwanie kaskad może być szybsze niż usuwanie wyzwalacza? –

+0

Niepewny, prawdopodobnie spróbuję to wykluczyć, a następnie spróbuj podejść do partycji – Jack

+0

Daj nam znać, co działa najszybciej, ciekawe pytanie – Jack

0

Rozwiązaliśmy ten problem, jak przy tworzeniu i upuszczenie partycje. Więc tworzymy partycje według daty w twojej tabeli (najlepsza praktyka - automatyzacja za pomocą zdarzeń MySql), a kiedy musisz usunąć stare dane - po prostu upuść niektóre partycje - operacja będzie natychmiastowa, bez żadnych opóźnień lub blokady.

+0

To jest droga, która mnie najbardziej interesuje, ale nie wiem jak zarządzać 6 powiązanymi tabelami. W pozostałych tabelach nie ma pola do podziału. –

0

Co powiesz na zapisanie identyfikatorów wierszy, które zamierzasz usunąć, do tabeli tymczasowej przed ich usunięciem.

Następnie można zmienić skrypt czyszczenia z łączenia na dużej tabeli, gdzie id = null do łączenia na małej (er) tabeli, gdzie id <> null.

+1

Z MySQL 5.6+ możesz określić partycję do przyłączenia się, unikając konieczności kopiowania danych do tabeli tymczasowej. '... wewnętrznie dołącz partycję_big_table (partition_name) na ...' – weirdan

+0

Czy byłoby to znacznie szybsze? Mogę zdecydowanie spróbować tego. –

0

Chciałbym zrobić dwie rzeczy:

zdefiniować kluczy obcych w innych tabelach z

ON DELETE CASCADE 

i zamiast skubać z dala na rzędy godzinę przez godzinę, dodać ograniczenie prostego usuwania

DELETE FROM event 
WHERE timestamp < DATE_SUB(NOW(), INTERVAL 31 DAY) 
LIMIT 500000 

Kontynuuj ponowne uruchamianie, dopóki nie zostaną uszkodzone żadne wiersze lub tyle razy, ile doświadczenie wskazuje, że jest to potrzebne.

Wyreguluj wartość 500000 tak, aby była jak największa bez utraty ważności zapytania.

+0

Z tego, co widziałem, "NA KASKU DELETE" jest tylko na tabelach InnoDB. Używam tabel MyISAM. Słyszałem o tej metodzie usuwania w porcjach. Nie rozumiem jednak poprawy wydajności. Jak to jest szybciej/wydajniej? –

+0

Czy 'event.cid' jest przyrostowe (zawsze zwiększa wartość)? – Bohemian

+0

Nie sądzę. Niekoniecznie. –

0

Zmiana skryptu:

  • zapewnić istnieje indeks na cid dla wszystkich tabel
  • przechwytywania wartości cid masz zamiar usunąć z imprezy
  • zamiast kierowania wszystko stary wydziwianie. target (maksymalnie) a (małe) maksymalna ilość starych wierszy, dlatego wykonuje się stosunkowo szybko
  • uruchomić skrypt często (powiedzmy co 5 minut, co godzinę, codziennie, co ma sens)

Coś jak:

CREATE TABLE IF NOT EXISTS deleted_cids(int cid); -- ensure same datatype as cid in tables 
TRUNCATE deleted_cids; 
INSERT INTO deleted_cids 
SELECT cid FROM event 
WHERE timestamp BETWEEN DATE_SUB('${OLDEST_TIMESTAMP}', INTERVAL 1 HOUR) 
    AND DATE_SUB(NOW(), INTERVAL 31 DAY) 
LIMIT 100000; -- Choose largest LIMIT that gives acceptable execution time 
DELETE event FROM deleted_cids, event WHERE event.cid = deleted_cids.cid; 
DELETE data FROM deleted_cids, data WHERE data.cid = deleted_cids.cid; 
DELETE iphdr FROM deleted_cids, iphdr WHERE iphdr.cid = deleted_cids.cid; 
DELETE icmphdr FROM deleted_cids, icmphdr WHERE icmphdr.cid = deleted_cids.cid; 
DELETE tcphdr FROM deleted_cids, tcphdr WHERE tcphdr.cid = deleted_cids.cid; 
DELETE udphdr FROM deleted_cids, udphdr WHERE udphdr.cid = deleted_cids.cid; 
DELETE opt FROM deleted_cids, opt WHERE opt.cid = deleted_cids.cid; 

zaletą jest to, że każdy delecja jest indeksowane na bazie pojedynczego wykonanie usunąć wszystkie kierowane wiersze - należy wykonać szybko.

Przez dostrojenie LIMITU i częstotliwości wykonania, można znaleźć właściwą równowagę obciążenia serwera. Zdecydowałbym się na częste egzekucje mniejszych kwot, więc twój serwer nigdy nie zostanie przerwany przez ten proces.

Powiązane problemy