PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Performancevergleich bei Count (Oracle,DB2,MaxDB)



Gaert
06-05-2007, 22:37
Hallo Zusammen,

ich habe ein Programm entwickelt, mit dem ich auf SAP Systemen testen möchte wieviele Datensätze sich pro Tabelle in einem Mandanten befinden.

Für nicht SAPler: die meisten Tabellen in einem SAP System sind Mandantenabhängig, d.h. das erste Feld im Primärschlüssel ist ein Feld in dem die Identifikationsnummer des Mandanten enthält.

Ich habe das Programm SAP Systemen mit verschiedenen DBMS drunter getestet, und bin dabei auf erstaunliches gestoßen.

Statement (CLIENT ist das Mandantenfeld - 1. Feld im Primärschlüssel):
SELECT COUNT(*) FROM tabelle WHERE CLIENT = '123'

Die Tabelle auf der ich das ausführe enthält ca. 50GB Daten (ohne Indizes).

Bei DB2 dauert die Abfrage ca. 5 Minuten
Bei Oracle ca. 35 - 45 Minuten
Bei MaxDB über 24 Stunden (danach musste ich abbrechen)

Ich habe auch Erfahrung mit MS SQL Server gesammelt... ich schätze ihn von der Performance her gleichauf mit Oracle.

Die Hardware ist leider nicht ganz vergleichbar, allerdings sind die Unterschiede auch so enorm.
Kann mir jemand erklären, wie die einzelnen Datenbanken technisch vorgehen um einen Count durchzuführen, und woher die unterschiedlichen Laufzeiten kommen?

Kann man bei MaxDB was drehen um das zu optimieren?
Was macht DB2 anders um solche Ergebnisse zu liefern?

Gruß,

Gaert

Christoph
07-05-2007, 09:28
Das sieht mir so aus als wenn DB2 die einzige Datenbank ist, die den Count über den Index macht. Wenn Du die Abfrage öfters machst, würde ich es bei PostgreSQL mal mit einem funktionalen Index auf COUNT(id) probieren. Keine Ahnung, welche der von Dir genannten Datenbanken funktionale Indizes können, aber es ist auf jeden fall einen Versuch wert.

Gaert
07-05-2007, 10:04
Hallo Christoph,

danke für deine Antwort.
Oracle liest auch über den Index - bei MaxDB bin ich mir da nicht so sicher, müsste aber eigentlich auch...

Aus irgendeinem Grund tut sich DB2 mit einem Count nicht so schwer wie Oracle oder MaxDB.

Das mit dem Count(id) ist so ne Sache - Oracle und DB2 unterstützen es... aber SAPs OpenSQL Dialekt nicht - und direkt mit NativeSQL durch die Abstraktionsebene auf die DB zuzugreifen ist mir ein wenig zu heiß.

Übrigens Danke für den Tipp mit PostgreSQL, aber die DB wird von SAP nicht unterstützt und steht daher nicht zur Diskussion.:rolleyes:

Gruß,

Gaert

jan61
07-05-2007, 22:04
Hallo,

zu MaxDB kann ich nichts sagen, aber aus eigener Erfahrung weiss ich, dass Oracle gerade beim count(*) nicht unbedingt naheliegende Indizes verwendet. Man kann dem Optimizer aber über sog. Hints auf die Sprünge helfen. Das sieht dann ungefähr so aus (aus dem Gedächtnis, also besser nochmal nachschlagen):


select count(*) /*+ Tabelle Index */ from Tabelle where Spalte = Wert;

hth
Jan

jan61
07-05-2007, 22:15
Hallo,

zu MaxDB kann ich nichts sagen, aber aus eigener Erfahrung weiss ich, dass Oracle gerade beim count(*) nicht unbedingt naheliegende Indizes verwendet. Man kann dem Optimizer aber über sog. Hints auf die Sprünge helfen. Das sieht dann ungefähr so aus (aus dem Gedächtnis, also besser nochmal nachschlagen):


select count(*) /*+ Tabelle Index */ from Tabelle where Spalte = Wert;

hth
Jan

P.S.: (ich antworte mir selbst, weil der Ändern-Button sich offenbar mit sich selbst beschäftigt - kommt die SW mit Konqueror nicht zurecht?) - ich halte das für einen üblen Bug in Oracle - eine Abfrage mit identischer where-Bedingung, aber ohne count(*) kommt im Bruchteil der Zeit zurück. Kann man z. B. testen mit:

select * from Tabelle where Spalte = Wert and rownum < 50;

Turbohummel
08-05-2007, 07:20
Bei SAP nützen diese Hints nichts, da es Embedded SQL nutzt (zumindest ABAP).
Ich hab die Querry bei uns auch mal durchlaufen lassen. 22 GB Daten auf MaxDB - abbruch nach 3 Stunden.
Lösung:
Nicht count(*) benutzen, sondern
SELECT id FROM blabla WHERE client="blub"
Und dann über die ABAP-Funktion die Anzahl der Werte auslesen.
17 Minuten hat der Spaß gedauert.

Gaert
10-05-2007, 14:28
Bei SAP nützen diese Hints nichts, da es Embedded SQL nutzt (zumindest ABAP).
Ich hab die Querry bei uns auch mal durchlaufen lassen. 22 GB Daten auf MaxDB - abbruch nach 3 Stunden.
Lösung:
Nicht count(*) benutzen, sondern
SELECT id FROM blabla WHERE client="blub"
Und dann über die ABAP-Funktion die Anzahl der Werte auslesen.
17 Minuten hat der Spaß gedauert.

Ich habe versucht das nachzuvollziehen, aber bin leider nicht auf die selben Ergebnisse gekommen wie du.
Die Tabelle mit der ich getestet habe ist AGR_HIERT (Texte zu Berechtigungen).
Das ABAP Coding sieht so aus:


DATA: lt_clnt TYPE TABLE OF MANDT.
DATA: lv_clnt TYPE MANDT.

* Variante 1
SELECT MANDT FROM AGR_HIERT
CLIENT SPECIFIED
INTO lv_clnt
WHERE MANDT = '800'.
ENDSELECT.

WRITE: / 'Anzahl Sätze:', sy-dbcnt.

* Variante 2
SELECT MANDT FROM AGR_HIERT
CLIENT SPECIFIED
INTO TABLE lt_clnt
WHERE MANDT = '800'.

WRITE: / 'Anzahl Sätze:', sy-dbcnt.

* Variante 3
SELECT COUNT(*) FROM AGR_HIERT
CLIENT SPECIFIED
WHERE MANDT = '800'.

WRITE: / 'Anzahl Sätze:', sy-dbcnt.
Varianten 1 und 2 sind bei mir sehr viel langsamer als Variante 3...

Die Datenbank ist Oracle 8.1.7.0.0 (das System ist ein 4.6C IDES)

Kannst du das Beispiel vielleicht bei dir mal nachvollziehen?

Gruß,

Gaert

Turbohummel
10-05-2007, 16:02
Genau so gemacht (Allerdings mit der BANF-Tabelle).
Die anderen Datenbanken hab ich nicht im Zugriff.

Vielleicht irgendein Konfigurationsfehler? Wenig Ram? (MaxDB scheint mir recht RAM-Lastig zu sein im Vergleich zu DB2 (Oracle hab ich nie getestet).

Hast du die Abfrage mal über ein Abfrage-Tool losgelassen? Nur so um zu testen. Vielleicht läuft der SQL-Wrapper irgendwie Amok.

Gaert
11-05-2007, 10:17
Hallo,

ich habe mal einen kleinen Report zum testen gebaut:



PARAMETERS P_TAB TYPE DD02l-TABNAME.

DATA: f1 TYPE INT4.
DATA: f2 TYPE INT4.

DATA: t1 TYPE INT4.
DATA: t2 TYPE INT4.

DATA: lv_time TYPE INT4.

DATA: TF2 TYPE TABLE OF mandt.


DO 5 TIMES.

GET RUN TIME FIELD f1.
SELECT MANDT FROM (P_TAB)
CLIENT SPECIFIED
INTO TABLE TF2
PACKAGE SIZE 1000000
WHERE MANDT <> '800'.
CLEAR TF2[].
ENDSELECT.
GET RUN TIME FIELD f2.

WRITE: / 'SELECT'.
lv_time = f2 - f1.
WRITE: / lv_time, 'msec'.

GET RUN TIME FIELD t1.
SELECT COUNT(*) FROM (P_TAB) CLIENT SPECIFIED WHERE MANDT <> '800'.
GET RUN TIME FIELD t2.

WRITE: / 'COUNT(*)'.
lv_time = t2 - t1.
WRITE: / lv_time, 'msec'.


ENDDO.

WRITE: / sy-dbcnt, 'Sätze'.
Die Ausgabe ist wie folgt... (Oracle)



SELECT
9.562.668 msec
COUNT(*)
997.379 msec
SELECT
2.496.473 msec
COUNT(*)
793.977 msec
SELECT
2.607.573 msec
COUNT(*)
796.406 msec
SELECT
2.588.213 msec
COUNT(*)
798.658 msec
SELECT
2.861.512 msec
COUNT(*)
848.646 msec
600.244 Sätze
Auf einem anderen System (MaxDB) gabs bei einer ähnlichen Tabelle folgendes Ergebnis:


SELECT
7.817.943 msec
COUNT(*)
7.576.127 msec
SELECT
9.258.830 msec
COUNT(*)
10033.309 msec
SELECT
7.908.668 msec
COUNT(*)
10525.332 msec
SELECT
8.287.411 msec
COUNT(*)
9.287.431 msec
SELECT
7.542.867 msec
COUNT(*)
8.780.092 msec
616.483 Sätze

Die Datenbanken arbeiten also grundsätzlich verschieden... interessant ist, dass die Hardware des MaxDB Systems vom Prozessor her eigentlich stärker ist (aber ein bisschen weniger RAM).

Die Zugriffszeiten bei MaxDB sind eigentlich alle schrott... bei Oracle lädt er die Tabelle wohl erstmal in den Speicher und greift dann je nach Verfahren unterschiedlich zu.

Gruß,

Gaert

Turbohummel
13-05-2007, 07:06
Prozessor ist bei großen Datenbanken nur selten das Nadelöhr. Das liegt im Raid btw im RAM. Richtig Spaß macht das bei MaxDB mit großen Tabellen erst so ab 8 Gig (meine dass wir das drin haben, bin mir nicht ganz sicher).

Unsere IT hat mir folgendes Buch empfohlen:
http://www.sap-press.de/katalog/buecher/titel/gp/titelID-1155
MaxDB soll in der Standard-Config wohl wirklich recht langsam sein, weil es alles von der Platte holt und nichts im Ram behält (so die IT), das lässt sich aber konfigurieren.

Jasper
13-05-2007, 12:41
Statement (CLIENT ist das Mandantenfeld - 1. Feld im Primärschlüssel):
SELECT COUNT(*) FROM tabelle WHERE CLIENT = '123'

Die Tabelle auf der ich das ausführe enthält ca. 50GB Daten (ohne Indizes).

Bei DB2 dauert die Abfrage ca. 5 Minuten
Bei Oracle ca. 35 - 45 Minuten
Bei MaxDB über 24 Stunden (danach musste ich abbrechen)


zu oracle:

in 35-45min liest Oracle auf halbwegs vernünftiger hardware (mein laptop reicht schon) die ganze 50g-tabelle, d.h. kein indexzugriff. wieviele zeilen ingesamt und wieviele davon fallen in die ergebnismenge? wieviele clients ingesamt in der tabelle? am besten mal ein histogramm mit 'select client, count(*) from tab group by client' erstellen.
nr.1 problem sind immer wieder fehlende oder falsche statistiken. verfügt die tabelle über aktuelle oder korrekte statistiken?

-j

Gaert
14-05-2007, 11:06
Hallo Zusammen,

vielen Dank für die Antworten.

@Turbohummel: Danke für den Tipp mit dem Buch - das kenne ich schon.
@Jasper: Ich nehme an, dass die Statistiken aktuell waren - in der Untersuchten Tabelle waren nur Daten aus einem Mandanten - es hätte aber durchaus möglich sein können, dass mehrere Mandanten drin sind.

Grundsätzlich: Ich habe keinen Zugriff auf die Konfiguration der Systeme und ich weiß auch im Vorfeld nicht wieviele Mandanten das System hat... Ich bin dafür zuständig, dass die Software bei unseren Kunden auf allen Konfigurationen und DBMS vernünftig läuft.

Vielleicht noch eine kurze Hintergrundinfo warum ich die Datensätze Zählen muss:
Es geht darum sehr viele Daten selektiv von einem System in ein anderes zu migrieren. Bevor die Daten kopiert werden, wird eine Simulation gestartet, welche die zu kopierenden Datensätze zählt.
Um den benötigten Speicherplatz auf dem Ziel zu berechnen brauche ich im Vorfeld eine Aussage wieviel Bytes ein Datensatz durchschnittlich pro Tabelle auf der Festplatte belegt (die Größe der Tabelle lese ich mir aus den Statistiken der Quelle und teile sie durch die Anzahl aller Datensätze in der Tabelle (Mandantenübergreifend)).
Zusätzlich benötige ich die Anzahl der Sätze im Mandanten als Entscheidungsgrundlage um festzulegen, ob eine Tabelle komplett oder in teilen kopiert werden soll.
D.h. ich muss zweimal zählen, einmal mandantenübergreifend und einmal mandantenabhängig...

Vielleicht fällt euch aber auch ein Trick ein, wie ich schneller herausfinde wieviele Bytes ein Datensatz bei Oracle, MaxDB oder DB2 wirklich auf der Festplatte belegt?

Jasper
14-05-2007, 23:04
Um den benötigten Speicherplatz auf dem Ziel zu berechnen brauche ich im Vorfeld eine Aussage wieviel Bytes ein Datensatz durchschnittlich pro Tabelle auf der Festplatte belegt (die Größe der Tabelle lese ich mir aus den Statistiken der Quelle und teile sie durch die Anzahl aller Datensätze in der Tabelle (Mandantenübergreifend)).


oracle:
anzahl der datensätze:
select num_rows from all_tab_statistics where owner=? and table_name=?

grösse der tabelle:
select a.blocks*b.block_size from all_tab_statistics a join b.dba_tablespaces on a.owner = b.owner and a.tablespace_name = b.tablespace_name where a.owner =? and a.table_name=?

durchschnittliche grösse eines datensatzes:
select avg_row_len from all_tab_statistics where owner=? and table_name=?

alle werte bedingen korrekte statistiken. am besten prüfen mit

select stale_stats from all_tab_statistics where owner=? and table_name=?
select monitoring from all_tables where owner=? and table_name=?
select last_analyzed from all_tab_statistics where owner=? and table_name=?

erstes sollte 'N' ergeben, zweiteres 'Y'. falls zweiteres 'N' ergibt, last_analyzed verwenden.



Zusätzlich benötige ich die Anzahl der Sätze im Mandanten als Entscheidungsgrundlage um festzulegen, ob eine Tabelle komplett oder in teilen kopiert werden soll.
D.h. ich muss zweimal zählen, einmal mandantenübergreifend und einmal mandantenabhängig...


einmal zählen reicht:

select client, count(*) from tabelle group by rollup(client)



Vielleicht fällt euch aber auch ein Trick ein, wie ich schneller herausfinde wieviele Bytes ein Datensatz bei Oracle, MaxDB oder DB2 wirklich auf der Festplatte belegt?

siehe oben: blocks*block_size/num_rows


-j

Gaert
15-05-2007, 11:17
Hallo Jasper,

erstmal vielen Dank... das mit dem ROLLUP ist eine sehr gute Idee!

Das Auslesen der Statistiken funktioniert auf der hier installierten Oracle 8 so leider nicht - ich nehme an, da heißen die Systemtabellen anders - irgendeine Idee?
Googeln hat leider nichts gebracht ich find immer nur Hilfen für 10g...

Gruß,

Gaert

Gaert
15-05-2007, 14:04
Habs gefunden... der View heißt hier DBA_ALL_TABLES statt all_tab_statistics.
Die Frage ist, gibts den unter Oracle 9 und 10 auch?

Gruß,

Gaert

Jasper
15-05-2007, 16:55
Habs gefunden... der View heißt hier DBA_ALL_TABLES statt all_tab_statistics.
Die Frage ist, gibts den unter Oracle 9 und 10 auch?


ja, gibt es. ob alle werte auch dort zu finden sind, sollte die doku klären können:

http://www.oracle.com/technology/documentation/index.html

-j