Laden...

SQL-Compact: InsertData: Für diesen Vorgang ist nicht genügend Speicher verfügbar.

Erstellt von 10110010 vor 10 Jahren Letzter Beitrag vor 10 Jahren 3.460 Views
1
10110010 Themenstarter:in
7 Beiträge seit 2013
vor 10 Jahren
SQL-Compact: InsertData: Für diesen Vorgang ist nicht genügend Speicher verfügbar.

Hallöle Leute,

ich sitze da vor ein Paar Tagen vor einem Problem.
Ich habe da eine Anwendung die meine Festplatte Systematisch durchläuft und von jeder Datei einen Hashcode (mittels MD5) bildet. Dieser wird in eine SQL-Compact Datenbank eingetragen.
Sollte dabei Duplikate festgestellt werden, wird dies über ei Dialog mitgeteilt.
Die Anwendung läuft soweit einwandfrei, aber wenn der Prozess die 180MB erreicht, dann bekomme ich diese Exception:

System.Data.SqlServerCe.SqlCeException ist aufgetreten.
HResult=-2146233087
Message=Für diesen Vorgang ist nicht genügend Speicher verfügbar.
Source=SQL Server Compact ADO.NET Data Provider
NativeError=0
StackTrace:
bei Storage.DataProvider.DataProvider.InsertData(FileInfo file, String hash)
bei Duplicate.UiContext.FileChecker_FileItemChecked(Object sender, FileItemCheckEventArgs e)
InnerException:

Ich verstehe nicht, warum hier ein Problem auftaucht!

In der Datenbank befinden sich derzeit 11852 Zeilen und sie ist 3.988 KB groß.

Beim Zugriff auf die Datenbank wird vor jeder Abfrage Connected und nach der Abfrage wieder Disconnected.
Die Verarbeitung der Dateien läuft über einen anderen Thread über den ThreadPool.

Ich hoffe hier kann jemand helfen.

Gruß
10110010

C
2.122 Beiträge seit 2010
vor 10 Jahren

Könnten die 180 MB eine Obergrenze für SQL Compact sein? Oder es hängt an was anderem als an dieser Größe.
Machst du irgendwas nicht zu? Tritt es immer bei der selben Anzahl Dateien auf?

Ich würde hier verschiedenes versuchen. Zuerst nicht immer auf/zumachen, sondern Connection offen lassen und alles auf einmal reinschieben. Gehts dann?
Was ist die "Verarbeitung"? Ist das was separates? Dann lass das mal weg, vielleicht hat das noch einen Nebeneffekt.

F
10.010 Beiträge seit 2004
vor 10 Jahren

Nein, 180MB ist nicht die Obergrenze, aber es kann im Gegenteil genau das sein was Du vorschlägst, nämlich das eine offene Connection oder eine Transaction zu soetwas führt.

Auch z.b. Firebird macht das selbe wenn man mehr als ca 100.000 am stück per ADO.NET einfügt.

@B2:
Wie machst du das Insert und das suchen?

1
10110010 Themenstarter:in
7 Beiträge seit 2013
vor 10 Jahren

Hallöle,

das Insert wird so durchgeführt:

public FileData InsertData(System.IO.FileInfo file, string hash)
        {
            if (this.GetFileData(file) == null)
            {
                Storage.DataProvider.DB dataProvider = new Storage.DataProvider.DB();
                try
                {
                    dataProvider.SQL = "insert into Data (Directory, Filename, LastUpdate, Hash, [Exist]) values (@Directory, @Filename, @LastUpdate, @Hash, @Exist)";
                    dataProvider.Parameters.Add(new DbParameterHelper("@Directory", System.Data.DbType.String, file.DirectoryName));
                    dataProvider.Parameters.Add(new DbParameterHelper("@Filename", System.Data.DbType.String, file.Name));
                    dataProvider.Parameters.Add(new DbParameterHelper("@LastUpdate", System.Data.DbType.DateTime, file.LastWriteTimeUtc));
                    dataProvider.Parameters.Add(new DbParameterHelper("@Hash", System.Data.DbType.String, hash));
                    dataProvider.Parameters.Add(new DbParameterHelper("@Exist", System.Data.DbType.Boolean, 1));
                    dataProvider.Connect(this.ConnectionString, this.Provider);
                    dataProvider.ExecuteNoneQuery();
                }
                catch (System.Exception ex)
                {
                    throw ex;
                }
                finally
                {
                    dataProvider.Disconnect();
                }
            }
            return this.GetFileData(file);
        }

Die Verarbeitung sieht so aus, dass ich mit

foreach (DirectoryInfo item in directory.Directory.GetDirectories("*", SearchOption.TopDirectoryOnly))

die Verzeichnisse durchlaufe und rekusif zum untersten gehe.
von dort aus werden alle Files innerhalb der Ordner geladen und der Hashwert gebildet. dieser wird dann in die Datenbank geschrieben.
Anschließend wird geprüft, ob dieser Hashwert mehrmals in der Datenbank auftaucht. Wenn ja, dann werden alle Infos dieser Dateien in einem Dialog angezeigt.

Da diese Verarbeitung in einem eigenen Thread abläuft, finden hier viele Invokes statt. Ich denke aber, dass das keine Probleme machen sollte.

Im Großen und Ganzen war es das.

Ich werde mal nachsehen, ob es immer die selbe Datei ist, und wie viele Connections insgesamt auf gemacht wurden.

Gruß
10110010

W
955 Beiträge seit 2010
vor 10 Jahren

Hi,

der dataProvider verwendet doch bestimmt. IDisposable. Dann lass doch das try catch finally weg und nimm using().
Ansonsten mal mit einem MemoryProfiler nachschauen.

1
10110010 Themenstarter:in
7 Beiträge seit 2013
vor 10 Jahren

Hallöle,

habe das mit dem Dispose mal versucht.
Leider ohne erfolg.
Da auf die Datenbank Asynchron zugegriffen wird, kann ich nicht die Verbindung ein mal öffnen und erst beim Beenden der Anwendung schließen.

habe mal nachgesehen wieviele connections hergestellt werden.
es sind immer unterschiedliche.
mal bei 88501, dann bei 88561, 88454 und 88476.

es sind auch immer andere Files.
Noch jemand ideen?

gruß
10110010

F
115 Beiträge seit 2012
vor 10 Jahren

Hi,

ich würde in's Blaue hinein einfach mal alle 1000 Inserts ein Commit machen.

http://msdn.microsoft.com/en-us/library/esdw1h9d(v=vs.100).aspx

Gruß
f_igy

W
955 Beiträge seit 2010
vor 10 Jahren

...aber warum hast Du denn da 88000 Connections? Ich hätte jetzt an eine Consumer/Producer-Queue etc gedacht, wo einige Tasks ihre Dateisystem-Ergebnisse reinstellen, die dann von wenigen/einen Schreiber abgearbeitet werden, jeder mit einer dedizierten Connection?
Wir können Dir da nicht weiter helfen da wir zuwenig von der Architektur wissen.

F
10.010 Beiträge seit 2004
vor 10 Jahren

Das ist der komplett falsche Ansatz.

Wenn man so etwas so "einfach" parallelisiert, wird es selten gut.

Erstelle dir eine Queue die die Daten entgegen nimmt und arbeite diese ab.
Der SqlServer Compact ist eh nicht für sowas wirklich ausgelegt.

16.842 Beiträge seit 2008
vor 10 Jahren

Das was Du da machst ist doch nichts anderes als eine Metadatenbank.
Nicht nur, dass Dein Ansatz völlig Käse ist (jedes File eine Connection) ist auch die Umgebung meiner Meinung nach "völlig"/ziemlich ungeeignet.
Es gibt hier spezialisierte Umgebungen, wie MongoDB GridFS, die Datei und Metdaten aus Anwendungssicht gemeinsam, auf Dateiebene aber getrennt halten (verwende ich auch in zwei Projekten sehr erfolgreich).

Zur Not belass es bei SQLite / SqlCe, aber vergewaltige doch die Datenschicht nicht so.

1
10110010 Themenstarter:in
7 Beiträge seit 2013
vor 10 Jahren

Hallöle Leute,

erstmal vielen Dank für eure Einwände.
Habe jetzt mal einige Änderungen vorgenommen.
Alle Datenbankanfragen werden jetzt in einen Stapel abgelegt und dieser von einem eigenen Thread verarbeitet.
In diesem wird dann auch nur zu beginn die Connection aufgebaut und beim beenden erst geschlossen.

Das ist auch schon mal besser.
Mein Memory-Leak habe ich auch gefunden.
die Threads mussten aktualisierungen des UIs vornehmen.
Dafür habe ich in den Jeweiligen Klassen ein Control erzeugt, das nur für den Invoke zuständig war. Beim Dispose habe ich das Handle nicht zerstört. 😕
Jetzt ist das super.

Vielen Dank nochmal für eure Hilfe.

Gruß
10110010

F
10.010 Beiträge seit 2004
vor 10 Jahren

Dafür habe ich in den Jeweiligen Klassen ein Control erzeugt, das nur für den Invoke zuständig war.

Das ist auch der falsche Ansatz.

1
10110010 Themenstarter:in
7 Beiträge seit 2013
vor 10 Jahren

Hallöle,

Welcher wäre den der richtige?
Möchte ja dazulernen.

Gruß
10110010