it-swarm.com.de

Der beste Weg, um sehr große Recordset in Oracle zu löschen

Ich verwalte eine Anwendung mit einem sehr großen Oracle-Datenbank-Backend (fast 1 TB Daten mit mehr als 500 Millionen Zeilen in einer Tabelle). Die Datenbank macht eigentlich nichts (keine SProcs, keine Trigger oder irgendetwas), sondern ist nur ein Datenspeicher.

Jeden Monat müssen wir Datensätze aus den beiden Haupttabellen löschen. Die Kriterien für die Bereinigung variieren und setzen sich aus dem Zeilenalter und einigen Statusfeldern zusammen. In der Regel werden zwischen 10 und 50 Millionen Zeilen pro Monat gelöscht (über Importe werden wöchentlich etwa 3 bis 5 Millionen Zeilen hinzugefügt).

Derzeit müssen wir diese Löschung in Stapeln von ungefähr 50.000 Zeilen durchführen (dh 50000 löschen, festschreiben, 50000 löschen, festschreiben, wiederholen). Wenn Sie versuchen, den gesamten Stapel auf einmal zu löschen, reagiert die Datenbank etwa eine Stunde lang nicht (abhängig von der Anzahl der Zeilen). Das Löschen der Zeilen in solchen Stapeln ist für das System sehr schwierig, und wir müssen dies normalerweise "so tun, wie es die Zeit erlaubt" im Laufe einer Woche tun. Wenn das Skript kontinuierlich ausgeführt wird, kann dies zu einer Leistungsminderung führen, die für den Benutzer nicht akzeptabel ist.

Ich glaube, dass diese Art des Batch-Löschens auch die Indexleistung beeinträchtigt und andere Auswirkungen hat, die letztendlich dazu führen, dass sich die Leistung der Datenbank verschlechtert. Es gibt 34 Indizes für nur eine Tabelle, und die Indexdatengröße ist tatsächlich größer als die Daten selbst.

Hier ist das Skript, mit dem einer unserer IT-Mitarbeiter diese Bereinigung durchführt:

BEGIN
LOOP

delete FROM tbl_raw 
  where dist_event_date < to_date('[date]','mm/dd/yyyy') and rownum < 50000;

  exit when SQL%rowcount < 49999;

  commit;

END LOOP;

commit;

END;

Diese Datenbank muss um 99,99999% gestiegen sein und wir haben nur einmal im Jahr ein 2-Tage-Wartungsfenster.

Ich suche nach einer besseren Methode zum Entfernen dieser Datensätze, habe aber noch keine gefunden. Irgendwelche Vorschläge?

19
Coding Gorilla

Die Logik mit 'A' und 'B' könnte hinter einer virtuellen Spalte "versteckt" sein, in der Sie die Partitionierung durchführen könnten:

alter session set nls_date_format = 'yyyy-mm-dd';
drop   table tq84_partitioned_table;

create table tq84_partitioned_table (
  status varchar2(1)          not null check (status in ('A', 'B')),
  date_a          date        not null,
  date_b          date        not null,
  date_too_old    date as
                       (  case status
                                 when 'A' then add_months(date_a, -7*12)
                                 when 'B' then            date_b
                                 end
                        ) virtual,
  data            varchar2(100) 
)
partition   by range  (date_too_old) 
( 
  partition p_before_2000_10 values less than (date '2000-10-01'),
  partition p_before_2000_11 values less than (date '2000-11-01'),
  partition p_before_2000_12 values less than (date '2000-12-01'),
  --
  partition p_before_2001_01 values less than (date '2001-01-01'),
  partition p_before_2001_02 values less than (date '2001-02-01'),
  partition p_before_2001_03 values less than (date '2001-03-01'),
  partition p_before_2001_04 values less than (date '2001-04-01'),
  partition p_before_2001_05 values less than (date '2001-05-01'),
  partition p_before_2001_06 values less than (date '2001-06-01'),
  -- and so on and so forth..
  partition p_ values less than (maxvalue)
);

insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '2008-04-14', date '2000-05-17', 
 'B and 2000-05-17 is older than 10 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('B', date '1999-09-19', date '2004-02-12', 
 'B and 2004-02-12 is younger than 10 yrs, must be kept');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2000-06-16', date '2010-01-01', 
 'A and 2000-06-16 is older than 3 yrs, must be deleted');


insert into tq84_partitioned_table (status, date_a, date_b, data) values 
('A', date '2009-06-09', date '1999-08-28', 
 'A and 2009-06-09 is younger than 3 yrs, must be kept');

select * from tq84_partitioned_table order by date_too_old;

-- drop partitions older than 10 or 3 years, respectively:

alter table tq84_partitioned_table drop partition p_before_2000_10;
alter table tq84_partitioned_table drop partition p_before_2000_11;
alter table tq84_partitioned_table drop partition p2000_12;

select * from tq84_partitioned_table order by date_too_old;
18

Die klassische Lösung hierfür ist Partition Ihre Tabellen, z. nach Monat oder nach Woche. Wenn Sie zuvor noch nicht auf sie gestoßen sind, ähnelt eine partitionierte Tabelle bei der Auswahl mehreren identisch strukturierten Tabellen mit einem impliziten UNION, und Oracle speichert beim Einfügen basierend auf den Partitionierungskriterien automatisch eine Zeile in der entsprechenden Partition. Sie erwähnen Indizes - nun, jede Partition erhält auch ihre eigenen partitionierten Indizes. In Oracle ist es eine sehr kostengünstige Operation, eine Partition zu löschen (in Bezug auf die Last ist sie analog zu einer TRUNCATE, da Sie genau das tun - eine dieser unsichtbaren Untertabellen abschneiden oder löschen). Es wird eine erhebliche Menge an Verarbeitung sein, um "nachträglich" zu partitionieren, aber es macht keinen Sinn, über verschüttete Milch zu weinen - die Vorteile, die dies bisher mit sich bringt, überwiegen die Kosten. Jeden Monat teilen Sie die oberste Partition auf, um eine neue Partition für die Daten des nächsten Monats zu erstellen (Sie können diese problemlos mit einem DBMS_JOB Automatisieren).

Und mit Partitionen können Sie auch parallele Abfrage und Partitionseliminierung ausnutzen, was Ihre Benutzer sehr glücklich machen sollte ...

14
Gaius

Ein zu berücksichtigender Aspekt ist, wie viel der Löschleistung aus Indizes und wie viel aus der Rohtabelle resultiert. Jeder aus der Tabelle gelöschte Datensatz erfordert das gleiche Löschen der Zeile aus jedem btree-Index. Wenn Sie mehr als 30 btree-Indizes haben, wird vermutlich die meiste Zeit für die Indexpflege aufgewendet.

Dies wirkt sich auf die Nützlichkeit der Partitionierung aus. Angenommen, Sie haben einen Index für den Namen. Ein Standard-Btree-Index, alle in einem Segment, muss möglicherweise vier Sprünge ausführen, um vom Stammblock zum Blattblock zu gelangen, und einen fünften Lesevorgang, um die Zeile zu erhalten. Wenn dieser Index in 50 Segmente unterteilt ist und Sie den Partitionsschlüssel nicht als Teil der Abfrage haben, muss jedes dieser 50 Segmente überprüft werden. Jedes Segment ist kleiner, so dass Sie möglicherweise nur 2 Sprünge ausführen müssen, aber möglicherweise immer noch 100 Lesevorgänge anstelle der vorherigen 5 ausführen.

Wenn es sich um Bitmap-Indizes handelt, sind die Gleichungen unterschiedlich. Sie verwenden wahrscheinlich keine Indizes, um einzelne Zeilen zu identifizieren, sondern Gruppen von ihnen. Anstelle einer Abfrage mit 5 E/A zur Rückgabe eines einzelnen Datensatzes wurden 10.000 E/A verwendet. Daher spielt der zusätzliche Aufwand für zusätzliche Partitionen für den Index keine Rolle.

4
Gary

das Löschen von 50 Millionen Datensätzen pro Monat in Stapeln von 50.000 ist nur 1000 Iterationen. Wenn Sie alle 30 Minuten 1 löschen, sollte dies Ihren Anforderungen entsprechen. Eine geplante Aufgabe zum Ausführen der von Ihnen geposteten Abfrage, zum Entfernen der Schleife, damit sie nur einmal ausgeführt wird, sollte jedoch keine merkliche Beeinträchtigung für die Benutzer verursachen. Wir machen ungefähr das gleiche Volumen an Aufzeichnungen in unserer Produktionsanlage, die fast rund um die Uhr läuft und unseren Anforderungen entspricht. Wir verteilen es tatsächlich alle 10 Minuten auf etwas mehr als 10.000 Datensätze, die in etwa 1 oder 2 Sekunden auf unseren Oracle-Unix-Servern ausgeführt werden.

2
Jason Jakob

Wenn der Speicherplatz nicht knapp ist, können Sie möglicherweise eine "Arbeits" -Kopie der Tabelle erstellen, z. B. my_table_new unter Verwendung von CTAS (Create Table As Select) mit Kriterien, bei denen die zu löschenden Datensätze weggelassen werden. Sie können die create-Anweisung parallel und mit dem Anhangshinweis ausführen, um sie schnell zu machen, und dann alle Ihre Indizes erstellen. Benennen Sie die vorhandene Tabelle nach Abschluss (und Test) in my_table_old und benenne die "Arbeit" -Tabelle in my_table. Sobald Sie mit allem vertraut sind drop my_table_old purge um den alten Tisch loszuwerden. Wenn es eine Reihe von Fremdschlüsselbeschränkungen gibt, werfen Sie einen Blick auf dbms_redefinitionPL/SQL-Paket . Bei Verwendung der entsprechenden Optionen werden Ihre Indizes, Einschränkungen usw. geklont. Dies ist eine Zusammenfassung eines Vorschlags von Tom Kyte von AskTom Ruhm. Nach dem ersten Durchlauf können Sie alles automatisieren, und die Erstellungstabelle sollte viel schneller ausgeführt werden. Sie kann ausgeführt werden, während das System hochgefahren ist. Die Ausfallzeit der Anwendung wäre auf weniger als eine Minute begrenzt, um die Tabellen umzubenennen. Die Verwendung von CTAS ist viel schneller als das Löschen mehrerer Stapel. Dieser Ansatz kann besonders nützlich sein, wenn Sie keine lizenzierte Partitionierung haben.

Beispiel für CTAS, wobei Zeilen mit Daten der letzten 365 Tage und flag_inactive = 'N':

create /*+ append */ table my_table_new 
   tablespace data as
   select /*+ parallel */ * from my_table 
       where some_date >= sysdate -365 
       and flag_inactive = 'N';

-- test out my_table_new. then if all is well:

alter table my_table rename to my_table_old;
alter table my_table_new rename to my_table;
-- test some more
drop table my_table_old purge;
1
Mark Stewart

wenn Sie eine Partition löschen, lassen Sie globale Indizes unbrauchbar, die neu erstellt werden müssen. Die Neuerstellung globaler Indizes wäre ein großes Problem. Wenn Sie dies online tun, ist dies ziemlich langsam, da Sie sonst Ausfallzeiten benötigen. In beiden Fällen kann nicht für die Anforderung passen.

"Normalerweise werden zwischen 10 und 50 Millionen Zeilen pro Monat gelöscht"

ich würde empfehlen, PL/SQL Batch zu löschen, mehrere Stunden ist in Ordnung, denke ich.

0
iceburge5