36 Datenbanken SQL Server 2008 FileStreams Das Ende von NTFS? Der neue SQL Server 2008 bietet mit den FileStreams eine neue Möglichkeit zur Speicherung von BLOB-Daten von Meinrad Weiss Die Artikelüberschrift ist natürlich absichtlich provokativ: Obwohl es der neue Filestream-Datentyp von SQL Server 2008 erlaubt, Dateien effizient „im“ SQL Server 2008 zu speichern und er zudem noch diverse Managementvorteile bietet, gilt auch bei NTFS: Totgesagte leben länger. Denn der Filestream-Datentyp benötigt für seine Existenz natürlich ein NTFS-Verzeichnis. Sehr oft benötigt eine Applikation neben relationalen Daten auch unstrukturierte Informationen in Form von Dateien, z.B. Dokumenten, Musikdateien, Videos kurz & bündig Inhalt Ein Überblick über die mit SQL Server 2008 eingeführten FileStreams Zusammenfassung Der neue FileStream-Typ ist ideal für die Speicherung von typischen BLOB-Daten. Auch wenn die Daten extern gelagert werden, gehören sie zur Datenbank. Der Zugriff erfolgt via SQL oder einer Win32-API, die am besten in einer .NET-Klasse gekapselt wird. Es gibt aber auch Nachteile. Insbesondere im Bereich von Backup und Restore usw., also allgemein BLOBs. Bei den bisherigen Versionen war es schwierig, beide Informationstypen effizient und sicher zu verwalten. Grundsätzlich boten sich zwei Varianten an: Entweder wurden die unterschiedlichen Typen unterschiedlich gespeichert, sprich die relationalen Daten in der Datenbank und die Dateien auf dem Dateisystem, oder es wurde alles in der Datenbank abgelegt. Im gemischten Ansatz werden die relationalen Daten in der Datenbank gespeichert. Zusätzlich zu den realen Daten muss ein Link (Dateiname) zur Datei auf dem Dateisystem verwaltet werden. Die Datei selbst befindet sich, getrennt von den strukturierten Daten, auf dem Datei- system. Damit wird die Datenbank zwar nur minimal belastet und dem Entwickler steht ein sehr effizientes ProgrammierInterface zur Verfügung, aber dafür ist das Management extrem schwierig und aufwändig. So ist es zum Beispiel nahezu unmöglich, ein konsistentes Backup von Datenbank und Dateisystem zu erstellen, ohne das System zu stoppen. Der Datenbankansatz ist viel einfacher aufgebaut und bietet entsprechende Managementvorteile. Es ist zum Beispiel trivial, ein konsistentes Backup zu erstellen, und auch die Zugriffsrechte müssen nur einmal definiert werden. Dafür müssen aber auch gravierende Nachteile in Kauf genommen werden. In den „Large Data Quellcode (C#) SQLServer2008FileStream.sln Quellcode auf CD 4.08 Abb. 1: Das Ergebnis einer Abfrage www.dotnet-magazin.de SQL Server 2008 FileStreams Types“ (varbinary(max)) können maximal zwei GB Daten gespeichert werden. Zudem kann nur via SQL auf die Daten zugegriffen werden und damit steht kein einfach umzusetzendes Streaming-Interface zur Verfügung. Erschwerend kommt noch hinzu, dass alle Manipulationen durch den Datenbank-Kernel abgearbeitet werden müssen. Dies kann zu einer enormen Belastung des TransaktionsLogfiles führen und wirkt sich negativ auf die Cache-Hit-Ratio aus, da sehr viele Data-Pages von den großen Objekten angesprochen werden. Der goldene Mittelweg: „Varbinary(max) Filestream“ Mit dem neuen Datentyp Varbinary(max) Filestream bietet SQL Server 2008 den goldenen Mittelweg. Aus Administrationssicht befindet sich alles in der Datenbank, dadurch vereinfacht sich der Betrieb erheblich. Wer etwa Rechte auf die strukturierte Information besitzt, hat auch automatisch Zugriff auf die BLOB-Daten, und ein Datenbank-Backup beinhaltet alle „in“ der Datenbank gespeicherten Informationen. In Wahrheit befindet sich der Inhalt der Filestream-Attribute aber in einzelnen Dateien auf dem Dateisystem. Die so entstehenden Dateien werden aber vom SQL Server selbständig verwaltet. Ein Datentyp, zwei Programmiermodelle Auf Filestream-Daten kann entweder via SQL oder mithilfe der Win32-API zugegriffen werden. Aus SQL-Sicht verhält sich ein Filestream-Attribut wie ein normales varbinary(max)-Attribut. Der Unterschied zwischen einem varbinary(max) und einem Filestream ist nur beim Erstellen der Tabelle sichtbar. Datenmanipulationen werden mit den bekannten Insert-, Update- und Delete-Befehlen durchgeführt. Das Beispiel in Listing 1 zeigt den Filestream-Datentyp im Einsatz. Damit die Funktionsweise einfach sichtbar ist, wird in das Filestream-Attribut ein Varchar-Wert: ‚Simple CV ...‘ geschrieben und gelesen. Aus diesem Grund sind die beiden Convert-Befehle notwendig. Normalerweise befinden sich in den BLOBAttributen binäre Objekte und dann entfallen diese Operationen. Ein Drop Database bedeutet für eine Datenbank nicht nur das Löschen der www.dotnet-magazin.de Daetnbanken Abb. 2: Die Sicht auf das Dateisystem Datenbank und Logfiles, sondern auch das Löschen aller via Filestream erstellten Dateien. Deshalb kann ein „Drop Database“ bei einer Datenbank mit vielen Filestream Datensätzen relativ lange dauern. Einer der großen Vorteile des Filestream-Datentyps ist die Tatsache, dass neben dem normalen SQL-Interface auch direkt mit dem Win32-API auf Filestream-Attribute zugegriffen werden kann. Beim Zugriff via Win32 verhält sich ein Filestream-Objekt wie eine normale Datei. Den Zugriffspfad auf die „FilestreamDatei“ liefert die Methode PathName des Filestream-Types. Zusätzlich zum Pfad ist ebenfalls ein Transaktionskontext notwendig. Dieser Transaktionskontext kann mit der Funktion Get_filestream_transaction_context() ermittelt werden. Das einzige kleine Hindernis ist zurzeit noch das Fehlen einer .NET-Klasse, welche das Win32-Interface kapselt. Microsoft hat allerdings angekündigt, eine entsprechende Klasse kurz nach der Fertigstellung des SQL Server 2008 zu liefern. Zurzeit muss man sich daher noch mit einer „WrapperAssembly“ begnügen. Weil aber lediglich die eine Methode OpenSqlFilestream für den Zugriff notwendig ist, ist eine solche Assembly schnell erstellt – nachzulesen in Listing 2. Die Methode OpenSqlFilestream liefert eine Stream-Klasse zurück. Nach einem Aufruf von OpenSqlFilestream entspricht die .NET-Programmierung dem Zugriff auf eine reguläre Datei. Das einzige, was hier speziell betrachtet werden Listing 2 Ein Wrapper-Assembly erspart den direkten Aufruf der Win32-API using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace Trivadis.Training.SqlFilestream { public class SqlNativeClient { public const UInt32 DESIRED_ACCESS_READ = 0x00000000; public const UInt32 DESIRED_ACCESS_WRITE = 0x00000001; public const UInt32 DESIRED_ACCESS_READWRITE = 0x00000002; Listing 1 [DllImport(“sqlncli10.dll“, SetLastError = true, Das Anlegen einer Tabelle mit FileStream-Attribut public static extern SafeFileHandle OpenSqlFilestream( CharSet = CharSet.Unicode)] string FilestreamPath, Create Table Person UInt32 DesiredAccess, (PERS_ID uniqueidentifier rowguidcol not null primary key UInt32 OpenOptions, ,PERS_Name varchar(50) byte[] FilestreamTransactionContext, ,PERS_CV varbinary(max) Filestream UInt32 FilestreamTransactionContextLength, ) LARGE_INTEGER_SQL AllocationSize); insert Person(PERS_ID ,PERS_Name ,PERS_CV) values [StructLayout(LayoutKind.Sequential)] (‘DF307D1E-7BDB-4CE0-9401-1BE15E14CE6A’ public struct LARGE_INTEGER_SQL ,’Muster Hans’ { ,Convert(Varbinary(max), ‘Simple CV ....’) public Int64 QuadPart; ) public LARGE_INTEGER_SQL(Int64 quadPart) { QuadPart = quadPart; } select PERS_ID ,PERS_Name } ,Convert(varchar, PERS_CV) as PERS_CV } from Person } 4.08 37 38 Datenbanken SQL Server 2008 FileStreams Abb. 4: Der FileStreamZugriff muss administrativ geregelt werden Abb. 3: Ein Filestream kann via SQL oder Win32 angesprochen werden muss, ist die Tatsache, dass während des gesamten Zugriffs eine SQL-Transaktion bestehen muss, damit der SQL-Server die Multi-User-Zugriffsfragen regeln kann – wie in Listing 3 dargestellt ist. Ohne DBA läuft gar nichts Bevor der Filestream-Datentyp verwendet werden kann, muss der Datenbankadministrator den Filestream-Service zulassen. Dabei kann festgelegt werden, ob nur mit SQL gearbeitet werden kann oder ob auch das Win32-Interface lokal beziehungsweise auch Remote verfügbar ist. Anschließend kann beim Erstellen der Datenbank festgelegt werden, auf welchen Laufwerken die FilestreamDaten gespeichert werden. Zu diesem Zweck können die Filestream-Dateigruppen mit Contains FileStream gekennzeichnet werden. Der SQL Server alloziert zu diesem Zeitpunkt keinen Platz für die Filestream-Filegruppe(n). Dieser wird erst später von den darin angelegten Dateien beansprucht. Die Größe der Dateien ist nicht mehr durch den SQL Server auf zwei Gigabyte limitiert, wie das bei normalen varbinary(max)Attributen der Fall ist. Die Größe wird ausschließlich durch den auf dem Dateisystem verfügbaren Platz limitiert. Mit Listing 3 Beim FileStream-Zugriff muss eine SQL-Transaktion eingerichtet werden private void btnReadContent_Click( object sender, EventArgs e) { // Open file handle to media stored in SQL Server. SafeFileHandle safeFileHandle = SqlNativeClient OpenDBConnection(); .OpenSqlFilestream( sqlFilePath.Value, SqlNativeClient.DESIRED_ACCESS_READ, // Start a transaction 0, SqlTransaction sqlTransaction = sqlConnection transactionToken.Value, .BeginTransaction(); (UInt32)transactionToken.Value.Length, // Build select statment and pass params. new SqlNativeClient.LARGE_INTEGER_SQL(0)); SqlCommand cmd = new SqlCommand(“SELECT PERS_CV.PathName() “ + “,GET_FILESTREAM_TRANSACTION_CONTEXT() “ // Back to traditional Filestream world FileStream fileStream = new FileStream(safeFileHandle ,FileAccess.Read); + “ FROM Person “ + “ WHERE PERS_ID = @PERS_ID;“ ,sqlConnection // Simplified Version. I know the content for my demo is ,sqlTransaction); cmd.Parameters.Add(“@PERS_ID“, SqlDbType .UniqueIdentifier).Value = new Guid(txtPERS_ID.Text); // Execute query and read results. SqlDataReader rdr = cmd.ExecuteReader( CommandBehavior.SingleRow); rdr.Read(); SqlString sqlFilePath = rdr.GetSqlString(0); SqlBinary transactionToken = rdr.GetSqlBinary(1); rdr.Close(); 4.08 TextReader textReader = new StreamReader(fileStream); // very small txtContent.Text = textReader.ReadToEnd(); // Close the reader, stream and handle textReader.Close(); fileStream.Close(); safeFileHandle.Close(); // Commit Transaction and close connection sqlTransaction.Commit(); sqlConnection.Close(); } SQL Server 2008 müssen die FilestreamDaten auf einem lokalen Drive des Datenbankservers gespeichert werden. Die Filestreams einer Tabelle können mittels Partitionierung der Tabelle auf mehrere Dateigruppen verteilt werden. Eine Transaktion ist der Schlüssel zu den Daten Bei der Verwendung von Filestream-Daten muss der SQL Server 2008 in der Lage sein, Zugriffe parallel von SQL-Interface wie auch via Win32-API zuverlässig zu verwalten. Damit keine Concurreny-Probleme auftreten, ist es ebenfalls notwendig, auch bei der Win32-Programmierung eine offene Transaktion zu besitzen. So kann der SQL Server den Zugriff auf seine Objekte in gewohnter Manier mit Locks in der Datenbank regeln. Wenn versucht werden sollte, ohne eine laufende Transaktion auf Filestream-Daten zuzugreifen, quittiert der SQL Server die Anfrage mit „Invalid Handle“. Mit dem SQL Server 2008 wird lediglich der Isolation Level „Read Commited“ für den Datenzugriff via Win32 unterstützt. Die Filestream-Daten verhalten sich bezüglich Transaktionen wie normale Attribute in der Datenbank. Das bedeutet, dass Insert-, Update- und Delete-Operationen mit einem Rollback rückgängig gemacht werden können. Die gilt sowohl für Zugriffe via SQL wie auch für das Win32-Interface. Architektur Für die Integration des Win32-Zugriffs in die Datenbank-Transaktionslogik wird mit der Installation von SQL Server 2008 ein neuer Dateisystemtreiber (FS Driver) installiert. Dieser prüft vor jedem Zugriff auf die Daten die SecurityDefinitionen innerhalb der Datenbank und auch die Existenz einer gültigen Transaktion. Innerhalb der Datenbank steht dem FS-Driver der FS-Agent zur Verfügung. Dieser kommuniziert mit dem Datenbank-Kernel und überprüft die Gültigkeit www.dotnet-magazin.de SQL Server 2008 FileStreams des Tokens und der Transaktion wie auch das Zugriffsrecht auf das BLOB-Attribut. Die Filestream-Daten werden von den Clients durch einen speziellen FS-DriverShare angesprochen, der vom SQL Server selbständig verwaltet wird. SQL Server-Failover-Cluster können auch mit Filestream-Daten aufgebaut werden. Dabei ist es allerdings notwendig, dass sich alle Filestream-Dateien auf einem Shared-Drive des Clusters befinden, damit diese bei einem Failover auf dem RemoteNode auch zur Verfügung stehen. Daetnbanken Abb. 5: Das Zusammenspiel zwischen Client und Datenbank läuft über einen eigenen Dateisystemtreiber Auch Filestreams haben ihren Preis Ganz spurlos geht der Einsatz von Filestream nicht an der Datenbank vorbei. Die größten Auswirkungen werden im Bereich von Backup und Restore spürbar. Über das Win32-Interface können Daten sehr effizient in die Datenbank gebracht werden. Der Schreibvorgang der Filestream-Daten hat dabei nur minimalen Einfluss auf das Transaktions-Log, da nur die Tatsache, dass geschrieben wird, im Log vermerkt wird. Beim Backup des Transaktion-Logs müssen aber alle Veränderungen aufgezeichnet werden. Damit werden die Log-Backups entsprechend größer und dauern länger. Das muss bei der Planung des Backup Konzeptes mit berücksichtig werden. Die Auswirkungen im Restore Fall sind noch größer: Wenn bei einer Datenbank mit Filestream-Attributen die Festplatte mit den Filestream-Daten ausfällt, kann ein Datenverlust, selbst im „Full Recovery Model“, nicht vermieden werden. Da das Transaktions-Log nur die Tatsache von Updates, nicht aber die konkreten Update-Daten beinhaltet, können alle Änderungen an FilestreamDaten nach dem letzten TransaktionsLog-Backup nicht mehr wiederhergestellt werden. Verloren gehen aber nur die Änderungen der Filestream-Attribute. Dies kann aber zu einer „inkonsistenten“ Datenbank führen. Aus diesem Grund Abb. 6: Für den FS-Driver wird ein eigener Share angelegt müssen die Filestream-Daten auf einer möglichst ausfallsicheren Disk (RaidArray) gespeichert werden. Mit sehr regelmäßigen Transaktions-Log-Backups können die Auswirkungen eines trotzdem auftretenden Laufwerksverlusts minimiert werden. Zudem werden diverse Datenbankfeatures im Zusammenhang mit Filestreams nicht unterstützt. Es ist zum Beispiel nicht möglich, einen kompletten Snapshot einer Datenbank mit Filestream-Dateigruppen zu erstellen. Die Snapshot-Datenbank darf nur die Nicht-Snapshot-Dateigruppen beinhalten, und Abfragen auf dem Snapshot müssen die Filestream-Attribute ausklammern. Database Mirroring wird zusammen mit Filestreams überhaupt nicht unterstützt. Interessanterweise ist dafür Log-Shipping möglich, wenn beide Server mit SQL Server 2008 betrieben werden und Filestream auf beiden Systemen erlaubt ist. Es ist auch nicht möglich, eine Transparent Database Encryption im Zusammenhang mit Filestream zu verwenden. Ebenso wenig können die neuen SQL Server 2008 Table-Parametertabellen mit Filestream-Attributen definiert werden. Filestreams sind ideal für BLOB-Daten Mit dem neuen Filestream steht bei SQL Server 2008 ein für BLOB-Daten extrem praktischer Datentyp zur Verfügung. Dieser erleichtert das Erstellen von Applikationen, die sowohl strukturierte wie nicht strukturierte Informationen verwalten müssen, erheblich. Der Datentyp stellt via das Win32-API ein effizientes, streamingfähiges Programmiermodell für den Zugriff auf die BLOB-Daten zur Verfügung. Da alle Daten logisch in der Datenbank gespeichert sind, können die Zugriffsrechte elegant an einem Ort verwaltet werden. Grundsätzlich vereinfacht sich dadurch das Management des Systems bis hin zu Backup- und Restore-Fragen. Einige Details, wie eingeschränkte Isolation-Level und Replikations-Möglichkeiten oder fehlender Mirroring- und Encryption-Support müssen allerdings bei der Verwendung schon berücksichtigt werden. Da Filestreams im Hintergrund auf dem NTFSDateisystem aufbauen, wird NTFS die Anwender noch für lange Zeit begleiten und zusammen mit Filestream noch viele gute Dienste leisten. Meinrad Weiss ist Principal Consultant bei Trivadis und Microsoft Regional Director für die Schweiz. Seine Erfahrungen reichen von DBATätigkeiten über Applikations-Entwicklung bis zum Design von verteilten Datenbanken. In den letzten Jahren hat er sich vor allem mit den Bereichen Data Warehousing, OLAP/Analysis Services und .NET beschäftigt. 39