Announcement

Collapse
No announcement yet.

BLOB, Komprimierung,

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • BLOB, Komprimierung,

    Hallo zusammen!

    Spezial-Frage zum Blob-Datentyp:
    Ich programmiere eine Anwendung, über welche man Dokumente
    verwalten kann. Das Szenario ist folgendes:

    .) Bei Auswahl eines Dokumentes wird dieses
    über Sockets vom Client auf einen Server übertragen und an
    einem zentralen Platz gespeichert. Der Pfad zur Datei
    wird dabei in der Datenbank in ein Feld geschrieben.
    .) Will der Benutzer die Datei ansehen, wird wieder über
    Sockets die Datei vom Server zum Client übertragen.
    Die Information, wo die Datei am Server liegt, wird aus der
    Datenbank gelesen.

    Nun zur Frage:
    1.) Welche Vorteile/Nachteile habe ich, wenn ich die Datei
    nicht "extern" speichere (also als Datei), sondern in
    ein Blob-Feld direkt in die Datenbank?
    2.) Wie hoch ist dabei die Komprimierung in den Fällen
    .) Datei ist eine "EXE"
    .) Datei ist eine "ZIP"
    .) Datei ist ein BMP
    .) Datei ist ein JPG
    Werden die Daten bereits am Client komprimiert und dann
    beim Commit bereits komprimiert zum Server geschickt oder
    erfolgt die Komprimierung erst beim Server?
    3.) Welche Auswirkungen habe ich bei Dateien mit einer Grösse von
    z.B. 50MB bzw. bei sagen wir mal 1000 Dateien mit je 50MB
    bzw. bei 100000 Dateien mit je 0,5MB
    4.) Wie sieht es mit der Belastung am Server aus?

    Wenn ich diesen Weg einschlagen sollte, dann würde ich für diesen
    Zweck eine eigene Datenbank verwenden. Hätte dies einen Sinn?

    Ich bitte Euch um Hilfe!

    mfg.
    Schardl Robert

  • #2
    Hallo,

    zu Frage 1: <br>
    Wenn alle Daten direkt in der DB gespeichert werden, ist <br>
    a) das Backup einfacher, <br>
    b) die Zugriffssicherheit zentral über die DB-Rechte steuerbar,<br>
    c) die Datensicherheit besser, da niemand eine Datei mal auf die Schnelle über den Windows-Explorer löschen kann, und <br>
    d) jeder Zugriff über Transaktionen kontrollierbar.

    zu Frage 2:<br>
    Wenn Delphi oder BDB verwendet wird, steht die OPEN SOURCE-Komprimierung über ZLIB zur Verfügung. Dort kann man zwischen schnellster und maximaler Komprimierung wählen. Der Komprimierungsfaktor entspricht den Werten, die bei ZIP üblich sind.
    Die Daten werden bereits beim Client komprimiert und nur in komprimierter Form zum Server geschickt (etwas anderes macht in der Regel auch keinen Sinn).

    zu Frage 3: <br>
    Bei derart grossen Dateien (50 MB) würde ich die Daten <b>nicht</b> in der Datenbank speichern, sondern dort nur einen Verweis auf den Zugriffspfad über das Dateisystem unterbringen. Auch unter NT/2000 kann man ganze Verzeichnis als komprimiert kennzeichnen, so dass die Dateien völlig transparent in komprimierter Form abgelegt werden

    Comment


    • #3
      Sehr geehrter Herr Andreas Kosch!

      Vielen Dank für die Antwort!
      Eine Frage noch:
      Da sehr viele Dateien in diese Datenbank aufgenommen werden
      (es könnten u.U. 100.000 Dateien oder mehr sein),
      wo liegt die Grenze, aus Ihrer Erfahrung gesehen?
      UND vor allem -> hätte es einen Vorteil, dies in einer
      eigenen Datenbank zu realisieren?

      Vielen Dank.
      Robert Schard

      Comment


      • #4
        Hallo,

        wenn es 100 000 Datensätz je 0,5 MByte (also im Durchschnitt ca. 0,25 MB komprimiert) wären, könnte man das Ganze in einem simulierten Test einmal ausprobieren. Wenn ich dann allerdings die zu erwartende Datenbankgröße betrachte, bekomme ich beim InterBase etwas Bauchschmerzen. Es wird eine Größe erreicht, bei der ich dann doch eher zum MS SQL Server 2000 oder zu ORACLE greifen würde. Zumal man bei diesen Datenbanken zusätzliche Datenbankdateien definieren kann, in die automatisch alle BLOb- und Memo-Daten ausgelagert werden, um die Zugriffsperformance auf die normalen Daten nicht zu beeinträchtigen.

        Der grosse Vorteil der Integration der BLObs in der Datenbank besteht darin, alles über Transaktionen kontrollieren zu können (bei den externen Dateien ist das nicht der Fall). Wenn man beim InterBase zwei separate Datenbanken verwendet, müsste man dann zum 2-Phasen-Commit übergeben. In Abhängigkeit vom Zugriffsweg (BDE, ODBC, ADO oder IBX) bedeutet dies mehr oder weniger Mehraufwand

        Comment


        • #5
          Sehr geehrter Herr Andreas Kosch!

          Nochmals Danke für Ihre Antwort!

          Bei welcher Datenbankgrösse bei Interbase
          bekommen Sie "Bauchschmerzen"?

          Mein aktueller Anwendungsfall ist das Speichern von
          EMail-Attachments in eine Datenbank.
          Die Useranzahl beträgt 100 aufwärts.

          Ich will nicht alle Attachments in die Datenbank aufnehmen.
          Das Hauptkriterium für die Aufnahme des Attachments
          in die Datenbank ist die Grösse der Datei. Wenn diese
          einen Wert "x" überschreitet, wird diese ganz "normal"
          im Dateisystem abgespeichert.
          Ein 2. Kriterium ist das Datum der Datei für eine
          Autoarchivierung. Alle Dateien älter als y-Tage werden
          wieder in das Dateisystem gespeichert (u.z. komprimiert)
          und aus der Datenbank gelöscht.

          Gibt es dazu irgenwelche Erfahrungswerte bzw.
          wo kann ich mich im Internet ein wenig umsehen?

          Mit bestem Dank.
          Robert Schard

          Comment


          • #6
            Hallo,

            ich werde das Gefühl nicht los, dass mich hier jemand auf eine konkrete Aussage "festnageln" will. Und hinterher bin ich Schuld, wenn es trotzdem in die Hose geht ;-)

            Wenn es um eMail-Attachments für verschiedene Benutzer geht, ist das ein Bereich, der sich gut horizontal partitionieren lässt. Man könnte also für die Anfangsbuchstaben A...E die 1. Datenbank und für F...J die 2. Datenbank usw. verwenden. Wenn die Datenbanken unter 2 GByte bleiben, sollte das einen Versuch wert sein (zumal die Zugriffshäufigkeit bei eMails entsprechend niedrig sein wird).

            Ich würde daher an Ihrer Stelle ein Testprogramm schreiben, das eine entsprechende Last simuliert und mehrere Instanzen von diesem Programm parallel ausführen. Wenn dann der komplette Betrieb (inklusive Backup/Restore) als Simulation einige Tage stabil läuft, spricht nichts gegen den InterBase. <br>
            (<i>P.S: Es gab auch technische Gründe, warum Borland das InterBase 6-Projekt terminiert hat, der Server hat immer noch seine Macken. Daher wird nur ein Test unter Last letzte Gewissheit schaffen, wobei man sich am Ende die Logdatei »interbase.log« genauer anschauen sollte</i>)

            Comment


            • #7
              Sehr geehrter Herr Andreas Kosch!

              Vielen Dank für Ihre Antwort!

              Ich will Sie sicher nicht "festnageln", denn die
              entgültige Entscheidung muss ich fällen
              und damit muss ich auch alle Konsequenzen tragen. :-)

              Mir geht es nur um Erfahrungswerte.
              Ich habe Erfahrung mit dem SQL-Server, weil Sie "den"
              im letzten Kommentar angeschnitten haben. Und da zeigt sich,
              dass zumindest die vorletzte Version sehr instabil ist und
              bei einem Crash gleich die ganze Datenbank flöten geht.

              Ich kenne z.B. eine Firma, welche Interbase einsetzt,
              seit 5 Jahren, und seit 5 Jahren gab es noch KEINEN
              Crash, keine defekte Datenbank, obwohl die Datenbank(en)
              sehr sehr stark belastet ist(sind).

              Bei Umstellung und Umprogrammierung auf den SQL-Server
              gab es nur mehr Probleme. Ergebnis: wieder alles zurück
              auf den Interbase -> seither keine Probleme.

              Ich bedanke mich für den sehr hilfreichen Gedankenaustausch.

              mfg.
              Robert Schard

              Comment


              • #8
                Auf der Borland Homepage?

                z.B.

                INTERBASE TECHNICAL SPECIFICATIONS Database Statistics (Upper Limits)

                Maximum size of database: 32TB using multiple files; largest recorded InterBase database in production is over 200GB

                Maximum size of one file: 4GB on most platforms; 2GB on some platforms

                Maximum number of tables: 64K Tables

                Maximum size of one table: 32TB

                Maximum number of rows per table: 4G Rows

                Maximum row size: 64KB

                Maximum number of columns per table: Depends on the datatypes you use. (Example: 16,384 INTEGER (4 byte) values per row.)

                Maximum number of indexes per table: 64K indexes

                Maximum number of indexes per database: 4G indexe

                Comment


                • #9
                  hallo,
                  ich habe auch ein Problem mit Blobs.
                  <br>bisher bin ich soweit gekommen das ich dateien in einem Blobfeld abspeichern kann aber nun meine frage wie kann ich z.B ein rtf oder doc datei wieder aus der Datenbank auslesen in ein memofeld ?

                  Comment


                  • #10
                    Hallo,

                    wie erfolgt der Zugriff auf diese Datenbank? Wenn es sich um Delphi und IBX handelt, ist hier im Forum ein komplettes Beispielprojekt unter <i>Delphi | IBX | RedSys</i> zu finden (dort werden Word-Dokumente in komprimierter Form gespeichert/gelesen und auch sonstige Dateien wie ZIPs und Grafiken als BLOb gespeichert)

                    Comment


                    • #11
                      Hallo, <br>
                      leider ist ist in dem Projekt noch kein ibx verwendet und muss mich leider noch mit den normalen Datenbankkompos herumschlagen.Ich verwende die normalen delphi datenbankkomponenten : Database und TQuery. nun möchte ich eben das blob feld über die query mit text füllen

                      Comment


                      • #12
                        Hallo,

                        in diesem Fall ist ein Beispielprojekt, das beliebig grosse Dateien einliest, komprimiert und in einer BLOb-Feld einer InterBase-Tabelle ablegt, in meinem Buch <i>Client/Server Datenbankentwicklung mit Delphi</i> zu finden. Dort verwendet ich zum Beispiel eine <b>TQuery</b>-Instanz, um die komprimierten Daten zu übergeben:
                        <pre>
                        { Alle Dateien (mit Ausnahme von "*.~*") aus dem Projektverzeichnis
                        werden in einer Transaktion in die Datenbank via SQL importiert.
                        Dies gilt auch für das BLOB-Feld mit dem Dateiinhalt.
                        Abzuarbeitende Datenbank-Aktionen:
                        1. AutoCommit deaktivieren (eigene Transaktion)
                        2. DeleteProjectFiles -> alte Projektdateien löschen
                        3. Neue Projektdateien einlesen
                        4. Datum in ProVer für diese Versionsnummer aktualisieren
                        5. Feld "Abschluss" nach dem gelungenen Import auf "J" setzen.
                        (das Feld wird beim Export auf "N" gesetzt!) }

                        procedure TDM.InsertProjectFiles(CompressLevel: Integer;
                        FilePath: String);
                        resourcestring
                        cErrCompLevel = 'Der CompressionsLevel %d ist ungültig';
                        cMsgStatus = '%d Bytes zu %d Bytes komprimiert.' + #13 +
                        'Komprimierte Daten: %d %% der Originalgröße';
                        var
                        DirInfo : TSearchRec; // FindFirst-Record
                        iRet, // Rückgabewert
                        iCount : Integer; // Endwert für ProgressBar
                        sPfad, // FindFirst-Suchmaske
                        sExt, // Dateinamen-Erweiterung
                        sBlobFile : String; // kompletter Dateiname
                        InFile : TStream; // TFileStream für Dateiinhalt
                        OutFile : TMemoryStream; // Ziel für die Komprimierung
                        ZStream : TCustomZLibStream; // Kompressor
                        iReadByte, // Hilfsvariable Statistik
                        iWriteByte: Integer; // Hilfsvariable Statistik
                        aCompress : TCompressionLevel; // Komprimierungsgrad
                        begin
                        // welche Komprimierungsmethode soll verwendet werden
                        case CompressLevel of
                        0 : aCompress := clNone;
                        1 : aCompress := clFastest;
                        2 : aCompress := clMax;
                        else
                        raise Exception.CreateFmt(cErrCompLevel, [CompressLevel]);
                        end;
                        iReadByte := 0;
                        iWriteByte := 0;
                        iCount := 0;
                        // Suchmaske zuweisen
                        sPfad := FilePath + '*.*';
                        // Step 1: Anzahl der Dateien ermitteln
                        iRet := FindFirst(sPfad, FaAnyfile, DirInfo);
                        while iRet = 0 do
                        begin
                        if ((DirInfo.Attr and FaDirectory <> FaDirectory) and
                        (DirInfo.Attr and FaVolumeId <> FaVolumeID))
                        then
                        // Delphi-Sicherungskopie überspringen
                        if Pos('~', ExtractFileExt(DirInfo.Name)) = 0
                        // Endwert für ProgressBar ermitteln
                        then Inc(iCount);
                        iRet := FindNext(DirInfo);
                        end;
                        SysUtils.FindClose(DirInfo);
                        // Step 2: Endewert + Skalierung von ProgressBar setzen
                        with FormImport.ProgressBar1 do
                        begin
                        Max := iCount;
                        Step := 1;
                        end;
                        // Step 3: Dateien einzeln in die Datenbank übernehmen
                        QueryProFiles.Close;
                        Database1.StartTransaction;
                        // alle eventuell bereits vorhandenen Files löschen
                        DM.DeleteProjectFiles;
                        try
                        try
                        QueryInsertProFiles.Prepare;
                        iRet := FindFirst(sPfad, FaAnyfile, DirInfo);
                        while iRet = 0 do
                        begin
                        if ((DirInfo.Attr and FaDirectory <> FaDirectory) and
                        (DirInfo.Attr and FaVolumeId <> FaVolumeID))
                        then with QueryInsertProFiles do
                        begin
                        sExt := AnsiUpperCase(ExtractFileExt(DirInfo.Name));
                        // den Punkt (z.Bsp. ".DPR") löschen
                        System.Delete(sExt, 1, 1);
                        // Delphi-Sicherungskopie überspringen
                        if Pos('~', sExt) = 0 then
                        begin
                        sBlobFile := FilePath + DirInfo.Name;
                        // Anzeige in der Statuszeile aktualisieren
                        FormMain.StatBar.Panels[0].Text := sBlobFile;
                        // Parameter für INSERT übergeben
                        ParamByName('nVerNr').Value := TableProVerVERNR.Value;
                        ParamByName('sFilename').Value := DirInfo.Name;
                        ParamByName('sFileTyp').Value := sExt;
                        ParamByName('nFileSize').AsInteger := DirInfo.Size;
                        ParamByName('dtFileDate').AsDate := FileDateToDateTime(DirInfo.Time);
                        // ZLib
                        InFile := TFileStream.Create(sBlobFile, fmOpenRead);
                        // Anzahl der eingelesenen Bytes hochzählen
                        Inc(iReadByte, InFile.Size);
                        try
                        OutFile := TMemoryStream.Create;
                        try
                        ZStream := TCompressionStream.Create(aCompress, OutFile);
                        try
                        ZStream.CopyFrom(InFile, 0);
                        finally
                        // erst nach Free wird der Buffer geleert !
                        ZStream.Free;
                        // Anzahl der komprimierten Bytes hochzählen
                        Inc(iWriteByte, OutFile.Size);
                        // komprimierten Dateiinhalt als Parameter übergeben
                        ParamByName('bFileData').LoadFromStream(OutFile, ftBlob);
                        end;
                        finally
                        OutFile.Free;
                        end;
                        finally
                        InFile.Free;
                        end;
                        // jede Datei einzeln via INSERT in die Tabelle schreiben
                        ExecSQL;
                        FormImport.ProgressBar1.StepIt;
                        end;
                        end;
                        iRet := FindNext(DirInfo);
                        end;
                        finally
                        SysUtils.FindClose(DirInfo);
                        QueryInsertProFiles.UnPrepare;
                        end;
                        // Feld VERDATUM + ABSCHLUSS in ProVer aktualisieren
                        with SP_ProVerImport do
                        begin
                        Params[0].Value := TableProVerVERNR.Value;
                        Execproc;
                        end;
                        // Transaktion bestätigen
                        Database1.Commit;
                        QueryProFiles.Open;
                        // Anzeige von ProVer aktualisieren
                        TableProVer.Refresh;
                        except
                        // Fehler -> Rollback (keine Daten übernehmen)
                        Database1.Rollback;
                        QueryProFiles.Open;
                        Raise;
                        end;
                        // alles erledigt -> Balkenanzeige auf 0
                        FormImport.ProgressBar1.Position := 0;
                        // Anzeige von ProDetail aktualisieren
                        RefreshProFiles;
                        with CreateMessageDialog(Format(cMsgStatus, [iReadByte,iWriteByte,
                        (iWriteByte * 100) div iReadByte]),
                        mtInformation,[mbOk]) do
                        try
                        Caption := 'Komprimierungsergebnis';
                        ShowModal;
                        finally
                        Release;
                        end;
                        end;

                        { Der Inhalt des Blob-Feldes des ausgewählten Detaildatensatzes
                        soll angezeigt werden -> TQuery liest die Daten aus der
                        Tabelle für diese FileNr aus. }

                        procedure TDM.OpenBlobData;
                        resourcestring
                        cErrNullData = 'TBlobStream enthält keine Daten!';
                        cMsg1 = '%d komprimierte Bytes zu %d Bytes expandiert.';
                        var
                        InFile : TBlobStream;
                        OutFile : TStream;
                        TextStream : TMemoryStream;
                        ZStream : TCustomZLibStream;
                        Count : Integer;
                        begin
                        with QuerySelectBlob do
                        begin
                        Params[0].Value := QueryProFilesFILENR.Value;
                        Open;
                        end;
                        InFile := TBlobStream.Create(QuerySelectBlobFILEDATA, bmRead);
                        // wurden Daten aus dem Blob-Feld gelesen ?
                        if InFile.Size = 0 then
                        begin
                        InFile.Free;
                        ShowMessage(cErrNullData);
                        Exit;
                        end;
                        try
                        // Exportdatei anlegen (Exportpfad + Dateiname)
                        OutFile := TMemoryStream.Create;
                        try
                        // komprimierte Blob-Daten entkomprimieren
                        ZStream := TDecompressionStream.Create(InFile);
                        try
                        // in 4096-Byte Häppchen in die Datei schreiben
                        while True do begin
                        Count := ZStream.Read(ZLibBuffer, ZLibBufferSize);
                        if Count <> 0
                        then OutFile.WriteBuffer(ZLibBuffer,Count)
                        else Break;
                        end;
                        finally
                        // Free -> Buffer wird geleert
                        ZStream.Free;
                        // Ergebnis anzeigen
                        with FormBlob do begin
                        OutFile.Position := 0;
                        // ASCII-Datei oder binäre DFM-Datei?
                        if bShowDFM then
                        begin
                        TextStream := TMemoryStream.Create;
                        try
                        TextStream.Position := 0;
                        ObjectResourceToText(OutFile, TextStream);
                        TextStream.Position := 0;
                        //alt (TMemo): Lines.LoadFromStream(TextStream);
                        ShowData(TextStream);
                        finally
                        TextStream.Free;
                        end
                        end
                        else
                        //alt (TMemo): Lines.LoadFromStream(OutFile);
                        ShowData(OutFile);
                        StatBarBlob.SimpleText := Format(cMsg1, [InFile.Size, OutFile.Size]);
                        end;
                        end;
                        finally
                        OutFile.Free;
                        end;
                        finally
                        InFile.Free;
                        end;
                        end;
                        </pre>
                        Die Konfiguration der TQuery-Instanzen sieht im Objektinspektor wie folgt aus:
                        <pre>
                        object QuerySelectBlob: TQuery
                        DatabaseName = 'DBSrcArc'
                        SQL.Strings = (
                        'SELECT FileData FROM ProFiles'
                        'WHERE FileNr = :nFileNr')
                        Left = 272
                        Top = 104
                        ParamData = <
                        item
                        DataType = ftInteger
                        Name = 'nFileNr'
                        ParamType = ptUnknown
                        end>
                        object QuerySelectBlobFILEDATA: TBlobField
                        FieldName = 'FILEDATA'
                        BlobType = ftBlob
                        Size = 1
                        end
                        end
                        object QueryExportFiles: TQuery
                        DatabaseName = 'DBSrcArc'
                        SQL.Strings = (
                        'SELECT FileName, FileData FROM ProFiles'
                        'WHERE VerNr = :nVerNr')
                        Left = 608
                        Top = 104
                        ParamData = <
                        item
                        DataType = ftInteger
                        Name = 'nVerNr'
                        ParamType = ptUnknown
                        end>
                        object QueryExportFilesFILENAME: TStringField
                        FieldName = 'FILENAME'
                        Size = 30
                        end
                        object QueryExportFilesFILEDATA: TBlobField
                        FieldName = 'FILEDATA'
                        BlobType = ftBlob
                        Size = 1
                        end
                        end
                        object QueryInsertProFiles: TQuery
                        DatabaseName = 'DBSrcArc'
                        SQL.Strings = (

                        'INSERT INTO PROFILES (VerNr,Filename,FileTyp,FileSize, FileDate,' +
                        'FileData)'

                        'VALUES (:nVerNr,:sFilename,:sFileTyp,:nFileSize,:dtFileDa te,:bFi' +
                        'leData)')
                        Left = 440
                        Top = 104
                        ParamData = <
                        item
                        DataType = ftInteger
                        Name = 'nVerNr'
                        ParamType = ptUnknown
                        end
                        item
                        DataType = ftString
                        Name = 'sFilename'
                        ParamType = ptUnknown
                        end
                        item
                        DataType = ftString
                        Name = 'sFileTyp'
                        ParamType = ptUnknown
                        end
                        item
                        DataType = ftInteger
                        Name = 'nFileSize'
                        ParamType = ptUnknown
                        end
                        item
                        DataType = ftDate
                        Name = 'dtFileDate'
                        ParamType = ptUnknown
                        end
                        item
                        DataType = ftBlob
                        Name = 'bFileData'
                        ParamType = ptUnknown
                        end>
                        end
                        </pre&gt

                        Comment


                        • #13
                          hallo,<br>
                          das folgende problem liegt darin , dass ich noch kein ibx verwende in dem projekt.So muss ich mich eben mit den normalen bde kompos rumschlagen. Bis habe ich immer einer insert über ein query gemacht exec... Das gieng ja auch bisher mit normalen feldern aber jetzt mit einem blobfeld kommen ich total raus! Muss ich den Text sepereat einfügen oder in einem Stream ......---> keine Ahnung können sie mir nicht einen Tipp geben bitte .

                          <br>Gruß Volke

                          Comment


                          • #14
                            Hallo,

                            mein 2. Beispiel vom 22. Oktober hat <b>TQuery</b> (und somit die BDE verwendet)

                            Comment

                            Working...
                            X