[Unlösbar] RecordReference Performanceproblem

11. August 2008 15:13

Hallo zusammen,

ich habe eine Funktion die per RecordReference die Daten von frei definierbaren Tabellen in einen anderen Mandanten kopiert.
Bei einer Tabelle mit ca. 40000 Sätzen (in 89 Feldern, davon 40 FlowFields) bricht ab ca. 3500 kopierten Datensätzen die Performance ein. Ab ca. 8000 Datensätzen werden nur noch max. 2 Datensätze pro Minute kopiert.
Die Funktion verarbeitet nur Felder der Feldklasse Normal.

Datenbank: 8 GB (native), zu 40 % belegt, 800 MB DBMS Cache, Commit Cache aktiviert.

Habt Ihr eine Idee wie man das Kopieren beschleunigen kann? Ein Umstieg auf SQL-Server ist nicht möglich.

Danke

42
Zuletzt geändert von 42 am 12. August 2008 20:36, insgesamt 2-mal geändert.

Re: RecordReference Performanceproblem

11. August 2008 16:25

Hallo

Das ist ein altbekanntes Problem. Die Recordefs werden ab einer bestimmten Datenmenge immer wie langsamer.
Die genau gleiche Verarbeitung "Mandant kopieren" habe ich auch bei mehreren Kunden im Einsatz und es ist bei allen das gleiche Verhalten zu beobachten.
Ich habe alles probiert clear usw. -> Nichts, keine Besserung. Müssen wir wohl damit leben.

Gruss

Re: RecordReference Performanceproblem

11. August 2008 22:36

Mögliche Ursache: Während einer Transaktion wächst im Temp-Verzeichnis eine Tempdatei mit an.

Versuche doch mal bitte auf einer Test-DB, ob folgender Tipp etwas bringt:

Sofern mit der Business-Logik vereinbar, könntest du die Transaktion in mehrere kleinere Transaktionen unterteilen (z. B statt eine Transaktion mit 40.000 Datensätzen machst du daraus 80 Transaktionen á 500 Datensätze).
Dazu lässt du einen einfachen Zähler mitlaufen, der mit jedem verarbeiteten Datensatz um 1 erhöht wird (solltest du eine Fortschrittanzeige mit Prozent-Anzeige mitlaufen lassen, solltest du so einen Zähler bereits haben).
Nun baust du innerhalb deiner Schleife (REPEAT..UNTIL) folgenden Zweizeiler ein:
Code:
IF Zähler MOD 500 = 0 THEN
  COMMIT;

Hierdurch wird die Transaktion beendet und mit dem nächsten INSERT wird implizit eine neue Transaktion eröffnet.

Wichtig:
Bitte prüfe unbedingt, ob die Transaktion in mehrere kleinere aufgeteilt werden kann/darf!
Führe diese Änderung zuerst in einer Test-Datenbank durch!


Apropos Fortschrittanzeige: Jedes Dialog.UPDATE frisst unglaublich Performance, von daher kann man gerade hier einiges an Performance herausholen.
Der Klassiker:
Code:
Window.OPEN(...);
NoOfRecs := Rec.COUNT;
CurrRec := 0;
IF Rec.FINDSET THEN BEGIN
  Window.UPDATE(1,Primärschlüssel);
  Window.UPDATE(2,ROUND(CurrRec / NoOfRecs * 10000,1));
  [...]
UNTIL Rec.NEXT = 0;

Bei 40.000 zu verarbeitenden Datensätzen würde die Fortschrittsanzeige 80.000 mal aktualisiert.
Da eine Prozentanzeige nur 101 verschiedene Werte anzeigen kann (von 0% bis 100%), wird der Dialog genau 79899 mal zu oft aktualisiert.
Abhilfe schafft auf die schnelle schonmal folgender Code:
Code:
Window.OPEN(...);
NoOfRecs := Rec.COUNT;
CurrRec := 0;
LastTime := TIME;
IF Rec.FINDSET THEN BEGIN
  IF NoOfRecs <= 100 THEN BEGIN
    Window.UPDATE(1,Primärschlüssel);
    Window.UPDATE(2,ROUND(CurrRec / NoOfRecs * 10000,1));
  END ELSE BEGIN
    IF (TIME - LastTime) > 1000 THEN BEGIN
      Window.UPDATE(1,Primärschlüssel);
      LastTime := TIME;
    END;
    IF (CurrRec MOD (NoOfRecs DIV 100) = 0) THEN
      Window.UPDATE(2,ROUND(CurrRec / NoOfRecs * 10000,1));
  END;
  [...]
UNTIL Rec.NEXT = 0;

Resultat: Das Dialogfenster wird nur noch im Sekundentakt bzw. wenn sich die Prozentanzeige tatsächlich ändert aktualisiert.

Re: RecordReference Performanceproblem

12. August 2008 09:51

Hallo

Die Tipps von Timo sind gut, bringen bei den RecordRefs aber nichts.
Es sind nicht die Dialoge die die Verarbeitung langsam machen.
Ein Commit bringt auch nichts. Habe ich alles versucht.
Ich habe den Eindruck, dass die RecordRefs irgendwie allozierten Speicher nicht mehr freigeben.
Komischerweise bringt auch ein Clear nichts.

:-(

Gruss

Re: RecordReference Performanceproblem

12. August 2008 13:34

Hallo zusammen,

martinst hat Recht, ein commit bringt bei Referenzen leider keinen Erfolgt. Das Phänomen hatte ich auch schon beobachtet.

Mir ist auch aufgefallen, dass der Performanceverlust von der Tabellenstruktur abhängt - bei manchen Tabellen habe ich trotz
mehrerer 100000 Datensätze nicht solche Performanceprobleme.
Ich habe jetzt FlowFields und FlowFilter von der Übertragung ausgeschlossen, auf einer Single User Datenbank läuft das Programm jetzt auch recht vernünftig durch aber sobald weitere User online gehen ist es wieder vorbei.

Der Code meiner Funktion sieht folgendermaßen aus, vielleicht habt Ihr ja noch einen Verbesserungsvorschlag

Code:
RecordRefQuelle.OPEN(ObjectID,FALSE,TxtQuellmandant);

IF RecordRefQuelle.FIND('-') THEN BEGIN
  RecordRefZiel.OPEN(ObjectID,FALSE,TxtZielmandant);
 
  Dlg.UPDATE(1, RecordRefQuelle.COUNT);
  LocIntSatzZaehler := 0;

  REPEAT
    IntSatzZaehler += 1;

    IF IntSatzZaehler MOD 50 = 0 then
      GDlg.UPDATE(2, IntSatzZaehler);

    RecordRefZiel.INIT;

    FOR IntZaehler := 1 TO RecordRefQuelle.FIELDCOUNT DO BEGIN
      CLEAR(FieldRefQuelle);
      CLEAR(FieldRefZiel);

      FieldRefQuelle := RecordRefQuelle.FIELDINDEX(IntZaehler);
      IF (FORMAT(FieldRefQuelle.CLASS) = 'Normal') AND
         (FieldRefQuelle.ACTIVE) THEN BEGIN

        FieldRefZiel := RecordRefZiel.FIELDINDEX(IntZaehler);

        IF (FORMAT(FieldRefQuelle.TYPE) = 'TableFilter') OR (FORMAT(FieldRefQuelle.TYPE) = 'BLOB') THEN BEGIN
          EVALUATE(FieldRefZiel,FORMAT(FieldRefQuelle.VALUE));
        END ELSE BEGIN
          FieldRefZiel.VALUE := FieldRefQuelle.VALUE;
        END;
      END;
    END; 

    IF NOT RecordRefZiel.INSERT THEN
      RecordRefZiel.MODIFY;
    IntCommitZaehler += 1;

    IF IntCommitZaehler Mod 500 = 0 THEN
      COMMIT;
  UNTIL RecordRefQuelle.NEXT = 0;

  RecordRefZiel.CLOSE();
END;
RecordRefQuelle.CLOSE();


Edit:
Als Alternative fällt mir momentan eigentlich nur der unelegante Weg ein, bei Tabellen die Performanceprobleme verursachen die Datenübertragung über Recordvariablen zu realisieren - aber das war ja genau das was ich vermeiden wollte.

Gruß

42

Re: RecordReference Performanceproblem

12. August 2008 14:27

Hallo

Die Idee mit den BLOB bzw. Flowfields hatte ich bereits getestet.
Bringt aber auch nichts.

Bei grossen Datenmengen wird das Kopieren mit RecRef einfach extrem langsam.
Wir haben dies auch beim CH-Upgrade Toolkit gemerkt.
Dort werden die Daten auch mit RecRef kopiert. Was zum programmieren elegant ist, dauert beim Kunden Tage um die Daten zu kopieren.

Ich denke, dass müsste Microsoft etwas machen. Wird aber wohl nicht priorität haben :-(

Gruss

Re: [Unlösbar] RecordReference Performanceproblem

1. Oktober 2008 16:05

Doch lösbar!

Habe das gleiche Problem, ich habe mir einen Report gebastelt, mit dem ich komfortabel über RecordRef und FieldRef Tables auslesen kann. Soweit so gut dachte ich, funktioniert prima. Aber bei großen Tables kam dann die Ernüchterung, die CPU-Auslastung erreichte permanent 100% wie oben beschrieben.

Ich habe dann tausend Sachen ausprobiert, aber nix brachte was (siehe oben). Dann habe ich folgendes gemacht: ich habe den Teil, der die Fields abarbeitet in ein Codeunit gepackt, dem ich die RecRef-Variable übergebe. Das hats dann endlich gebracht!!!

Im Report:
Code:
// Sätze ausgeben
IF RecRef.FIND('-') THEN REPEAT;
  Fenster.UPDATE(1,SatzNummer);
  XmlSchreiben.FelderSchreiben(RecRef,Datei);
UNTIL RecRef.NEXT = 0;


Das Codeunit XmlSchreiben:
Code:
FelderSchreiben(RecRef : RecordRef;VAR Datei : File)
// Felder
FOR I := 1 TO RecRef.FIELDCOUNT DO BEGIN
  Feld := RecRef.FIELDINDEX(I);
  IF HoleFeldWert(Feld,FeldWert) THEN
    WriteXmlFeld('F'+FORMAT(Feld.NUMBER),FeldWert,Datei);
END;

Re: [Unlösbar] RecordReference Performanceproblem

1. Oktober 2008 16:14

Habs ausprobiert, es geht auch, wenn man kein separates Codeunit benutzt, sondern im Report eine Funktion baut, der man die RecordRef übergibt. Wichtig ist dabei nur, dass die Recordref als lokale Variable an die Funktion übergeben wird.

Re: [Unlösbar] RecordReference Performanceproblem

1. Oktober 2008 16:16

Bravo :-D :-D :-D :-D

Rein Memorymässig scheint das Problem tatsächlich behoben.
Die Performance überzeugt mich aber immer noch nicht.

Trotzdem vielen Dank