SQL Server FileStream (转载)

以往我们对文件管理有两种方法:

  1. 数据库只保存文件的路径,具体的文件保存在文件服务器(NFS)上,使用时,编程实现从文件服务器读取文件;
  2. 将文件直接以varbinary(max)或image数据类型保存在数据库中。

  上面两种文件存放方式都有问题:第一种方法因为会访问磁盘,故受I/O影响性能不是很好,而且不能很好的进行文件备份;第二种方法虽然解决了文件备份(数据库的备份)问题,但是由于字段的字节数太大,对数据库本身也会造成影响,性能也很低下。

  微软在SQL Server 2008推出了一种新的方式 - FileStream,它不是一种新的数据类型,而是一种技术,它使SQL Server数据库引擎和NTFS文件系统成为了一个整体,它结合了上面两种方式的优点:FileStream使用NT系统来缓存文件数据,而对文件数据的操作可使用Transact-SQL语句对其进行插入、更新、查询、搜索和备份。

  https://msdn.microsoft.com/en-us/library/gg471497(v=sql.110).aspx

  FILESTREAM integrates the SQL Server Database Engine with an NTFS file system by storing varbinary(max) binary large object (BLOB) data as files on the file system. Transact-SQL statements can insert, update, query, search, and back up FILESTREAM data. Win32 file system interfaces provide streaming access to the data.

  FILESTREAM uses the NT system cache for caching file data. This helps reduce any effect that FILESTREAM data might have on Database Engine performance. The SQL Server buffer pool is not used; therefore, this memory is available for query processing.

一、FileStream配置



  1. 配置SQL Server安装实例:Start -> All Programs -> Microsoft SQL Server 2008 R2 -> Configuration Tools -> SQL Server Configuration Manager

  右击属性,切换到FILESTREAM标签,勾选如下配置

  2. 打开SQL Server,并配置如下

  以上也可以通过如下脚本执行:

Exec sp_configure filesteam_access_level, 2
RECONFIGURE

  最后重启SQL Server Service

二、实例展示



  创建FileStream类型文件/组

  FILESTREAM data must be stored in FILESTREAM filegroups. A FILESTREAM filegroup is a special filegroup that contains file system directories instead of the files themselves. These file system directories are called data containers. Data containers are the interface between Database Engine storage and file system storage.

--Create filestreamgroup
ALTER DATABASE [Archive]
ADD FILEGROUP [FileStreamGroup] CONTAINS FILESTREAM
GO

--Create filestream and association with filestreamgroup above
ALTER DATABASE [Archive]
ADD FILE ( NAME = N‘FileStream‘, FILENAME = N‘D:\Company\Data\SQL Server\FileStream‘) TO FILEGROUP [FileStreamGroup]
GO

  注意上面将FILEGROUP类型的文件组FileStreamGroup关联到FILENAME时,我们给FILENAME声明的实际上是一个文件夹:FILENAME = N‘D:\Company\Data\SQL Server\FileStream‘,而不是个具体的文件,该文件夹会存储FileStreamGroup文件组中所有的相关文件,如下图所示:

  filestream.hdr 文件是 FILESTREAM 容器的头文件。filestream.hdr 文件是重要的系统文件。它包含 FILESTREAM 标头信息。请勿删除或修改此文件。

  创建测试表(注意:如果表包含FILESTREAM列,则每一行都必须具有唯一的行ID)

--Create table
CREATE TABLE Archive.dbo.Attachment (
    [ID] [UNIQUEIDENTIFIER] ROWGUIDCOL NOT NULL PRIMARY KEY,
    [FileName] NVARCHAR(100) NULL,
    [CreateUser] NVARCHAR(100) NULL,
    [CreateDatetime] DATETIME NULL,
    [Content] VARBINARY(MAX) FILESTREAM NULL
)
FILESTREAM_ON [FileStreamGroup]

  什么是rowguidcol关键字

rowguidcol:指定列为全球惟一鉴别行号列(rowguidcol是Row Global UniqueIdentifier Column的缩写)。此列的数据类型必须为UNIQUEIDENTIFIER类型。一个表中数据类型为UNIQUEIDENTIFIER的列中只能有一个列被定义为rowguidcol列。rowguidcol属性不会使列值具有惟一性,也不会自动生成一个新的数据值给插入行。需要在INSERT语句中使用NEWID()函数或指定列的默认值为NEWID()函数。

  插入一些测试数据

--Insert some records
INSERT INTO Attachment VALUES
(NEWID(),‘File Name 1‘,‘shg.cpan‘, GETDATE(),NULL),
(NEWID(),‘File Name 1‘,‘shg.cpan‘, GETDATE(),CAST(‘‘ AS VARBINARY(MAX))),
(NEWID(),‘File Name 1‘,‘shg.cpan‘, GETDATE(),CAST(‘This is a attachment, which contains all introduction for filestream‘ AS VARBINARY(MAX)))

  从前台插入一些数据

using (SqlConnection conn = new SqlConnection("server=10.7.15.172;database=Archive;uid=sa;pwd=1234;Connect Timeout=180"))
{
    conn.Open();
    using (SqlCommand cmd = conn.CreateCommand())
    {
        string id = Guid.NewGuid().ToString();
        cmd.CommandText = "INSERT INTO Attachment VALUES(‘" + id + "‘,‘File Name 2‘,‘shg.cpan‘,‘" + DateTime.Now + "‘,@content)";
        SqlParameter param = new SqlParameter("@content", SqlDbType.VarBinary, 1000000000);
        param.Value = File.ReadAllBytes(@"D:\Folder\131 u_ex151207.log");
        cmd.Parameters.Add(param);
        cmd.ExecuteNonQuery();
    }
    conn.Close();
}

  检索数据

SELECT DATALENGTH(CONTENT)/(1024.0 * 1024.0) AS MB,* FROM ATTACHMENT

  结果

  文件系统

  上面的文件都是上传的真实文件,只不过没有后缀,如果重命名加上后缀,即可读取,如最后一个是excel文件,加上.xls,即可用Excel软件打开此文件。

  下面我们再插入一条记录

INSERT INTO Attachment VALUES
(NEWID(),‘Win32API‘,‘shg.cpan‘, GETDATE(),CAST(‘This is a attachment, which contains all introduction for filestream‘ AS VARBINARY(MAX)))

  文件名00000016-0000016e-000c是如何和数据关联的呢

  DBCC IND (Archive, Attachment, -1)

  我们看一下PagePID为110的页情况(PageType为1表明是数据页)

DBCC TRACEON (3604);
DBCC PAGE (Archive, 1, 110, 3);
GO

  看到了什么?CreateLSN即是我们在文件系统中看到的文件名00000016:0000016e:000c,这样数据库中的纪录就和文件联系起来了

三、使用 Win32 管理 FILESTREAM 数据



https://technet.microsoft.com/zh-cn/library/cc645940(v=sql.105).aspx

可以使用 Win32 在 FILESTREAM BLOB 中读取和写入数据。您需要执行以下步骤:

  1. 读取 FILESTREAM 文件路径;
  2. 读取当前事务上下文;
  3. 获取 Win32 句柄,并使用该句柄在 FILESTREAM BLOB 中读取和写入数据

读取 FILESTREAM 文件路径

DECLARE @filePath varchar(max)

SELECT @filePath = Content.PathName()
FROM Archive.dbo.Attachment
WHERE FileName = ‘Win32API‘
PRINT @filepath

\\CHUNTING-PC\MSSQLSERVER\v1\Archive\dbo\Attachment\%!Content\583FFDB4-921B-4340-8247-130174488DC8

读取当前事务上下文

DECLARE @txContext varbinary(max)

BEGIN TRANSACTION
SELECT @txContext = GET_FILESTREAM_TRANSACTION_CONTEXT()
PRINT @txContext
COMMIT

0x9D84E776FD943D419C99727C7AAA5B00

获取 Win32 句柄,并使用该句柄在 FILESTREAM BLOB 中读取和写入数据

若要获取 Win32 文件句柄,请调用 OpenSqlFilestream API。此 API 是从 sqlncli.dll 文件中导出的。可以将返回的句柄传递给以下任何 Win32 API:ReadFileWriteFileTransmitFileSetFilePointerSetEndOfFileFlushFileBuffers。下面的示例说明了如何获取 Win32 文件句柄并使用它在 FILESTREAM BLOB 中读取和写入数据。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using System.Text;

namespace FileStreamConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnection sqlConnection = new SqlConnection("server=10.7.15.172;database=Archive;Connect Timeout=180;Integrated Security=true");

            SqlCommand sqlCommand = new SqlCommand();
            sqlCommand.Connection = sqlConnection;

            try
            {
                sqlConnection.Open();

                //The first task is to retrieve the file path of the SQL FILESTREAM BLOB that we want to access in the application.
                sqlCommand.CommandText = "SELECT Content.PathName() FROM Archive.dbo.Attachment WHERE FileName = ‘Win32API‘";

                String filePath = null;

                Object pathObj = sqlCommand.ExecuteScalar();
                if (DBNull.Value != pathObj)
                {
                    filePath = (string)pathObj;
                }
                else
                {
                    throw new System.Exception("Chart.PathName() failed to read the path name for the Chart column.");
                }

                //The next task is to obtain a transaction context. All FILESTREAM BLOB operations occur within a transaction context to maintain data consistency.

                //All SQL FILESTREAM BLOB access must occur in a transaction. MARS-enabled connections have specific rules for batch scoped transactions,
                //which the Transact-SQL BEGIN TRANSACTION statement violates. To avoid this issue, client applications should use appropriate API facilities for transaction management,
                //management, such as the SqlTransaction class.

                SqlTransaction transaction = sqlConnection.BeginTransaction("mainTranaction");
                sqlCommand.Transaction = transaction;

                sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";//注意这里返回的就是事务上下文,该事务就是上面sqlConnection创建的事务transaction

                Object obj = sqlCommand.ExecuteScalar();
                byte[] txContext = (byte[])obj;//事务上下文是数据库二进制类型

                //The next step is to obtain a handle that can be passed to the Win32 FILE APIs.
                SqlFileStream sqlFileStream = new SqlFileStream(filePath, txContext, FileAccess.ReadWrite);//这里要将事务上下文也传给SqlFileStream,这样该SqlFileStream就和前面创建的数据库事务transaction绑定了

                byte[] buffer = new byte[512];

                int numBytes = 0;

                //Write the string, "EKG data." to the FILESTREAM BLOB. In your application this string would be replaced with the binary data that you want to write.

                string someData = "EKG data.";
                Encoding unicode = Encoding.GetEncoding(0);

                sqlFileStream.Write(unicode.GetBytes(someData.ToCharArray()), 0, someData.Length);

                //Read the data from the FILESTREAM BLOB.

                sqlFileStream.Seek(0L, SeekOrigin.Begin);

                numBytes = sqlFileStream.Read(buffer, 0, buffer.Length);

                string readData = unicode.GetString(buffer);

                if (numBytes != 0)
                {
                    Console.WriteLine(readData);
                }
                //Because reading and writing are finished, FILESTREAM must be closed. This closes the c# FileStream class,
                //but does not necessarily close the the underlying FILESTREAM handle.
                sqlFileStream.Close();

                //The final step is to commit or roll back the read and write operations that were performed on the FILESTREAM BLOB.

                sqlCommand.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                sqlConnection.Close();
            }
            Console.ReadKey();
        }
    }
}

注意此处的连接方式,必须是integrated security,如果设置成如下

SqlConnection conn = new SqlConnection("server=10.7.15.172;database=Archive;uid=sa;pwd=1234;Connect Timeout=180")

会报错提示权限问题

  这是因为实际的文件保存在服务器的硬盘上,读取时读到的文件是"\\xxxx",还是通过网上邻居,所以要使用信任连接

Only the account under which the SQL Server service account runs is granted NTFS permissions to the FILESTREAM container. We recommend that no other account be granted permissions on the data container.

Note

SQL logins will not work with FILESTREAM containers. Only NTFS authentication will work with FILESTREAM containers.

另一篇文章 http://stackoverflow.com/questions/1398404/sql-server-filestream-access-denied

插入Attachment的完整C#语句

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.IO;
using System.Transactions;

namespace FileStreamConsoleApp
{
    public class ArchiveAttachment
    {
        private const string ConnStr = "server=10.7.15.172;database=Archive;Connect Timeout=180;Integrated Security=true";

        public static void InsertAttachment(string Id, string fileName, string fileFullName, string createUser, DateTime createDatetime)
        {
            string InsertTSql = @"INSERT INTO Attachment(Id, FileName, CreateUser, CreateDatetime, Content) VALUES(@Id, @FileName, @CreateUser, @CreateDatetime, 0x);
                                    SELECT Content.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM Attachment WHERE Id = @Id";

            string serverPath;
            byte[] serverTransaction;

            using (TransactionScope ts = new TransactionScope())
            {
                using (SqlConnection conn = new SqlConnection(ConnStr))
                {
                    conn.Open();

                    using (SqlCommand cmd = new SqlCommand(InsertTSql, conn))
                    {
                        cmd.Parameters.Add("@Id", SqlDbType.NVarChar).Value = Id;
                        cmd.Parameters.Add("@FileName", SqlDbType.NVarChar).Value = fileName;
                        cmd.Parameters.Add("@CreateUser", SqlDbType.NVarChar).Value = createUser;
                        cmd.Parameters.Add("@createDatetime", SqlDbType.DateTime).Value = createDatetime;
                        using (SqlDataReader sdr = cmd.ExecuteReader())
                        {
                            sdr.Read();
                            serverPath = sdr.GetSqlString(0).Value;
                            serverTransaction = sdr.GetSqlBinary(1).Value;
                            sdr.Close();
                        }
                    }
                    SaveAttachment(fileFullName, serverPath, serverTransaction);
                }
                ts.Complete();
            }
        }

        private static void SaveAttachment(string clientPath, string serverPath, byte[] serverTransaction)
        {
            const int BlockSize = 1024 * 512;

            using (FileStream source = new FileStream(clientPath, FileMode.Open, FileAccess.Read))
            {
                using (SqlFileStream dest = new SqlFileStream(serverPath, serverTransaction, FileAccess.Write))
                {
                    byte[] buffer = new byte[BlockSize];
                    int bytesRead;
                    while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        dest.Write(buffer, 0, bytesRead);
                        dest.Flush();
                    }
                    dest.Close();
                }
                source.Close();
            }
        }
    }
}

四、使用场合



  请注意以下事项:

  并不是所有的文件存储都适合使用FileStream,如果所存储的文件对象平均大于1MB考虑使用FileStream,否则对于较小的文件对象,以varbinary(max)BLOB存储在数据库中通常会提供更为优异的流性能

https://msdn.microsoft.com/en-us/library/gg471497(v=sql.110).aspx

http://stackoverflow.com/questions/13420305/storing-files-in-sql-server#

http://research.microsoft.com/apps/pubs/default.aspx?id=64525

一些参考链接:

使用FILESTREAM最佳实践:https://technet.microsoft.com/zh-cn/library/dd206979(v=sql.105).aspx

设计和实现 FILESTREAM 存储:https://technet.microsoft.com/zh-cn/library/bb895234%28v=sql.105%29.aspx?f=255&MSPPError=-2147217396

二进制大型对象 (Blob) 数据 (SQL Server):https://technet.microsoft.com/zh-cn/library/bb895234(v=sql.110).aspx

Files and Filegroups Architecture:https://msdn.microsoft.com/en-us/library/ms179316.aspx?f=255&MSPPError=-2147217396

FILESTREAM Storage in SQL Server 2008https://msdn.microsoft.com/en-us/library/hh461480.aspx?f=255&MSPPError=-2147217396

原文链接

时间: 2024-10-29 19:10:33

SQL Server FileStream (转载)的相关文章

SQL Server FileStream优点与不足

LOB优点: 1.保证大对象的事务一致性. 2.备份与还原包括大数据对象,可以对它进行时点恢复. 3.所有数据都可以使用一种存储与查询环境. LOB不足: 1.大型对象在缓存中占非常大的缓存区. 2.更新大型对象时会产生大量的数据碎片. 3.数据库文件可能变的非常大. FileStream 优点: 1.大型对象存储在文件系统中.数据库中只保存48字节的指针. 2.大型对象数据在事务中与结构数据保持一致. 3.大型对象可以通过 API .T-SQL.NTFS流访问,从而提高性能. 4.大型对象只受

SQL Server 存储过程(转载)

SQL Server 存储过程 Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø 存储过程的概念 存储过程Procedure是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行. 存储过程中可以包含逻辑控制语句和数据操纵语句,它可以接受参数.输出参数.返回单个或多个结果集以及返回值. 由于存储

SQL Server Profiler(转载)

SQL Server Profiler工具 一.SQL Profiler工具简介 SQL Profiler是一个图形界面和一组系统存储过程,其作用如下: 图形化监视SQL Server查询: 在后台收集查询信息: 分析性能: 诊断像死锁之类的问题: 调试T-SQL语句: 模拟重放SQL Server活动: 也可以使用SQL Profiler捕捉在SQL Server实例上执行的活动.这样的活动被称为Profiler跟踪. 1.Profiler跟踪 从开始=>所有程序=>Microsoft SQ

Sql Server FileStream

sqlserer 安装的时候记得勾选filestream: 下面启动: 执行: EXEC SP_CONFIGURE FILESTREAM_ACCESS_LEVEL,2 RECONFIGURE 重启数据库服务: 创建数据库: CREATE DATABASE [Filestream] 由于filestream是个特殊用途的文件组,要创建一个文件组指定constains filestream ALTER DATABASE Filestream ADD FILEGROUP FG_FT CONTAINS

SQL Server 内存中OLTP内部机制概述(三)

----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory OLTP Internals Overview>:http://technet.microsoft.com/en-us/library/dn720242.aspx 译者水平有限,如有翻译不当之处,欢迎指正. ----------------------------我是分割线---------------

SQL Server 全文索引介绍(转载)

概述 全文引擎使用全文索引中的信息来编译可快速搜索表中的特定词或词组的全文查询.全文索引将有关重要的词及其位置的信息存储在数据库表的一列或多列中.全文索引是一种特殊类型的基于标记的功能性索引,它是由 SQL Server 全文引擎生成和维护的.生成全文索引的过程不同于生成其他类型的索引.全文引擎并非基于特定行中存储的值来构造 B 树结构,而是基于要编制索引的文本中的各个标记来生成倒排.堆积且压缩的索引结构.在 SQL Server 2008 中,全文索引大小仅受运行 SQL Server 实例的

SQL SERVER: 合并相关操作(Union,Except,Intersect) - 转载

SQL Server 中对于结果集有几个处理,值得讲解一下 1. 并集(union,Union all) 这个很简单,是把两个结果集水平合并起来.例如 SELECT * FROM A UNION SELECT * FROM B [注意]union会删除重复值,也就是说A或B中重复的数据行,最终只会出现一次,而union all则会保留重复行. 2. 差异(Except) 就是两个集中不重复的部分.例如 SELECT * FROM A EXCEPT SELECT * FROM B 这个的意思是,凡

Microsoft SQL Server 2008安装图解(Windows 7)(转载)

FoxDie 2010年04月17日 简介 本文详细记录了一次完整的Microsoft SQL Server 2008在Windows 7操作系统上的安装过程.注意:Microsoft SQL Server 2008与Windows 7操作系统存在一定的兼容性问题,在完成安装之后需要为Microsoft SQL Server 2008安装SP1补丁.下面将详细说明整个安装过程. 安装日志 在Windows7操作系统系,启动Microsoft SQL 2008安装程序后,系统兼容性助手将提示软件存

转载:sql2005 Microsoft SQL Server Management Studio Express的安装问题

转载地址:http://blog.csdn.net/rjc20098022/article/details/26958105 在这个网址http://www.microsoft.com/zh-cn/download/details.aspx?id=8961#filelist... 说明 注意:您必须在计算机上具有管理权限才能安装 SSMSE. 步骤 1: 对于 32 位平台,请下载并 安装 Microsoft .NET Framework 2.0 的 32 位版本.对于 64 位平台(仅 x64