何时将nvarchar / nchar与SQL Server 2019一起使用?


11

在SQL Server 2019中,Microsoft引入了对和数据类型的UTF-8支持,并说:CHARVARCHAR

根据使用的字符集,此功能可以节省大量存储空间。例如,使用启用了UTF-8的排序规则将具有ASCII字符串的现有列数据类型从NCHAR(10)更改为CHAR(10),可将存储需求减少近50%。这种减少是因为NCHAR(10)需要22个字节来存储,而CHAR(10)对于相同的Unicode字符串则需要12个字节。

UTF-8似乎支持每个脚本,因此基本上我们可以开始在varcharchar列中存储Unicode数据。就像文档中所说的那样,这可以减少表和索引的大小,并且由于读取的数据量更少,因此可以得到更好的性能。

我想知道这是不是意味着我们可以停止使用nvarcharnchar列,它实现UTF-16?

谁能指出一个方案和理由,不使用带UTF编码的char数据类型并继续使用n-chars类型?


您为什么不对其进行测试并进行报告?还让我们知道您从nvarchar转换为varchar花费了多少精力-alter table花了多长时间,花了多少时间测试,以及遇到了什么问题。
Colin't Hart

@ Colin'tHart如果没有已知问题或考虑因素,我正计划迁移数据,因为我相信读取较少的数据将完全改善系统的性能。关于转换-这当然需要时间,特别是如果您的索引具有给定的列-则需要重新构建它们,但是我相信这样做会带来很好的回报。当然,我将很快测试对性能的影响,只寻找是否有必要进行迁移的问题。
gotqn

请注意,使用PAGE或ROW压缩时,SQL Server支持NVarchar列的Unicode压缩。 docs.microsoft.com/en-us/sql/relational-databases/…–
大卫·布朗

1
值得注意的是,如果您要存储“类ASCII数据”,则UTF-8可能会节省空间,但它本身并不是压缩的,因此不应被误认为是。例如,如果您主要在数据库中存储中文名称,那么使用UTF-8 CHAR类型的情况要比使用Unicode类型的情况更糟(使用或不使用压缩,因为最终需要解压缩数据才能进行处理)。还请考虑Windows的本机字符串类型是Unicode,因此通常需要对UTF-8字符串进行解码。涉及的权衡意味着这些N类型不太可能在不久的将来被淘汰。
Jeroen Mostert '18

1
CHAR如果引擎在本地支持直接将字符串作为UTF-8处理,那么UTF-8的#1“杀手级应用” 可能是Linux上的SQL Server-在这里,UTF-8是“本机”字符集(或多或少)并保持字符串UTF-16的效率较低。CHAR当然,在已经使用Windows的地方使用Windows也不会造成伤害,因为归类限制了可以存储的字符从来就没有吸引力。
Jeroen Mostert

Answers:


6

可以减少表和索引的大小(增加了重点)

减少大小只可能是,大部分的人物基本上[space]0 - 9A - Za - z,和一些基本的标点符号。除了特定的字符集(在实际使用中,标准ASCII值32-126)之外,您的大小最好等于NVARCHAR/ UTF-16,或者在许多情况下更大。

我计划迁移数据,因为我相信读取更少的数据将完全改善系统的性能。

小心。UTF-8不是一个神奇的“修复所有问题”开关。在其他所有条件都相同的情况下,是的,减少阅读确实可以提高性能。但是这里的“其他所有东西” 并不相等。即使存储标准ASCII字符(意味着:所有字符均为1个字节,因此与相比,存储的空间也要减少一半NVARCHAR),使用UTF-8也会有轻微的性能损失。我认为问题是由于UTF-8是可变长度编码,这意味着每个字节在读取时都必须进行解释,以便知道它是一个完整字符还是下一个字节是其中的一部分。这意味着所有字符串操作都需要从头开始并逐字节进行。另一方面,NVARCHAR / UTF-16始终为2个字节(即使补充字符也包含两个2个字节的代码点),因此所有内容都可以2个字节的块形式读取。

在我的测试中,即使仅使用标准ASCII字符,将数据存储为UTF-8也不会节省经过的时间,但是绝对会浪费CPU时间。而且没有数据压缩,因此至少使用了更少的磁盘空间。但是,使用压缩时,UTF-8所需的空间仅小1%-1.5%。因此,对于UTF-8,实际上无法节省空间,却需要更长的CPU时间。

使用时,事情变得更加复杂,NVARCHAR(MAX)因为Unicode压缩不适用于该数据类型,即使该值足够小以至于可以存储在行中。但是,如果数据足够小,它仍然应该受益于行压缩或页面压缩(在这种情况下,它实际上变得比UTF-8更快)。但是,行外数据不能使用任何压缩。尽管如此,将表设置为“群集的列存储索引”确实会大大减小其大小NVARCHAR(MAX)(即使使用“群集的列存储索引”时它仍略大于UTF-8)。

任何人都可以指出一种情况和原因,而不是将字符数据类型与UTF编码一起使用

绝对是 实际上,在大多数情况下,我并没有找到令人信服的理由使用它。真正受益于UTF-8的唯一情况是:

  1. 数据大部分是标准ASCII(值0-127)
  2. 它必须是Unicode,因为它可能需要存储比任何单个8位代码页(例如VARCHAR)上都更大的字符
  3. 大多数数据存储在行外(因此页面压缩甚至无法工作)
  4. 出于非查询性能的原因,您有足够的数据需要/想要减小大小(例如,减少备份大小,减少备份/还原所需的时间等)
  5. 您不能使用“群集的列存储索引”(在这种情况下,表的使用可能会使性能变差吗?)

我的测试表明,几乎在所有情况下,NVARCHAR都更快,尤其是在有更多数据时。实际上,21k行平均每行 5k个字符,对于UTF-8,需要165 MB,对于NVARCHAR未压缩的则需要236 MB 。但是运行NVARCHAR时间快了2倍,CPU时间至少快了2倍(有时更多)。尽管如此,它确实占用了71 MB以上的磁盘空间。

除此之外,由于我在此功能中发现了许多错误,因此至少从CTP 2开始,我仍然不建议使用UTF-8。

有关此新功能的详细分析,包括对UTF-16和UTF-8之间差异的解释以及这些错误的列表,请参阅我的文章:

SQL Server 2019中的本机UTF-8支持:救星还是假先知?


12

UTF-8支持为您提供了一组新的选项。节省潜在空间(不进行行或页面压缩)是一个考虑因素,但是类型和编码的选择可能应该主要基于比较,排序,数据导入和导出的实际要求。

您可能需要做出比您想像的更多的更改,因为例如一种nchar(1)类型提供了两个字节的存储空间。这足以在BMP中存储任何字符(代码点000000到00FFFF)。该范围内的某些字符在UTF-8中将仅用1个字节编码,而其他字符将需要2个甚至3个字节(有关更多详细信息,请参见此比较表)。因此,要确保覆盖UTF-8中相同的字符集,将需要char(3)

例如:

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 char(1) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

给出熟悉的错误:

消息8152,级别16,状态30,行xxx
字符串或二进制数据将被截断。

或者,如果跟踪标志460处于活动状态:

消息2628,级别16,状态1,行xxx
字符串或二进制数据将在表'@T'的列'UTF8'中被截断。截断值:“”。

将UTF8列扩展为char(2)varchar(2)解决以下错误NCHAR(911)

DECLARE @T AS table 
(
    n integer PRIMARY KEY,
    UTF16 nchar(1) COLLATE Latin1_General_CI_AS,
    UTF8 varchar(2) COLLATE Latin1_General_100_CI_AS_SC_UTF8
);

INSERT @T (n, UTF16, UTF8)
SELECT 911, NCHAR(911), NCHAR(911);

但是,如果是例如NCHAR(8364),则需要将列进一步扩展到char(3)varchar(3)

还请注意,UTF-8归类均使用补充字符,因此不适用于复制。

除了其他功能外,UTF-8支持目前仅在预览中,因此无法用于生产环境。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.