首页 文章

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

提问于
浏览
227

我正在使用 SQL Server 2005 为我学校的小型网络应用程序创建一个数据库 .
关于 varcharnvarchar 的问题,我看到了几个思想流派:

  • 除非您处理大量国际化数据,否则请使用 varchar ,然后使用 nvarchar .

  • 只需使用 nvarchar 即可 .

我开始看到视图2的优点 . 我知道nvarchar确实占用了两倍的空间,但这不一定是一个大问题,因为这只是为几百名学生存储数据 . 对我而言,似乎最简单的是不要担心它并且只允许一切使用nvarchar . 还是有什么我想念的?

14 回答

  • 3

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

  • 219

    始终使用nvarchar .

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

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

  • 1

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

    你需要存储中文等脚本吗?是还是不是...

    并从MS BOL“Storage and Performance Effects of Unicode

    Edit

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

    SQL Server uses high CPU when searching inside nvarchar strings

  • 7

    始终如一!将VARCHAR加入到NVARCHAR中会有很大的性能影响 .

  • 4

    nvarchar将在内存,存储,工作集和索引方面产生巨大的开销,因此如果规范规定它确实是必要的,那就不要打扰了 .

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

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

  • 40

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

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

  • 6

    我不愿在这里添加另一个答案,因为已经有很多,但需要做出一些尚未制作或未制作清楚的要点 .

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

    你唯一应该使用“永远”这个词的时候就是建议“总是做最适合这种情况的事情” . 当然,这通常很难确定,特别是当试图 balancer 开发时间的短期收益时(经理:“我们需要这个功能 - 你直到现在才知道 - 一周前!”)期限维护成本(经理最初迫使团队在为期3周的冲刺中完成为期3个月的项目:“为什么我们遇到这些性能问题?我们怎么可能做到没有灵活性的X?我们负担不起一两个冲刺来解决这个问题 . 我们可以在一周内完成什么工作,以便我们可以回到我们的优先项目?我们肯定需要在设计上花更多的时间,所以这不会继续发生!“) .

    Second: @gbn 's answer touches on some very important points to consider when making certain data modeling decisions when the path isn' t 100%清除 . 但还有更多要考虑的因素:

    • 大小的事务日志文件

    • 复制所需的时间(如果使用复制)

    • 时间吧采取ETL(如果ETLing)

    • 将日志发送到远程系统并恢复所需的时间(如果使用日志传送)

    • 备份大小

    • 完成备份所需的时间

    • 进行恢复所需的时间长度(这可能在某一天很重要;-)
      tempdb需要

    • 大小

    • 触发器的性能(对于存储在tempdb中的已插入和已删除的表)

    • 行版本控制的性能(如果使用SNAPSHOT ISOLATION,因为版本存储在tempdb中)
      当CFO表示他们去年在SAN上花费了100万美元,因此他们不会再授权另外25万美元的额外存储空间时获得新磁盘空间的能力

    • 执行INSERT和UPDATE操作所需的时间

    • 进行索引维护所需的时间

    • 等等等

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

    Third: 虽然有些答案错误地关注"this is a small app"方面,有些答案正确地建议"use what is appropriate",但没有一个答案为O.P提供了真正的指导 . 问题中提到的一个重要细节是这是他们学校的网页 . 大!所以我们可以建议:

    • 学生和/或学院名称的字段应该是 NVARCHAR ,因为随着时间的推移,来自其他文化的名称越来越有可能出现在这些地方 .

    • 但是对于街道地址和城市名称?该应用程序的目的没有说明(它会有所帮助),但假设地址记录(如果有的话)仅适用于特定地理区域(即单一语言/文化),则使用 VARCHAR 与相应的代码页(这是从该领域的整理确定的) .

    • 如果存储状态和/或国家ISO代码(无需存储 INT / TINYINT ,因为ISO代码是固定长度,人类可读,以及标准:),对于两个字母代码使用 CHAR(2) ,如果使用3个字母代码则使用 CHAR(3) . 并考虑使用二进制校对,例如 Latin1_General_100_BIN2 .

    • 如果存储邮政编码(即邮政编码),请使用 VARCHAR ,因为它是国际标准,从不使用A-Z以外的任何字母 . 并且仍然使用 VARCHAR 即使只存储美国邮政编码而不是INT,因为邮政编码不是数字,它们是字符串,其中一些具有领先的"0" . 并考虑使用二进制排序规则,如 Latin1_General_100_BIN2 .

    • 如果存储电子邮件地址和/或URL,请使用 NVARCHAR ,因为这两者现在都可以包含Unicode字符 .

    • 等等......

    Fourth: 既然你有 NVARCHAR 数据占用的空间是它所需的空间的两倍,而且数据非常适合 VARCHAR ("fits nicely" =不会变成"?")并且不知何故,好像通过魔法,应用程序确实增长了,现在有了在至少其中一个字段中有数百万条记录,其中大多数行是标准ASCII,但有些包含Unicode字符,因此您必须保留 NVARCHAR ,请考虑以下事项:

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

    • NCHAR(1 - 4000)NVARCHAR(1 - 4000) 使用Standard Compression Scheme for Unicode,但仅在SQL Server 2008 R2中启动,并且仅适用于IN ROW数据,而不是OVERFLOW!这似乎比常规的ROW / PAGE压缩算法更好 .

    • NVARCHAR(MAX)XML (我猜也是 VARBINARY(MAX)TEXTNTEXT )数据是IN ROW(不是LOB或OVERFLOW页面中的行),至少可以进行PAGE压缩,但不能进行ROW压缩 . 当然,PAGE压缩取决于行内值的大小:我用VARCHAR(MAX)测试并看到6000个字符/字节行不会压缩,但是4000个字符/字节行 .

    • 任何OFF ROW数据,LOB或OVERLOW =无压缩!

    • 如果使用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)
           );
    
    • 您可以将传入的值GZIP转换为 VARBINARY(MAX) ,然后在出路时解压缩:

    • 对于SQL Server 2005 - 2014:您可以使用SQLCLR . SQL#(我写的SQLCLR库)附带 Util_GZipUtil_GUnzip 免费版

    • 对于SQL Server 2016及更高版本:您可以使用内置的 COMPRESSDECOMPRESS 函数,它们也是GZip .

    • 如果使用SQL Server 2017或更高版本,则可以考虑将表格设为Clustered Columnstore Index .

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

  • 0

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

    我还没有遇到来自较大索引等的任何与性能相关的问题 . 索引确实使用了更多内存,但内存很便宜 .

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

    因人而异 .

  • 59

    一般来说;从具有最少约束的最昂贵数据类型开始 . Put it in production . 如果性能开始成为问题,请找出实际存储在那些 nvarchar 列中的内容 . 那里有没有符合 varchar 的字符吗?如果没有,请切换到varchar . 在您知道疼痛的位置之前,不要尝试进行预先优化 . 我的猜测是在可预见的未来 the choice between nvarchar/varchar is not what's going to slow down your application . 应用程序的其他部分将进行性能调优,为您带来更多好处 .

  • 143

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

  • 7

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

    • 库存和定价的FTP源 - 当varchar工作正常时,项目描述和其他文本在nvarchar中 . 将这些转换为varchar减少了几乎一半的文件大小,真的有助于上传 .

    • 以上场景工作正常,直到有人在项目描述中放置一个特殊字符(可能是商标,不记得了)

    我仍然不会每次都使用nvarchar over varchar . 如果对特殊字符有任何疑问或可能,我使用nvarchar . 当我100%控制填充字段的内容时,我发现我主要使用varchar .

  • 14

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

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

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

  • 20

    有'll be exceptional instances when you' ll想要故意限制数据类型以确保它不包含某个集合中的字符 . 例如,我有一个场景,我需要将域名存储在数据库中 . 域名国际化在当时并不可靠,因此最好限制基层的输入,并有助于避免任何潜在的问题 .

  • 10

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

相关问题