不同服务器上的 SQL Server 还原数据库时差

SQL Server Restore Database Time Difference on Different Servers

提问人:David C 提问时间:11/17/2023 更新时间:11/17/2023 访问量:31

问:

此问题是从 Stack Overflow 迁移而来的,因为它可以在 Database Administrators Stack Exchange 上找到答案。6 天前迁移

我有两台服务器:

服务器 A - 安装了 64 GB RAM 的 Windows 2019 Standard 服务器。 - 具有 1.26 TB 内存的 D 驱动器

服务器 B - 安装了 200 GB RAM 的 Windows 2022 Standard 服务器。 - 具有 2.5 TB 内存的 D 驱动器

作为代理作业的一部分,我使用 azcopy 将 .bak 文件 (~90gb) 从服务器 C 复制到 服务器 A 和 B。复制到任一服务器所需的时间相同。 在服务器 A 上还原备份大约需要 40 分钟。在服务器 B 上 恢复始终需要 60 分钟以上。服务器 A 上其余的 ETL 比 B 快 40 分钟左右,它们运行相同的作业。

什么原因可能导致恢复速度的差异?

sql-服务器

评论

1赞 Charlieface 11/17/2023
硬件?驱动器是同一型号的吗?格式相同(NTFS,块大小?司机一样吗?两者都完全是最新的?
0赞 David C 11/20/2023
硬件相同。驱动相同的模型。格式相同。驱动程序在服务器 B 上的版本稍新。 @Charlieface
0赞 David C 11/20/2023
我的 DBA 经验非常有限,我是一名开发人员,只是想了解可能导致这种差异的原因。@Charlieface
0赞 N8allan 7/3/2015
.NET 的安全/允许文件名清理器的可能重复项

答:

-3赞 Bart McEndree 11/17/2023 #1

本文可能会解释性能差异。https://www.diskpart.com/server-2022/windows-server-2022-very-slow-0725.html

Server 2022 对硬件有更高的要求,以实现更高的性能;如果您的 Windows Server 性能较差,则可能与硬件和其他原因有关。

210赞 Jonathan Allen 12/2/2008 #2

呃,我讨厌人们试图猜测哪些字符是有效的。除了完全不可移植(总是考虑 Mono)之外,前面的两条评论都遗漏了更多的 25 个无效字符。

foreach (var c in Path.GetInvalidFileNameChars()) 
{ 
  fileName = fileName.Replace(c, '-'); 
}

或者在 VB 中:

'Clean just a filename
Dim filename As String = "salmnas dlajhdla kjha;dmas'lkasn"
For Each c In IO.Path.GetInvalidFileNameChars
    filename = filename.Replace(c, "")
Next

'See also IO.Path.GetInvalidPathChars

评论

10赞 Stefano Ricciardi 6/13/2011
此解决方案将如何处理名称冲突?似乎多个字符串可以匹配到单个文件名(例如“Hell?”和“Hell*”)。如果你只删除有问题的字符,那就好了;否则,您需要小心处理名称冲突。
2赞 Jack 2/26/2013
Filesytem 的名称(和路径)长度限制如何?保留文件名 (PRN CON) 呢?如果需要存储数据和原始名称,可以使用 2 个具有 Guid 名称的文件:guid.txt 和 guid.dat
1赞 WLin 3/19/2013
我只是想提一下,这个函数允许空格字符。
7赞 Paul Knopf 3/22/2013
一行,有趣的结果 = Path.GetInvalidFileNameChars()。Aggregate(result, (current, c) => current.替换(c, '-'));
1赞 Marcus 6/20/2015
@PaulKnopf,您确定 JetBrain 没有该代码的版权吗;)
22赞 Aaron Wagner 12/2/2008 #3

我同意 Grauenwolf 的观点,并强烈推荐Path.GetInvalidFileNameChars()

这是我的 C# 贡献:

string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(), 
      c => file = file.Replace(c.ToString(), String.Empty));

p.s. -- 这比它应该的更隐晦 -- 我试图简明扼要。

评论

3赞 BlueRaja - Danny Pflughoeft 4/12/2012
为什么你会在世界上使用而不是仅仅在这里Array.ForEachforeach
9赞 Michael Petito 10/11/2012
如果你想更简洁/隐晦:Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
0赞 Jonathan Allen 11/22/2014
@BlueRaja-DannyPflughoeft 因为你想让它变慢?
0赞 Ryan Buddicom 11/24/2014
@Johnathan Allen,是什么让您认为 Foreach 比 Array.ForEach 更快?
5赞 Jonathan Allen 2/6/2018
@rbuddicom Array.ForEach 接受一个委托,这意味着它需要调用一个不能内联的函数。对于短字符串,您最终可能会在函数调用开销上花费比实际逻辑更多的时间。.NET Core 正在寻找“去虚拟化”调用的方法,从而减少开销。
8赞 Keith 5/28/2009 #4

如果您想快速去除所有特殊字符,这有时对于文件名来说更易读,这很好用:

string myCrazyName = "q`w^e!r@t#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u";
string safeName = Regex.Replace(
    myCrazyName,
    "\W",  /*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/
    "",
    RegexOptions.IgnoreCase);
// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu"

评论

1赞 Ishmael 7/29/2014
实际上比非字母数字 () 匹配更多。所有Unicode的“单词”字符(русский中文等)也不会被替换。但这是一件好事。\W[^A-Za-z0-9_]
1赞 awe 9/23/2015
唯一的缺点是这也删除了,因此您必须先提取扩展名,然后再添加它。.
13赞 sidewinderguy #5

这是我现在正在使用的函数(感谢 jcollum 的 C# 示例):

public static string MakeSafeFilename(string filename, char replaceChar)
{
    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {
        filename = filename.Replace(c, replaceChar);
    }
    return filename;
}

为了方便起见,我只是把它放在“助手”类中。

45赞 Squirrel 9/9/2010 #6

要去除无效字符:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());

要替换无效字符:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars and an _ for invalid ones
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray());

要替换无效字符(并避免潜在的名称冲突,如 Hell* vs Hell$):

static readonly IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A")
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray());
34赞 Dour High Arch 9/10/2010 #7

这个问题以前已经问过很多了,而且正如之前多次指出的那样,是不够的。IO.Path.GetInvalidFileNameChars

首先,有许多名称(如 PRN 和 CON)是保留的,不允许用于文件名。仅在根文件夹中不允许使用其他名称。也不允许使用以句点结尾的名称。

其次,存在各种长度限制。在此处阅读 NTFS 的完整列表。

第三,您可以附加到具有其他限制的文件系统。例如,ISO 9660 文件名不能以“-”开头,但可以包含它。

第四,如果两个进程“任意”选择相同的名称,你会怎么做?

通常,使用外部生成的文件名名称是一个坏主意。我建议生成自己的私有文件名,并在内部存储人类可读的名称。

评论

14赞 CubanX 3/15/2011
虽然你在技术上是准确的,但 GetInvalidFileNameChars 适用于你使用它的 80%+ 情况,因此它是一个很好的答案。我认为,您的回答作为对已接受答案的评论会更合适。
5赞 rtpHarry 10/16/2012
我同意 DourHighArch 的观点。在内部将文件另存为 guid,根据存储在数据库中的“友好名称”引用该文件。不要让用户控制您在网站上的路径,否则他们会试图窃取您的 web.config。如果合并 url 重写以使其干净,则它仅适用于数据库中匹配的友好 url。
1赞 cjbarth 4/10/2013 #8

我发现使用它既快速又易于理解:

<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
    Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function

这之所以有效,是因为 a 是数组,并且有一个接受数组的构造函数字符串。stringIEnumerablecharstringchar

5赞 Ronnie Overby 4/18/2013 #9
static class Utils
{
    public static string MakeFileSystemSafe(this string s)
    {
        return new string(s.Where(IsFileSystemSafe).ToArray());
    }

    public static bool IsFileSystemSafe(char c)
    {
        return !Path.GetInvalidFileNameChars().Contains(c);
    }
}
6赞 George Birbilis 7/13/2013 #10

这是我刚刚添加到 ClipFlair (http://github.com/Zoomicon/ClipFlair) StringExtensions 静态类(Utils.Silverlight 项目)中的内容,基于从上面 Dour High Arch 发布的相关 stackoverflow 问题的链接中收集的信息:

public static string ReplaceInvalidFileNameChars(this string s, string replacement = "")
{
  return Regex.Replace(s,
    "[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]",
    replacement, //can even use a replacement string of any length
    RegexOptions.IgnoreCase);
    //not using System.IO.Path.InvalidPathChars (deprecated insecure API)
}

评论

1赞 Philipp Michalski 6/23/2022
只是缺少编译正则表达式并缓存它。
0赞 George Birbilis 6/24/2022
是的,理想情况下,您会在首次使用时进行编译和缓存。将使用静态字段并检查它是否为 null。如果是,将编译并缓存到它,然后使用它。如果多个线程同时调用它第一次,则可能会发生争用条件,但由于所有线程都会计算和缓存相同的内容,唯一的惩罚是其他线程的额外计算(最后一个线程将成功将其计算值持久化到静态字段)
14赞 csells 9/20/2013 #11

这是我的版本:

static string GetSafeFileName(string name, char replace = '_') {
  char[] invalids = Path.GetInvalidFileNameChars();
  return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}

我不确定 GetInvalidFileNameChars 的结果是如何计算的,但“Get”表明它很重要,所以我缓存了结果。此外,这只会遍历输入字符串一次,而不是多次,就像上面的解决方案一样,迭代无效字符集,一次替换源字符串中的一个字符。另外,我喜欢基于 Where 的解决方案,但我更喜欢替换无效的字符而不是删除它们。最后,我的替换正好是一个字符,以避免在我遍历字符串时将字符转换为字符串。

我说了所有没有做分析的事情——这个对我来说只是“感觉”很好。: )

评论

1赞 TrueWill 10/2/2015
你可以避免 O(n) 枚举 - 微优化。new HashSet<char>(Path.GetInvalidFileNameChars())
2赞 ecklerpa 2/20/2016 #12
private void textBoxFileName_KeyPress(object sender, KeyPressEventArgs e)
{
   e.Handled = CheckFileNameSafeCharacters(e);
}

/// <summary>
/// This is a good function for making sure that a user who is naming a file uses proper characters
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
internal static bool CheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e)
{
    if (e.KeyChar.Equals(24) || 
        e.KeyChar.Equals(3) || 
        e.KeyChar.Equals(22) || 
        e.KeyChar.Equals(26) || 
        e.KeyChar.Equals(25))//Control-X, C, V, Z and Y
            return false;
    if (e.KeyChar.Equals('\b'))//backspace
        return false;

    char[] charArray = Path.GetInvalidFileNameChars();
    if (charArray.Contains(e.KeyChar))
       return true;//Stop the character from being entered into the control since it is non-numerical
    else
        return false;            
}
5赞 Bart Vanseer 4/28/2017 #13

为什么不将字符串转换为 Base64 等效项,如下所示:

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));

如果你想把它转换回来,以便你可以阅读它:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));

我用它来保存PNG文件,这些文件具有随机描述中的唯一名称。

评论

0赞 s k 9/22/2022
Base64 不是路径安全的。它包含 /、+、= 等字符。此外,它有大写和小写两种形式,不适用于不区分大小写的Windows文件系统
1赞 AnonBird 1/11/2019 #14

许多 anwer 建议使用这对我来说似乎是一个糟糕的解决方案。我鼓励您使用白名单而不是黑名单,因为黑客最终总会找到绕过它的方法。Path.GetInvalidFileNameChars()

下面是一个您可以使用的代码示例:

    string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
    foreach (char c in filename)
    {
        if (!whitelist.Contains(c))
        {
            filename = filename.Replace(c, '-');
        }
    }
2赞 Roni Tovi 9/12/2019 #15

从我的旧项目中,我找到了这个解决方案,它已经完美地运行了 2 年多。我正在用“!”替换非法字符,然后检查双倍!!'s,使用你自己的字符。

    public string GetSafeFilename(string filename)
    {
        string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));

        while (res.IndexOf("!!") >= 0)
            res = res.Replace("!!", "!");

        return res;
    }
0赞 Michael Murphy 5/4/2023 #16

我接受了 Jonathan Allen 的答案,并制作了一个可以在任何字符串上调用的扩展方法。

public static class StringExtensions
{
    public static string ReplaceInvalidFileNameChars(this string input, char replaceCharacter = '-')
    {
        foreach (char c in Path.GetInvalidFileNameChars())
        {
            input = input.Replace(c, replaceCharacter);
        }

        return input;
    }
}

然后可以像这样使用:

string myFileName = "test > file ? name.txt";

string myValidFileName1 = myFileName.ReplaceInvalidFileNameChars();
string myValidFileName2 = myFileName.ReplaceInvalidFileNameChars('');
string myValidFileName3 = myFileName.ReplaceInvalidFileNameChars('_');