varchar和nvarchar SQL Server数据类型之间的主要性能差异是什么?

时间:2008-08-29 21:41:57

标签: sql-server sql-server-2005 storage varchar nvarchar

我正在使用SQL Server 2005在我的学校开发一个小型网络应用的数据库 关于varchar vs nvarchar

的问题,我看到了几种思想流派
  1. 除非您处理大量国际化数据,否则请使用varchar,然后使用nvarchar
  2. 只需使用nvarchar即可。
  3. 我开始看到视图2的优点。我知道nvarchar确实占用了两倍的空间,但这不一定是一个大问题,因为这只会存储几百名学生的数据。对我而言,似乎最简单的是不要担心它并且只允许一切使用nvarchar。或者有什么我想念的东西?

14 个答案:

答案 0 :(得分:220)

磁盘空间不是问题...但内存和性能将是。 双页读取,双索引大小,奇怪的LIKE和=常量行为等

你需要存储中文等脚本吗?是或否......

来自MS BOL“Storage and Performance Effects of Unicode

修改

最近的SO问题突出了nvarchar性能有多糟糕......

SQL Server uses high CPU when searching inside nvarchar strings

答案 1 :(得分:143)

始终使用nvarchar。

对于大多数应用程序,您可能永远不需要双字节字符。但是,如果您需要支持双字节语言并且在数据库模式中只有单字节支持,那么在整个应用程序中返回和修改是非常昂贵的。

将一个应用程序从varchar迁移到nvarchar的成本远远超过您在大多数应用程序中使用的额外磁盘空间。

答案 2 :(得分:60)

保持一致!将VARCHAR加入到NVARCHAR中会有很大的性能影响。

答案 3 :(得分:41)

nvarchar在内存,存储,工作集和索引方面会有很大的开销,所以如果规范要求它确实从不,那就不要打扰了。

我不会有一个坚硬而快速的“始终nvarchar”规则,因为在许多情况下它可能完全浪费 - 特别是来自ASCII / EBCDIC的ETL或通常是键和外键的标识符和代码列。

另一方面,有很多列的情况,我肯定会提前问这个问题,如果我没有立即得到一个快速的答案,我会把列设为nvarchar。

答案 4 :(得分:21)

对于您的应用程序,nvarchar很好,因为数据库大小很小。说“总是使用nvarchar”是一个巨大的过度简化。如果你不需要存储像汉字或其他疯狂字符这样的东西,使用VARCHAR,它将使用更少的空间。我的前任在我目前的工作中使用NVARCHAR设计了一些不需要的东西。我们最近将它切换到VARCHAR并在该表上保存了15 GB(它被高度写入)。此外,如果您在该表上有索引并且想要包含该列或创建复合索引,那么您只需将索引文件大小设置得更大。

在你的决定中要深思熟虑;在SQL开发和数据定义中,似乎很少有“默认答案”(当然,除了不惜一切代价避免使用游标)。

答案 5 :(得分:16)

我在这里添加另一个答案仍然犹豫不决,因为已经有很多,但需要做出一些尚未做出或未做出明确的要点。

首先: 始终使用NVARCHAR。这是一种非常危险且往往代价高昂的态度/态度。并且最好说" 从不使用游标"因为它们有时是解决特定问题的最有效方法,并且执行WHILE循环的常见解决方法几乎总是比正确完成的Cursor慢。

你唯一应该使用“#34;始终"是建议"始终做最适合的情况"。这通常很难确定,特别是当试图平衡开发时间的短期收益时(经理:"我们需要这个功能 - 你直到现在才知道 - 一周前!")具有长期维护成本(经理最初迫使团队在为期3周的冲刺中完成为期3个月的项目:"为什么我们遇到这些性能问题?我们怎么可能做到这一点? X没有灵活性?我们不能提供一两个冲刺来解决这个问题。我们可以在一周内完成什么工作以便我们可以回到我们的优先项目?我们肯定需要花更多时间在设计上这不会继续发生!")。

第二: @ gbn的回答涉及在路径不是100%明确时做出某些数据建模决策时要考虑的一些非常重要的要点。但还有更多需要考虑的问题:

  • 事务日志文件的大小
  • 复制所需的时间(如果使用复制)
  • ETL所需的时间(如果是ETLing)
  • 将日志发送到远程系统并恢复(如果使用日志传送)
  • 所需的时间
  • 备份大小
  • 完成备份所需的时间
  • 进行恢复所需的时间长度(这可能在某一天很重要; - )
  • tempdb所需的大小
  • 触发器的性能(对于存储在tempdb中的已插入和已删除的表)
  • 行版本控制的性能(如果使用SNAPSHOT ISOLATION,因为版本存储位于tempdb中)
  • 当CFO表示他们去年在SAN上花费了100万美元,因此他们不会再授权另外25万美元用于额外存储时获得新磁盘空间的能力
  • 执行INSERT和UPDATE操作所需的时间长度
  • 进行索引维护所需的时间
  • 等等等。

浪费空间对整个系统有一个巨大的级联效果。我写了一篇关于这个主题的明确细节的文章:Disk Is Cheap! ORLY?(需要免费注册;抱歉我不能控制该政策)。

第三:虽然有些答案错误地关注"这是一个小应用程序"方面,有些人正确地建议使用适当的东西",没有一个答案为O.P提供真正的指导。问题中提到的一个重要细节是这是他们学校的网页。大!所以我们可以建议:

  • 学生和/或教师姓名的字段应该可能NVARCHAR,因为随着时间的推移,其他文化的名字越来越有可能出现在这些地方。
  • 但对于街道地址和城市名称?该应用程序的目的没有说明(它会有所帮助),但假设地址记录(如果有的话)仅适用于特定地理区域(即单一语言/文化),则使用VARCHAR适当的代码页(根据字段的整理确定)。
  • 如果存储州和/或国家ISO代码(无需存储INT / TINYINT,因为ISO代码是固定长度,人类可读且标准良好:)使用CHAR(2)两个字母代码和CHAR(3)如果使用3个字母代码。并考虑使用二进制排序规则,例如Latin1_General_100_BIN2
  • 如果存储邮政编码(即邮政编码),请使用VARCHAR,因为它是国际标准,绝不使用A-Z以外的任何字母。是的,即使只存储美国邮政编码而不是INT也使用VARCHAR,因为邮政编码不是数字,它们是字符串,其中一些有一个领先的" 0"。并考虑使用二进制排序规则,例如Latin1_General_100_BIN2
  • 如果存储电子邮件地址和/或网址,请使用NVARCHAR,因为这两者现在都包含Unicode字符。
  • 依旧......

第四:现在您的NVARCHAR数据占用的空间比适合VARCHAR的数据所需空间的两倍("非常适合& #34; =没有变成"?")并且不知何故,好像通过魔术,应用程序确实增长了,现在至少有一个这样的领域有数百万条记录大多数行是标准ASCII,但有些包含Unicode字符,因此您必须保留NVARCHAR,请考虑以下事项:

  1. 如果您使用的是SQL Server 2008 - 2016 RTM 在Enterprise Edition上,或者如果使用SQL Server 2016 SP1(在所有版本中都提供数据压缩)或更新版本,那么您可以启用Data Compression。数据压缩可以(但不会"总是")压缩NCHARNVARCHAR字段中的Unicode数据。决定因素是:

    1. NCHAR(1 - 4000)NVARCHAR(1 - 4000)使用Standard Compression Scheme for Unicode,但仅从SQL Server 2008 R2开始,仅适用于IN ROW数据,而不是OVERFLOW!这似乎比常规的ROW / PAGE压缩算法更好。
    2. NVARCHAR(MAX)XML(我猜也是VARBINARY(MAX)TEXTNTEXT)数据是IN ROW(不是LOB中的行或OVERFLOW页面)至少可以进行PAGE压缩,但 ROW压缩。当然,PAGE压缩取决于行内值的大小:我使用VARCHAR(MAX)测试并看到6000个字符/字节行不会压缩,但是4000个字符/字节行。
    3. 任何OFF ROW数据,LOB或OVERLOW =没有压缩你!
  2. 如果在Enterprise Edition上使用SQL Server 2005或2008 - 2016 RTM和,则可以有两个字段:一个VARCHAR和一个NVARCHAR。例如,假设您存储的URL主要是所有基本ASCII字符(值0 - 127),因此适合VARCHAR,但有时会包含Unicode字符。您的架构可以包含以下3个字段:

      ...
      URLa VARCHAR(2048) NULL,
      URLu NVARCHAR(2048) NULL,
      URL AS (ISNULL(CONVERT(NVARCHAR([URLa])), [URLu])),
      CONSTRAINT [CK_TableName_OneUrlMax] CHECK (
                        ([URLa] IS NOT NULL OR [URLu] IS NOT NULL)
                    AND ([URLa] IS NULL OR [URLu] IS NULL))
    );
    

    在此模型中,您只能从[URL]计算列中 SELECT。对于插入和更新,您可以通过查看转换是否更改传入值来确定要使用的字段,传入值必须为NVARCHAR类型:

    INSERT INTO TableName (..., URLa, URLu)
    VALUES (...,
            IIF (CONVERT(VARCHAR(2048), @URL) = @URL, @URL, NULL),
            IIF (CONVERT(VARCHAR(2048), @URL) <> @URL, NULL, @URL)
           );
    
  3. 您可以将传入的值GZIP转换为VARBINARY(MAX),然后在出路时解压缩:

    • 对于SQL Server 2005 - 2014:您可以使用SQLCLR。 SQL#(我写的SQLCLR库)附带免费版 Util_GZip Util_GUnzip
    • 对于SQL Server 2016及更高版本:您可以使用内置的COMPRESSDECOMPRESS函数,这些函数也是GZip。
  4. 如果使用SQL Server 2017或更高版本,您可以考虑将表格设为群集列存储索引。

  5. 虽然这还不是一个可行的选择,但SQL Server 2019在VARCHAR / CHAR数据类型中引入了对UTF-8的本机支持。目前有太多的bug用于它,但如果它们被修复,那么这是一些场景的一个选项。有关此新功能的详细分析,请参阅我的帖子&#34; Native UTF-8 Support in SQL Server 2019: Savior or False Prophet?&#34 ;.

答案 6 :(得分:10)

由于你的应用程序很小,使用nvarchar而不是varchar基本上没有明显的成本增加,如果你需要存储unicode数据,你可以节省潜在的麻烦。

答案 7 :(得分:8)

一般来说;从具有最少约束的最昂贵数据类型开始。 投入生产。如果性能开始成为问题,请找出实际存储在nvarchar列中的内容。那里有没有符合varchar的字符吗?如果没有,请切换到varchar。在您知道疼痛的位置之前,不要尝试进行预先优化。我的猜测是 nvarchar / varchar之间的选择不会在可预见的未来减慢你的应用程序的速度。应用程序的其他部分中,性能调优将为您提供更多 bang for the bucks

答案 8 :(得分:7)

在过去的几年里,我们所有的项目都使用了NVARCHAR,因为所有这些项目都是多语言的。从外部源导入的数据(例如ASCII文件等)在插入数据库之前上转换为Unicode。

我还没有遇到大型索引等与性能相关的问题。索引会占用更多内存,但内存便宜。

无论是使用存储过程还是动态构造SQL,都要确保所有字符串常量都以N为前缀(例如SET @foo = N'Hello world。';),因此常量也是Unicode。这样可以避免在运行时进行任何字符串类型转换。

因人而异。

答案 9 :(得分:7)

我可以从中获得经验,谨防nvarchar。除非您绝对需要,否则此数据字段类型会破坏较大数据库的性能。我继承了一个在性能和空间方面受到损害的数据库。我们能够将30GB的数据库大小减少70%!还有其他一些修改可以帮助提高性能,但我确信varchar也有很大的帮助。如果您的数据库有可能将表增长到一百万+,则不惜一切代价远离nvarchar

答案 10 :(得分:4)

我经常在工作中处理这个问题:

  • 库存和定价的FTP Feed - 当varchar工作正常时,项目描述和其他文本在nvarchar中。将这些文件转换为varchar可将文件大小减少一半,并且确实有助于上传。

  • 上述情况正常,直到有人在项目描述中添加特殊字符(可能是商标,不记得了)

我每次都不会在varchar上使用nvarchar。如果对特殊字符有任何疑问或可能,我使用nvarchar。我发现我主要使用varchar,因为我100%控制填充字段的内容。

答案 11 :(得分:3)

为什么在所有这些讨论中都没有提到过UTF-8?能够存储完整的unicode字符范围并不意味着必须始终为每个字符分配两个字节(或“代码点”以使用UNICODE术语)。所有ASCII都是UTF-8。 SQL Server是否检查文本是严格ASCII的VARCHAR()字段(即顶部字节位为零)?我希望不会。

如果那时你想存储unicode 想要与旧的仅ASCII应用程序兼容,我认为使用VARCHAR()和UTF-8将是神奇的子弹:它只使用更多的空间它需要。

对于那些不熟悉UTF-8的人,我可以推荐a primer

答案 12 :(得分:2)

当您想要故意限制数据类型以确保包含来自某个集合的字符时,会出现异常情况。例如,我有一个场景,我需要将域名存储在数据库中。域名国际化在当时并不可靠,因此最好限制基层的输入,并有助于避免任何潜在的问题。

答案 13 :(得分:1)

如果你正在使用NVARCHAR只是因为系统存储过程需要它,最常见的事情是莫名其妙sp_executesql,并且你的动态SQL很长,那么从性能角度来看你会更好VARCHAR中的所有字符串操作(连接,替换等),然后将最终结果转换为NVARCHAR并将其提供给proc参数。所以不,不要总是使用NVARCHAR