![]() |
Datenbank: MS SQL Server • Version: 2022 • Zugriff über: ADO
TAdoTable.Open() sprengt Arbeitsspeicherlimit
Hallo,
ich habe als Quereinsteiger ein großes altes Delphi-Programm übernommen, das über ADO auf Datenbanken aus je ca. 15 Tabellen zugreift. Ursprünglich war dieses Programm nur für Access-DBs gedacht, seit einigen Jahren kann es aber auch SQL Server. Die Tabellenstrukturen sind auf beiden Seiten identisch. Im Moment habe ich folgende Situation: • Ein Kunde möchte aus vielen kleinen Access-DBs einen einzigen großen Datenbestand auf einem SQL Server zusammenkopieren, der in einem Remote-Netzwerk "steht". • Das Umkopieren erfolgt in einem historisch gewachsenen Mix aus Delphi/DB-API-Zugriffen und SQL-Befehlen. Das ist ein Legacy-Code, den ich bisher nur unvollständig durchschaue. Der Showstopper ist nun folgender: • Durch das Zusammenkopieren haben die Tabellen auf dem SQL Server inzwischen derart viele Datensätze, dass allein schon das Öffnen mit TAdoTable.Open() seehr lange dauert und bei einer bestimmten Tabelle sogar mit der Meldung „Zuwenig Arbeitsspeicher“ abbricht. • Der PC selbst hat genug Arbeitsspeicher, es wird einfach die Obergrenze von 4 GB für 32-bit-Programme überschritten. • Ich habe schon versucht, den Cursor der Server-Datenbank von client- auf serverseitig umzustellen (clUseServer). Dieser Modus wird von dem DB-Provider auch nicht zurückgesetzt, wenn ich das Open() ausführe. Demnach habe ich also eine gültige Kombination aus Cursorlocation und Cursortype gewählt. • Aber auch mit dem serverseitigen Cursor wird erstmal ca. 15 Minuten lang versucht, die gesamte Tabelle ins RAM zu laden, und endet ebenfalls in diesem Abbruch wegen zuwenig Arbeitsspeicher. :-( Jetzt meine Fragen: • Ich hatte es so verstanden, dass mit einem „serverseitigen Cursor“ das Open() nicht mehr versucht, die ganze Tabelle auf den lokalen PC runterzuladen. Tut es aber :-( . Mache ich was falsch? Muss ich eventuell noch mehr umstellen? Irgendwas an der Connection? Oder braucht man doch einen dynamischen Cursor, statt "Keyset"? • Kann man das Open() noch irgendwie beschleunigen? • Was ist mit dieser neuen Schnittstelle, FireDAC? Macht die das grundlegend anders? • Eine Umstrukturierung der DB, kommt m.E. nicht in Frage, da dazu Hunderte von uralten Stellen angefasst werden müssten. Selbst wenn, würde es m.E. nur das Speicherproblem lösen, aber nicht das Geschwindigkeitsproblem. • Gleiches gilt m.E. für eine Umstellung auf 64 bit. Gruß Axel |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Bist Du sicher, dass das Programm die zur Verfügung stehenden 4 GB auch nutzt? Dafür muss im PE-Header das Large Address Aware Flag gesetzt sein, sonst nutzt es nur 2 GB.
Falls es nicht gesetzt ist, reicht es vielleicht ja schon, es zu setzen, um das akute Problem zu beheben? |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
Zur Kontrolle solltest du das auch mit einer TADOTable versuchen, nur um auszuschließen, dass andere Code-Sequenzen für das Laden der kompletten Tabelle in den Speicher verantwortlich sind. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Statt TADOTable TADOQuery nehmen. Scheint sich (zuweilen) etwas dynamischer zu verhalten.
Benötigst Du tatsächlich alle Daten der Tabelle immer an "einem Stück"? Was genau wird gemacht? Tabelle lesen und woanders schreiben? Wenn die Daten nicht an einem Stück benötigt werden, sondern das Kopieren von A nach B auch in mehreren Schritten hintereinander (durchaus in einer Transaktion) erfolgen kann, dann verarbeite die Daten in einer Schleife. Gibt es einen eindeutigen Schlüssel (z. B. ID = 1:n), dann nimm immer ca. 10000 Sätze, also ID von 1 bis 10000, dann ID von 10001 bis 20000, ... Die Menge kannst Du natürlich variieren, indem Du erstmal versuchst herauszufinden, bei wievielen Sätzen der Arbeitsspeicher voll ist. Davon nimmst Du dann (vorsichtshalber) etwa die Hälfte. Sieht irgendwie nach Gefrickel aus, hat aber in der Vergangenheit schon häufiger für Jahre (mit wachsenden Datenmengen) funktioniert. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
|
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
|
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Wenn irgendwas "zusammenhängenden" Speicher benötigt, dann muß eben auch so ein großer Block aktuell im virtuellen Speicher frei sein.
Im Extremfall kann wird das schon bei durchschnittlich 300 bis 700 MB der Fall sein. z.B. ein Programm belegt aktull nur 10 MB Arbeitrspeicher, aber der Speicher ist extrem fragmentiert, also die paar MB sind schön verteilt und auch die DLLs liegen kreuz und quer überall rum, dann kann es bereits so sein, dass in den möglichen 4GB nichtmal mehr 100 MB zusammengängend frei sind, also es bereits knallt, obwohl nur 110 MB benötigt würden. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
In der Regel ist es kein Problem, mit FireDAC auf Datenbanken mit Millionen von Datensätzen zuzugreifen, auch wenn nicht alle Datensätze in den Hauptspeicher des Client passen würden. Es kommt halt darauf an, wie man es verwendet. Mit den Standardeinstellungen ist das Öffnen einer TFDQuery/TFDTable überhaupt kein Problem, egal wie viele Datensätze es gibt. Ohne die Datenbank allgemein zur Verfügung zustellen bist du aber der einzige hier der das prüfen kann. Wenn das reine Öffnen in einem Testprogramm sowohl mit FireDAC als auch ADO funktioniert, dann liegt das Problem eh woanders und bedarf einer tieferen Analyse der auslösenden Vorgänge. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
Zitat:
Zitat:
Was ich mir noch an Alternativen überlegt habe, um um das .Open() herumzukommen: - Aus den Access-DBs auf den SQL Server nicht direkt in die riesigen Originaltabellen kopieren, sondern erstmal in leere Dummy-Zieltabellen mit derselben Struktur, denn dann geht das .Open() ja schnell. Dann ein SQL-MERGE-Kommando absetzen, das jeweils die Dummytabelle in die Originaltabelle ein"merged" (oder Kombination aus INSERT und UPDATE). Dann ist ADO außen vor und der Server macht die Arbeit selbst. Allerdings müsste ich dann die Identitätsprüfung der Zeilen in SQL selbst formulieren, das dürfte auch haarig werden. - Ginge sowas auch mit Stored Procedures? Nur eine vage Idee. Zitat:
|
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
SQL-Code:
oder
update or insert
SQL-Code:
anbieten
merge
|
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
Notfalls kann man aber auch eigene INSERT/EDIT/DELETE-Statements angeben, wenn es gewisse Sonderfälle erfordern. Eine Query mit
Delphi-Quellcode:
entspricht der Table.
SELECT * FROM deinetablle
Lädt also auch ALLES. Teilweise kann man aber auch ein Nachladen/Paging aktieren (wenn DBMS, Treiber und QueryKomponente es ermöglichen) Dann wird nicht sofort alles geladen, sondern erst nach und nach, während man mit Next durchläuft, bzw. es wird immer nur ein kleiner Ausschnitt (Page) geladen und dann die Nächste und wieder freigeben. z.B. bei Grids muß natürlich auch das Grid und sein DataControler es unterstützen, wenn man quasi nur den grade sichbaren Bereich geladen haben will. Bei der Query kann man aber noch die Felder/Spalten auswählen/einschränken und vor allem auch die Records/Zeilen, durch ein entsprechendes WHERE und/oder LIMIT. Außerdem kann man JOINs einbinden und vieles mehr. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
Verarbeitungsmengen so wählen, dass der physikalische Speicher immer ausreicht, dann kann 1000 Mal ein Tausendstel durchaus schneller sein, als einmal alles, was ggfls. dann auch nur nach (gefühlten) Stunden mit 'ner Fehlermeldung scheitert. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
|
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Ich würde das überhaupt nicht mit Delphi lösen, sondern auf dem SQL-Server einen Zugriff auf die Access-Datenbank einrichten. Anschließend wird der Abgleich auf dem SQL-Server gemacht. Dafür bietet sich eine Stored Procedure an. Das dürfte um ein Vielfaches schneller sein und um die Speicherverwaltung kümmert sich SQL-Server.
Allerdings setzt das Kenntnisse in SQL voraus. Falls die bei dir nicht ausreichend sind, solltest du dafür jemanden beauftragen. Das spart vermutlich dennoch mehr Zeit und Kosten ein, als du jetzt für den Umbau in Delphi investieren musst. Ich gehe aber davon aus, dass eine Firma, die einen SQL-Server betreibt, auch jemanden hat, der über ausreichend SQL-Kenntnisse verfügt. Somit blieben die Kosten zumindest im Haus. Eine Stored Procedure auf dem SQL-Server kannst du dann auch mit Delphi starten. |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Liste der Anhänge anzeigen (Anzahl: 1)
Moin...8-)
Zitat:
![]() * Filter einrichten - jeder Abonnent bekommt nur das was er sehen darf :lol: * Die komplette Replikation kann als Script gespeichert werden zum Neuaufbau der Replikation. * Wir machen die Synchronierung der Daten zwischen den Filialen damit. Die Niederlassung schreibt was lokal rein, in "sekundenschnelle" ist die Information in der "Zentrale"... * Wenn mal ein Abonnent "kaputt" ist, dann reorganisiert die Replikation die komplette Datenbank aus dem Verleger neu. :thumb: |
AW: TAdoTable.Open() sprengt Arbeitsspeicherlimit
Sorry, dass ich so lange nicht geantwortet habe. Erstmal danke für die vielen Antworten und Informationen. :-)
Zitat:
Dafür dauert dann das .Locate() viel länger. Wenn in einer Query zu vielen Datensätzen positioniert werden muss, wartet man dann eben auch lange. Ich glaube aber, ich kann einen guten Kompromiss finden, bei welchen Tabellen ich mit clUseServer arbeite und bei welchen besser mit clUseClient. :-) Auch zu der Variante mit SQL "Merge" habe ich experimentiert. Das Fiese dabei ist ja, dass das nicht zwischen verschiedenen Datenbanken geht, die einzumergenden Daten müssen also schon in der Ziel-DB sein. Das kriege ich zwar mit einer leeren Puffertabelle mit derselben Struktur hin und mache dann das Merge zwischen der Puffertabelle und der Zieltabelle. Das ist dann wirklich sensationell schnell :shock: . Aber anschließend sind die Datensätze der Zieltabelle nicht mit den anderen Ziel-DB-Tabellen verknüpft, weil die Datensätze dort ja andere IDs haben. Statt eines einzigen "Merge" müsste ich dann wohl jeden Datensatz einzeln mergen :pale: und dabei die Ziel-IDs der anderen Tabellen als Parameter angeben, oder wie? Und die Puffertabelle(n) ist/sind ja auch nicht gerade elegant. Zitat:
Zitat:
Zitat:
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:48 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz