首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 2008 FILESTREAM 實戰:大型二進位資料的交易一致性儲存與 NTFS 流存取

     編輯:關於SqlServer

前言:BLOB 儲存的兩難與 FILESTREAM 破局

傳統 `varbinary(max)` 存資料庫:備份膨脹、Buffer Pool 污染、無法直接檔案存取。純檔案系統:交易不一致、備份分離、權限管理分離。SQL Server 2008 **FILESTREAM** 整合 NTFS 流 API,實現 資料庫交易一致性 + 檔案系統流存取效能 + 整合備份/還原。本文從啟用、建立、存取、遷移、效能調優全流程實戰。

一、啟用 FILESTREAM (三層配置)

1. Windows 層級:啟用 NTFS 流

  • 檔案系統必須為 NTFS (ReFS 不支援)
  • 無需額外設定,NTFS 原生支援 `:$DATA` 替代資料流

2. SQL Server 實例層級:Configuration Manager

  1. SQL Server Configuration Manager → SQL Server Services → 右鍵實例 → Properties
  2. FILESTREAM 頁籤:勾選所有三項
    • ☑ Enable FILESTREAM for Transact-SQL access
    • ☑ Enable FILESTREAM for file I/O streaming access (Win32)
    • ☑ Allow remote clients to have streaming access to FILESTREAM data
  3. Windows Share Name:預設 `MSSQLSERVER`,可自訂 (影響 UNC 路徑 `\\Server\MSSQLSERVER\...`)
  4. 重啟 SQL Server 服務生效

3. T-SQL 層級:伺服器配置選項

EXEC sp_configure filestream_access_level, 2;  -- 0=關閉, 1=T-SQLのみ, 2=T-SQL+Win32, 3=含遠端
RECONFIGURE;

二、建立 FILESTREAM 資料庫與資料表

1. 資料庫必須包含 FILESTREAM 檔案群組

CREATE DATABASE FS_Demo
ON PRIMARY
( NAME = FS_Demo_Data, FILENAME = 'D:\SQLData\FS_Demo.mdf' ),
FILEGROUP FS_FG CONTAINS FILESTREAM
( NAME = FS_Demo_FS, FILENAME = 'E:\FSData\FS_Demo_FS' )  -- 此目錄必須存在、空、NTFS
LOG ON
( NAME = FS_Demo_Log, FILENAME = 'F:\SQLLog\FS_Demo.ldf' );

2. 資料表需 ROWGUIDCOL + varbinary(max) FILESTREAM

CREATE TABLE dbo.Documents (
    DocID       UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE DEFAULT NEWSEQUENTIALID(),
    FileName    NVARCHAR(255) NOT NULL,
    ContentType NVARCHAR(100) NOT NULL,
    FileSize    BIGINT NOT NULL,
    Content     VARBINARY(MAX) FILESTREAM NULL,  -- 關鍵:FILESTREAM 屬性
    UploadedAt  DATETIME2(3) NOT NULL DEFAULT SYSUTCDATETIME(),
    UploadedBy  INT NOT NULL,
    CONSTRAINT PK_Documents PRIMARY KEY CLUSTERED (DocID)
) ON PRIMARY FILESTREAM_ON FS_FG;  -- 指定 FILESTREAM 檔案群組

三、T-SQL 存取 (交易一致性)

1. 插入 (串流寫入)

-- 從應用層串流插入 (避免大物件進記憶體)
INSERT INTO dbo.Documents (FileName, ContentType, FileSize, Content, UploadedBy)
SELECT @FileName, @ContentType, @FileSize, @ContentStream, @UserID;
-- @ContentStream 為 varbinary(max) 參數,驅動程式自動串流

2. 更新 (部分更新不支援,需整體替換)

UPDATE dbo.Documents
SET Content = @NewContent, FileSize = @NewSize, FileName = @NewName
WHERE DocID = @DocID;

3. 刪除 (檔案隨交易提交刪除)

DELETE FROM dbo.Documents WHERE DocID = @DocID;
-- FILESTREAM 垃圾收集器 (背景執行緒) 非同步清理實體檔案

4. 備份/還原 (整合)

BACKUP DATABASE FS_Demo TO DISK = 'Backup.bak' WITH COMPRESSION;
-- FILESTREAM 資料包含在備份集中,還原時自動重建檔案結構
RESTORE DATABASE FS_Demo FROM DISK = 'Backup.bak' WITH REPLACE, MOVE ...;

四、Win32 NTFS 流存取 (高效能、繞過 SQL 引擎)

1. 取得 OpenSqlFilestream 所需路徑與交易語境

DECLARE @Path NVARCHAR(MAX), @TxnContext VARBINARY(MAX);
SELECT
    @Path = FileTableRootPath() + '\' + FileTableDirectoryName() + '\' + FileName,  -- FileTable 專用
    -- 或標準 FILESTREAM:
    @Path = GET_FILESTREAM_TRANSACTION_CONTEXT(),  -- 取得交易語境
    @TxnContext = GET_FILESTREAM_TRANSACTION_CONTEXT()
FROM dbo.Documents
WHERE DocID = @DocID;

2. C# 應用層串流讀寫 (System.Data.SqlTypes.SqlFileStream)

using (var txn = new TransactionScope(TransactionScopeOption.RequiresNew))
using (var conn = new SqlConnection(connStr))
{
    conn.Open();
    // 1. 取得路徑與語境
    string path; byte[] ctx;
    using (var cmd = new SqlCommand(@"
        SELECT GET_FILESTREAM_TRANSACTION_CONTEXT() AS Ctx,
               FileTableRootPath() + '\' + FileTableDirectoryName() + '\' + FileName AS Path
        FROM dbo.Documents WHERE DocID = @Id", conn))
    {
        cmd.Parameters.AddWithValue("@Id", docId);
        using (var rdr = cmd.ExecuteReader())
        {
            rdr.Read();
            ctx = (byte[])rdr["Ctx"];
            path = (string)rdr["Path"];
        }
    }

    // 2. 開啟 SqlFileStream (Win32 CreateFile 透過 NTFS 流)
    using (var fs = new SqlFileStream(path, ctx, FileAccess.ReadWrite, FileOptions.SequentialScan, 0))
    {
        // 讀取
        await fs.CopyToAsync(outputStream);
        // 或寫入
        await inputStream.CopyToAsync(fs);
    }

    txn.Complete();  // 交易提交,FILESTREAM 檔案生效
}

3. 關鍵優勢

  • 繞過 SQL Buffer Pool:大檔案不污染記憶體
  • NTFS 原生效能:連續讀寫接近磁碟極限、支援範圍請求、記憶體對應檔案
  • 交易一致性:交易回滾 → 檔案變更撤銷;交易提交 → 檔案持久化

五、FileTable (SQL 2012+):更進一步的檔案系統整合


-- 啟用非交易存取
ALTER DATABASE FS_Demo SET FILESTREAM (NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = 'FS_Demo');

-- 建立 FileTable (自動包含檔案系統中繼資料欄位)
CREATE TABLE dbo.Documents_FT AS FILETABLE
WITH (FILETABLE_DIRECTORY = 'Documents', FILETABLE_COLLATE_FILENAME = database_default);
  • 無需 T-SQL 插入,直接用總管/命令列 `copy / move / rename` 操作
  • 支援非交易存取 (即時可見)、全文檢索、檔案屬性同步
  • 應用場景:文件管理系統、影像存檔、ETL 階段檔案

六、效能調優與最佳實踐

維度建議理由
儲存磁碟獨立 SSD/RAID 10、NTFS 64KB 配置單元大檔案連續 I/O、減少碎片
資料庫檔案群組FILESTREAM 專用檔案群組、隔離磁碟避免與資料/記錄 I/O 爭用
插入批次應用層串流參數、避免 `OPENROWSET(BULK...)` 大物件入記憶體記憶體穩定、可預測
垃圾收集監控 `sys.dm_filestream_garbage_collection`、必要時手動 `EXEC sp_filestream_force_garbage_collection`避免刪除檔案堆積佔用空間
備份策略完整備份含 FILESTREAM、記錄備份不含 (僅中繼資料)備份大小可控、還原一致性
防毒軟體排除排除 FILESTREAM 目錄 (`E:\FSData\...`)防毒掃描導致 NTFS 流存取衝突、效能驟降

七、遷移指南:varbinary(max) → FILESTREAM


-- 1. 新增 FILESTREAM 欄位
ALTER TABLE Docs ADD Content_FS VARBINARY(MAX) FILESTREAM NULL;
-- 2. 分批回填 (每批 1000 筆)
WHILE 1=1
BEGIN
    UPDATE TOP (1000) Docs
    SET Content_FS = Content_Old
    WHERE Content_FS IS NULL AND Content_Old IS NOT NULL;
    IF @@ROWCOUNT = 0 BREAK;
END
-- 3. 切換應用、刪除舊欄位 (維護窗口)

八、總結

FILESTREAM 為 SQL Server 帶來 「資料庫一致性 + 檔案系統效能」 的最佳平衡。關鍵成功因子:三層啟用配置正確、獨立儲存磁碟、應用層串流存取、防毒排除、垃圾收集監控。SQL 2012+ 專案請優先評估 FileTable,獲得更豐富的檔案系統語意與非交易存取能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved