[Oracle] count(*) Performance verbessern

Der nette Mann

Erfahrenes Mitglied
Hi,

ich habe ein Programm geschrieben, dass die Tabellen in ein Array schreibt, für die zu einer bestimmten Bedingung Einträge gefunden werden. Diesen Befehle wähle ich mit

select count(*) from Tabellenname where Bedingung

soweit so gut, leider haben einige Tabellen extrem viele Datensätze (auch mit Bedingung), so dass die Ausführung des Befehls für manche Tabellen über eine Minute dauert? Kann ich nicht irgendwie sagen, dass er z.B. beim ersten gefundenen Eintrag abbricht?

Danke für eure Antworten...
 
Um den COUNT(*) zu beschleunigen musst du einen FULL TABLE SCAN bei großen
Tabellen verhindern. Der führt dazu dass Oracle alle Blöcke der Tabelle
ließt vollkommen egal ob Daten drin stehen oder nicht. Er ließt das komplette
Segment bis zur High Watermark. Wenn deine Tabelle sehr fragmentiert ist
kann ein COUNT(*) per FULL TABLE Scan mehrere Minuten dauern, obwohl nur 3
Datensätze gefunden werden.

Die einzige Möglichkeit das ganze zu beschleunigen ist ein Index auf die
Suchbedingung zu legen. Dann kann er einen Index Range Scan machen, was um einiges
schneller geht.

Wenn dich aber die Anzahl nicht interessiert, kannst du auch so abfragen
Code:
SELECT /*+ FIRST_ROWS(1) */ 1 
FROM xyz 
WHERE bedingung
AND ROWNUM = 1;

das sollte zumindest um einniges schneller sein als per COUNT(*)
 
Hab das ganze nochmal selbst getestet, einfach aus Interesse was so an Performancegewinn möglich ist. Ich habe das ganze an einer zweispaltigen Tabelle getestest, die aus dem Thread "[Oracle] bestimmten "Zeilenbereich" einer Tabelle anzeigen. " von Thomas. Habe sie mit 10 Mio Datensätzen gefüllt und nach jedem ausgeführten Statement den Cache der Datenbank mit
Code:
 ALTER SYSTEM FLUSH SHARED_POOL; 
ALTER SYSTEM FLUSH BUFFER_CACHE;
wieder geleert um gleiche Bedingungen zu haben.

Hier meine Ergebnisse:

Code:
SQL> select count(*) from test where id = 10000000;

  COUNT(*)
----------
         1

Abgelaufen: 00:01:42.71

SQL> SELECT /*+ FIRST_ROWS(1) */ 1
  2  FROM test
  3  WHERE id = 10000000
  4  AND ROWNUM = 1;

         1
----------
         1

Abgelaufen: 00:00:06.31

Man sieht schonmal einen kleinen Unterscheid ;)
Jetzt hab ich ein Index über das Feld angelegt.

Code:
CREATE INDEX IDX_ID 
   ON TEST( ID ) 
   COMPUTE STATISTICS 
   TABLESPACE INDX 
   NOLOGGING;
   
Index wurde angelegt.

Abgelaufen: 00:02:13.84

Und jetzt alles nochmal...

Code:
  1  SELECT /*+ FIRST_ROWS(1) */ 1
  2  FROM test
  3  WHERE id = 10000000
  4* AND ROWNUM = 1

         1
----------
         1

Abgelaufen: 00:00:00.00

Das sieht noch besser aus ;) Ich denke damit sollte deine Frage beantwortet sein, oder ?

Wen es interessiert ... Die Tabelle hat knapp 255 MB mit 10 Millionen Einträgen, der Index hat knapp 170 MB.
 
Hallo!

Mal wieder Klasse gemacht @ ExceptionFault!
Mal noch eine Frage: sobald man auf eine Tabelle einen Index über eine oder mehrere Spalten anlegt erkauft man sich ja den Performancegewinn bei Select / Suchoperationen damit, dass einfüge / update Operationen etwas langsamer ablaufen (Da der Index ja u.U. wieder neu aufgebaut werden muss). Wie ermittelt man denn das "optimale" Verhältnis zwischen schneller Suche und vertretbar schnellen Einfüge/Update Operationen? Sprich um welchen Faktor werden denn Manipulationsanwesiungen durch Indizies verlangsamt (Am Beispiel einer Tabelle mit ähnlichen Dimensionen aus deinem Post (Nur mit ein wenig mehr Spalten ;-) )?

Gruß Tom
 
So, nach einem erheiternden Stromausfall im Geschäft versuch ich mich hier mal wieder mit erfreulicheren Problemen ;)

Eine generelle Formel gibt es für diese Frage (wie kann es anders sein) natürlich nicht. Es kommt auch ganz speziell auf das System an, sprich habe ich ein OLTP System mit vielen kleinen Transaktionen, oder betreibe ich ein Data Warehouse mit großen Batch Jobs und Ladeoperationen. Die beiden Systeme fordern komplett unterschiedliche Betrachtung des index Designs.

Oralce liefert zwei grobe Faustformeln die man hier beachten sollte. Zum einen ist ein INSERT oder UPDATE Statement ca. 3x so Resourcenintensiv mit einem Index, als ohne. Sprich wenn ich 3 Indizes auf einer Tabelle habe brauche ich fast 10x so viel Resourcen bei einem Insert. Ein DELETE braucht nicht ganz so viel, da der Leaf Block des Indexes nicht entfernt sondern nur als gelöscht markiert wird.

Dies führt wiederum zu dem Problem das Indizes immer größer und langsamer werden je mehr Bewegung in meiner Tabelle ist. Daher die zweite Fausformel: Wurden ca. 10% der Tabelle geändert oder gelöscht oder neu eingefügt, sollte der Index neu aufgebaut werden.

Anhand dieser beiden Kriterien muss man nun überlegen was für sein System das Beste ist. Für ein Datawarehouse mit zahllosen Inserts ist es zum Beispiel üblich den Index vor dem Import zu droppen und danach neu aufzubauen. Das beschleunigt den Import um ein vielfaches.

OLTP Systeme hingegen sind meist Abfrage lastig, weshalb auf Indizes niemals verzichtet werden kann. Oft bestehen Tabellen sogar nur aus reinen Index Strukturen (IOT`s) um den Zugriff weiter zu beschleunigen. hier fallen die wenigen Änderungen dann kaum ins Gewicht. Wer also ein OLTP System betreibt steht nicht vor der Frage ob er Indizes benutzt, sondern wie viele.

Also, wie schon gesagt, eine Fausformel gibt es dafür nicht, ich bin gerade in Webanwendungen immer sehr gut damit gefahren generell PK`s anzulegen und für Kreuztabellen (n:m) IOT`s zu verwenden. Ebenso wichtig ist es meiner Meinung nach alle FK Spalten mit Indizes zu belegen um Locks zu vermeiden. Das ist zwar in 9i nichtmehr so kritisch, kann aber dennoch nix schaden. Dann hab ich oft noch ein paar Unique Indizes für Spalten wie Username oder so, wenn ich als PK eine numerische ID habe. (Ich vermeide meist VARCHAR2 Felder als PK). Und ob weitere Indizes nötig sind wird im Testbetrieb ermittelt, wobei hier bei durchdachtem Tabellendesign so gut wie nie nachgebessert werden muss.

Im Datawarehouse Bereich würde ich dann eher auf Bitmap und Bitmap Join Indizes zurückgreifen, die werden nicht so groß und sind bei großen Datenmengen effizienter, ist aber wieder ein eigenes Kapitel da man bei Bitmap Indizes auch sehr viel Performance verlieren kann.
 
Hey,

klasse Antwort Exceptionfault. Da ich gestern leider keine Zeit hatte das ganze zu testen, werde ich mich heute daran machen. Ich berichte dann noch von meinem Test...

Bis dahin... Vielen Dank

Der nette Mann
 
Zuletzt bearbeitet:
Hi,

hier meine Testergebnisse...

Code:
meine größte Tabelle in der DB

vorher:
----------
Durchlaufzeit: 33,141 sec

nachher:
-------------
Durchlaufzeit: 2,229 sec

Da es an die 150 Tabellen sind (nicht alle brauchen so lange), macht sich das ganze deutlich in der Laufzeit bemerkbar.

Die Methode mit den Indizees hätte ich gerne ausprobiert, aber leider fehlen mir dazu die notwendigen Rechte :( Bin ja nur ein kleiner Programmiere ;)

Trotzdem... vielen Dank

Der nette Mann
 

Neue Beiträge

Zurück