【问题标题】:SQL Server 2012 FileTable Slow Performance when Creating File (Integrating Lucene.NET)SQL Server 2012 FileTable 创建文件时性能慢(集成 Lucene.NET)
【发布时间】:2013-09-28 12:20:43
【问题描述】:

我一直在设计一种使用 SQL Server FileTable 将 Lucene.NET 与 SQL Server 集成的方法的原型。使用非常方便,代码也很简单——自定义Lucene.NET不需要做任何特别的事情。我寻求的优势主要是操作性和企业性 - 我的公司运行 SQL Server 24/7 并将搜索索引保持在相同的控制空间中对我们来说有很多优势(......而且我确实意识到我不会获得精确的事务一致性,没关系)。

问题:无论我做什么,在使用 WinAPI(通过 System.IO.FileStream)写入 SQL FileTable UNC 共享中的文件时,似乎都会产生大约 200 毫秒(+- 20-30 毫秒)的开销。这对于 Lucene.NET 来说很重要,因为直接对我的本地文件系统执行索引写入操作大约需要 50 毫秒,而对 FileTable 的相同操作需要大约 2-3 秒!

为了彻底检查这一点,我创建了另一个实验,该实验以 10KB、1MB 和 10MB 写入 3 个新(创建)文件。我将这 3 个文件写到:

  1. 本地目录 (c:\Search\\)
  2. 通过 UNC 路径 (\\\127.0.0.1\\Search\\) 的非 FileTable 共享
  3. FileTable UNC 路径 (\\\127.0.0.1\[instance-share]\\search_index\\)

使用System.Diagnostics.Stopwatch,写入本地目录速度如预期最快,写入非FileTable 共享较慢但可比较,FileTable 慢一个数量级。有趣的是 2 个较大的文件大小在案例 2 和 3 中的表现相似,这让我相信文件创建的开销与时间有关。

问题:有没有人更深入地了解为什么使用 FileTable 创建文件如此“慢”?

这是一个并发活动很少的开发虚拟机(4GB RAM,2 个 vCPU,可能存在一些 IO 争用,但这些测试旨在进行相对比较)。插入到 SQL Server 中,在这个盒子上插入一个微不足道的行几乎没有达到 1 毫秒。

我手头没有代码,但很快就会发布编辑(带有确切的时间) - 它非常简单,只需将 4K 块的静态初始化字节数组循环写入所需的大小。

我确实实施了以下建议,并调整了 SMB 堆栈,性能没有差异: http://blogs.msdn.com/b/blogdoezequiel/archive/2011/02/11/best-practices-on-filestream-implementations.aspx#.UkbEYtKshcZ

编辑:输出测试控制台的计时:

Writing files for directory: c:\Search
        Writing file size : 10240
        Writing file size : 1048576
        Writing file size : 10485760
Writing files for directory: \\127.0.0.1\Search
        Writing file size : 10240
        Writing file size : 1048576
        Writing file size : 10485760
Writing files for directory: \\127.0.0.1\Sql2012\FIndex\search_index
        Writing file size : 10240
        Writing file size : 1048576
        Writing file size : 10485760

Write Timings
---------------------------------------------------------------
Paths (rows): Local, Shared, SQL File Table
Sizes (columns): 10KB, 1MB, 10MB
---------------------------------------------------------------
Local:  3                       2                       17
Share:  28                      31                      64
FTable: 205                     249                     317

源代码(很简单,为了完整而贴出来): 省略了 ASCII 艺术的控制台主:

private static readonly string[] paths = new string[] 
{
    @"c:\Search",
    @"\\127.0.0.1\Search",
    @"\\127.0.0.1\Sql2012\FIndex\search_index"
};

private static readonly int[] sizes = new int[] 
{
    1024 * 10,
    1024 * 1024,
    1024 * 1024 * 10
};

static void Main(string[] args)
{
    // Directory: Size 1, 2, 3
    var timings = new long[3, 3];
    var stopwatch = new Stopwatch();
    for(var x = 0; x < 3; x++)
    {
        Console.WriteLine("Writing files for directory: {0}", paths[x]);
        for(var y = 0; y < 3; y++)
        {
            Console.WriteLine("\tWriting file size : {0}", sizes[y]);
            string fileName = Path.Combine(paths[x], Guid.NewGuid().ToString() + ".bin");
            stopwatch.Start();
            FileIOTestHelper.WriteFile(fileName, sizes[y]);
            stopwatch.Stop();
            timings[x, y] = stopwatch.ElapsedMilliseconds;
            stopwatch.Reset();
        }
    }

// ascii art display code
}

实现类:

public static class FileIOTestHelper
{
    private static readonly byte[] testBuffer = CreateBuffer();

    private static byte[] CreateBuffer()
    {
        var buffer = new byte[4096];
        for (var i = 0; i < 4096; i++)
        {
            buffer[i] = (byte)(i % 256);
        }

        ForceIOJit(buffer);
        return buffer;
    }

    private static void ForceIOJit(byte[] initBuffer)
    {
        // Shouldn't matter, but eliminating any possible warm up cost.
        using (var fs = new FileStream(Path.GetTempFileName(), FileMode.Open))
        {
            fs.Write(initBuffer, 0, 4096);
            fs.Flush();
        }
    }

    public static void WriteFile(string name, int sizeInBytes)
    {
        var count = sizeInBytes / 4096;
        var remainder = sizeInBytes % 4096;
        using (var fs = new FileStream(name, FileMode.Create))
        {
            for (int i = 0; i < count; i++)
            {
                fs.Write(testBuffer, 0, 4096);
            }

            if (remainder > 0)
            {
                fs.Write(testBuffer, 0, remainder);
            }   

            fs.Flush();
        }
    }
}

【问题讨论】:

  • 我的一位同事是 MCS 的顾问,并且非常了解 SQL Server,他对这个问题做了一些实验,发现 FileTable 的性能对 FileStream 缓冲区大小很敏感。它似乎在 64KB 时表现最好。我将发布带有详细信息的编辑。

标签: c# sql-server sql-server-2012 lucene.net filetable


【解决方案1】:

经过大量测试后,我遇到的延迟是一致的,并且对缓冲区大小不敏感。我接受这是 FileTable 的一个限制,它对于非常健谈的 IO 来说不太理想。

【讨论】:

  • 不,前面没有。使用 Elasticsearch :>.
  • 我目前正在使用 SQL SERVER 2016 并将数千个 xml 文件放入文件流目录中。我对插入文件流目录需要多长时间印象深刻。删除物理 XML 或通过 SQL 使用 xml 记录的 openquery 插入没有区别。它似乎以每秒一个 xml 文件的速度复制。这很糟糕。我正在尝试使用单个 zip 文件在目录中解压缩。我还在尝试在引入新文件时停止桌面上的人口。到目前为止,看起来不太好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-05
相关资源
最近更新 更多