Frage

Ich verwalte eine Anwendung mit einer sehr großen (fast 1 TB Daten mit mehr als 500 Millionen Zeilen in einer Tabelle) Oracle Database Back End. Die Datenbank macht wirklich nichts (keine Sprocs, keine Auslöser oder irgendetwas), sondern nur ein Datenspeicher.

Jeden Monat müssen wir Aufzeichnungen von den beiden Haupttischen löschen. Die Kriterien für die Säuberung variieren und sind eine Kombination aus Reihenalter und einigen Statusfeldern. In der Regel speichern wir zwischen 10 und 50 Millionen Zeilen pro Monat (wir fügen ungefähr 3-5 Millionen Zeilen pro Woche über Importe hinzu).

Derzeit müssen wir dies in Chargen von etwa 50.000 Zeilen löschen (dh 50000, Comit, 50000, Commit, Wiederholung). Wenn Sie versuchen, das gesamte Stapel auf einmal zu löschen, reagieren die Datenbank etwa eine Stunde lang nicht (abhängig von der Anzahl der Zeilen). Das Löschen der Zeilen in solchen Chargen ist sehr rau auf dem System, und wir müssen es in der Regel "mit der Zeit zulässt" im Laufe einer Woche. Das kontinuierliche Ausführen des Skripts kann zu einem für den Benutzer nicht akzeptablen Leistungsverschlechterung führen.

Ich glaube, dass diese Art des Stapel -Löschens auch die Indexleistung beeinträchtigt und andere Auswirkungen hat, die letztendlich die Leistung der Datenbank beeinträchtigen. Es gibt 34 Indizes in nur einer Tabelle, und die Indexdatengröße ist tatsächlich größer als die Daten selbst.

Hier ist das Drehbuch, mit dem einer unserer IT -Leute diese Säuberung tut:

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 Seien Sie um 99,99999% und wir haben nur einmal im Jahr ein 2 -Tage -Wartungsfenster.

Ich suche eine bessere Methode, um diese Datensätze zu entfernen, aber ich habe noch keine gefunden. Irgendwelche Vorschläge?

War es hilfreich?

Lösung

Die Logik mit 'a' und 'b' könnte hinter a "versteckt" sein virtuell Spalte, auf der Sie die Partitionierung durchführen können:

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;

Andere Tipps

Die klassische Lösung dafür ist zu Trennwand Ihre Tische, z. B. für Monat oder bis zur Woche. Wenn Sie zuvor nicht auf sie gestoßen sind, ist eine partitionierte Tabelle wie mehrere identisch strukturierte Tabellen mit einem impliziten UNION Bei der Auswahl und Oracle speichert automatisch eine Zeile in der entsprechenden Partition, wenn sie sie basierend auf den Partitionierungskriterien einfügen. Sie erwähnen Indizes - Nun, jede Partition erhält auch ihre eigenen partitionierten Indizes. Es ist eine sehr billige Operation in Oracle, um eine Partition fallen zu lassen (es ist analog zu a TRUNCATE In Bezug auf die Ladung, weil Sie das wirklich tun - ein Abschneiden oder Ablegen eines dieser unsichtbaren Untertische). Es wird eine erhebliche Menge an Verarbeitung sein, um "danach" zu partitionieren, aber es gibt keinen Sinn, über verschüttete Milch zu weinen - die Vorteile, die die Kosten bisher bisher zu tun haben. Jeden Monat teilen Sie die Top -Partition, um eine neue Partition für die Daten des nächsten Monats zu erstellen (Sie können diese problemlos mit einem automatisieren DBMS_JOB).

Und mit Partitionen können Sie auch nutzen Parallele Anfrage und Teilungsausscheidung, was Ihre Benutzer sehr glücklich machen sollte ...

Ein Aspekt ist zu berücksichtigen, wie viel von der Löschleistung aus den Indizes und wie viel aus der Rohtabelle. Jeder aus der Tabelle gelöschte Datensatz erfordert die gleiche Löschung der Zeile aus jedem BTree -Index. Wenn Sie mehr als 30 Bree -Indizes haben, vermute ich, dass die meiste Zeit in der Indexwartung verbracht wird.

Dies hat einen Einfluss auf die Nützlichkeit der Partitionierung. Angenommen, Sie haben einen Index zum Namen. Ein Standard -BTREE -Index, alles in einem Segment, muss möglicherweise vier Sprünge durchführen, um vom Wurzelblock zum Blattblock zu gelangen, und eine fünfte Lektüre, um die Zeile zu erhalten. Wenn dieser Index in 50 Segmente aufgeteilt ist und Sie den Partitionschlüssel als Teil der Abfrage nicht haben, muss jeder dieser 50 Segmente überprüft werden. Jedes Segment ist kleiner, sodass Sie möglicherweise nur 2 Sprünge durchführen müssen, aber möglicherweise noch 100 Lesevorgänge anstelle der vorherigen 5.

Wenn es sich um Bitmap -Indizes handelt, sind die Gleichungen unterschiedlich. Sie verwenden wahrscheinlich keine Indizes, um einzelne Zeilen zu identifizieren, sondern sie. Anstatt eine Abfrage zu verwenden, die 5 iOS verwendet, um einen einzelnen Datensatz zurückzugeben, wurde 10.000 iOS verwendet. Daher spielt der zusätzliche Aufwand in zusätzlichen Partitionen für den Index keine Rolle.

Die Löschung von 50 Millionen Rekorde pro Monat in Chargen von 50.000 beträgt nur 1000 Iterationen. Wenn Sie alle 30 Minuten löschen, sollte dies Ihre Anforderungen erfüllen. Eine geplante Aufgabe zum Ausführen der von Ihnen veröffentlichten Abfrage, aber die Schleife entfernen, sodass sie nur einmal ausgeführt werden sollten. Wir machen ungefähr das gleiche Volumen an Aufzeichnungen in unserem Fertigungswerk, das so ziemlich rund um die Uhr läuft und unsere Bedürfnisse entspricht. Wir haben es tatsächlich alle 10 Minuten etwas mehr 10.000 Rekorde ausgebreitet, was in etwa 1 oder 2 Sekunden auf unseren Oracle Unix -Servern ausgeführt wird.

Wenn der Festplattenraum keine Prämie ist, können Sie in der Lage sein, eine "Arbeit" -Kopie der Tabelle zu erstellen, beispielsweise eine "Arbeit" my_table_new, mit CTAs (Tabelle erstellen wie ausgewählt) mit Kriterien, die die zu fallen gelassenen Datensätze weglassen. Sie können die Erstellung parallel und mit dem Anhang Hinweis erstellen, um sie schnell zu machen und dann alle Ihre Indizes zu erstellen. Dann, sobald es fertig war (und getestet), benennen Sie die vorhandene Tabelle in um my_table_old und benennen Sie den "Arbeit" -Tisch in um my_table. Sobald Sie sich mit allem wohl fühlen drop my_table_old purge den alten Tisch loswerden. Wenn es eine Reihe von ausländischen Schlüsselbeschränkungen gibt, schauen Sie sich das an die dbms_redefinition PL/SQL -Paket. Es wird Ihre Indizes, Krankenstaaten usw. klonen, wenn Sie die entsprechenden Optionen verwenden. Dies ist eine Summe eines Vorschlags von Tom Kyte von Asktom Ruhm. Nach dem ersten Lauf können Sie alles automatisieren, und die Erstellungstabelle sollte viel schneller laufen und können während des Ablaufs des Systems erfolgen, und die Ausfallzeit von Anwendungen wäre auf weniger als eine Minute für die Umbenennung der Tabellen beschränkt. Die Verwendung von CTAs wird viel schneller sein als mehrere Chargendeletten. Dieser Ansatz kann besonders nützlich sein, wenn Sie keine Partitionierung lizenziert haben.

Proben Sie CTAs, halten Sie Zeilen mit Daten aus den letzten 365 Tagen 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;

Wenn Sie eine Partition fallen lassen, lassen Sie globale Indizes unbrauchbar, die wieder aufbauen müssen. Der Wiederaufbau globaler Indizes wäre ein großes Problem, als ob Sie es online tun würden, es wird ziemlich langsam, sonst brauchen Sie Ausfallzeiten. In beiden Fällen kann nicht für die Anforderung passen.

"Wir landen normalerweise zwischen 10 und 50 Millionen Zeilen pro Monat"

Ich würde empfohlen, PL/SQL -Stapel -Löschen zu verwenden, mehrere Stunden sind in Ordnung, denke ich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit dba.stackexchange
scroll top