UCS-2 和 SQL Server

Posted

技术标签:

【中文标题】UCS-2 和 SQL Server【英文标题】:UCS-2 and SQL Server 【发布时间】:2012-02-18 22:50:27 【问题描述】:

虽然 researching options 用于在可能非常大的 SQL Server 数据库中存储主要是英语但有时不是数据的数据,但我倾向于将大多数字符串数据存储为 UTF-8 编码。

但是,Microsoft 选择 UCS-2 的原因我并不完全理解,这导致我对这种倾向产生了怀疑。 SQL Server 2012 的文档确实显示了如何创建 UTF-8 UDT,但 UCS-2 的决定可能遍及 SQL Server。

Wikipedia(有趣的是,UCS-2 已经过时,取而代之的是 UTF-16)指出 UTF-8 是一个可变宽度字符集,能够对任何 Unicode 数据点进行编码,并且它provides the de facto standard encoding for interchange of Unicode text。所以,感觉任何 Unicode 字符都可以用 UTF-8 表示,而且由于大多数文本都是英文的,所以表示形式几乎是 UCS-2 的两倍(我知道磁盘“便宜”,但磁盘缓存不是't,并且内存与我正在处理的数据大小相比没有。当工作集大于可用 RAM 时,许多操作会呈指数级下降)。

顺着 UCS-2 流向上游泳可能会遇到什么问题?

【问题讨论】:

Windows 2012 上的 SQL Server 2012 将支持高达 4TB 的内存。一般来说,购买 RAM 比雇人改进代码更便宜。这只是一个建议。 我在决定新系统的架构时提出了这个问题。这是提出此类问题的理想时机。 RAM 或编码工作是否更便宜取决于问题的规模。如果 Google 可以雇佣一个程序员几周来将他们的磁盘和 RAM 需求减少一半,那将是一项明智的投资。 OK 好点 :-) 我没有在这么大的系统上工作。对我来说,这样的问题很容易。 :-) @Ben 处理问题时要小心,只需查看相对于开发人员成本的硬件成本。添加一定数量的 RAM 和/或磁盘往往只能修复系统的一部分,但其中一些决策的影响会触及人们通常不会考虑的许多其他部分(备份、SAN 存储成本、开发人员当“更多 RAM”修复程序结束时,调试和支持问题所花费的时间等)。请在此处查看我的答案:What are the main performance differences between varchar and nvarchar SQL Server data types?. @srutzky,当然,但速度和空间是问题,无论是使用空间还是节省空间都会以某种方式花钱。六个月的工资可以为您购买一台配备 192GB 内存、SQL 标准 4 核许可证(将使用 128GB 内存)和 2TB 固态硬盘的最大戴尔 700 系列服务器。然后你就可以继续你的其他工作,而不是一直处理性能问题。 :-) .... 无论如何/我不认为我们真的不同意,只是强调:-) 【参考方案1】:

在可能非常大的 SQL Server 数据库中存储主要是英语但有时不是数据,我倾向于将大多数字符串数据存储为 UTF-8 编码。

与其他一些允许选择编码的 RDBMS 不同,SQL Server以 UTF-16(Little Endian)存储 Unicode 数据,以 8 位编码(扩展 ASCII 、DBCS 或 EBCDIC) 用于该字段的排序规则所暗示的任何代码页。

Microsoft 选择 UCS-2 的原因我并不完全理解

考虑到 UTF-16 于 1996 年中期引入并在 2000 年完全指定,他们选择 UCS-2 的决定是有道理的。许多其他系统也使用(或使用)它(请参阅:https://en.wikipedia.org/wiki/UTF-16#Usage)。他们继续的决定可能更值得怀疑,尽管这可能是由于 Windows 和 .NET 是 UTF-16。 UCS-2 和 UTF-16 之间的字节物理布局是相同的,因此从 UCS-2 升级系统以支持 UTF-16 应该是纯功能性的,无需更改任何现有数据。

SQL Server 2012 的文档确实展示了如何创建 UTF-8 UDT,

嗯,不。通过 SQLCLR 创建自定义的用户定义类型不是,无论如何,它会让您替换任何本机类型。创建一些东西来处理专门的数据非常方便。但是字符串,即使是不同的编码,也远非专业化的。为您的字符串数据采用这条路线会破坏系统的任何可用性,更不用说性能,因为您将无法使用 any 内置字符串函数。如果您能够在磁盘空间上节省任何东西,那么这些收益将被您在整体性能上所损失的东西抹去。通过将 UDT 序列化为 VARBINARY 来存储 UDT。因此,为了进行 any 字符串比较或排序,在“二进制”/“序数”比较之外,您必须将所有其他值一一转换回 UTF-8 到 then进行可以解释语言差异的字符串比较。

此外,“文档”实际上只是示例代码/概念证明。代码是在 2003 年 (http://msftengprodsamples.codeplex.com/SourceControl/latest#Kilimanjaro_Trunk/Programmability/CLR/UTF8String/CS/UTF8String/Utf8String.cs) 为 SQL Server 2005 编写的。我看到了一个测试功能的脚本,但没有涉及性能。

但 UCS-2 的决定可能遍及 SQL Server。

是的,非常如此。默认情况下,内置函数的处理仅适用于 UCS-2。但从 SQL Server 2012 开始,您可以使用以下排序规则之一让它们处理完整的 UTF-16 字符集(以及 Unicode 版本 5 或 6,具体取决于您的操作系统和 .NET Framework 版本)名称以_SC 结尾(即补充字符)。

Wikipedia ... 指出 UCS-2 已过时,取而代之的是 UTF-16

正确。 UTF-16 和 UCS-2 都使用 2 字节代码点。但是 UTF-16 成对使用其中的一些(即代理对)来映射其他字符。用于这些对的代码点在 UCS-2 中为此目的而保留,因此不用于映射到任何可用符号。这就是为什么您可以在 SQL Server 中存储任何 Unicode 字符并且可以正确存储和检索它的原因。

Wikipedia ... 指出 UTF-8 是一种可变宽度字符集,能够对任何 Unicode 数据点进行编码

正确,但具有误导性。是的,UTF-8 是可变宽度的,但 UTF-16 的可变宽度也很小,因为所有补充字符都由两个双字节代码点组成。因此 UTF-16 每个符号使用 2 或 4 个字节,尽管 UCS-2 总是 2 个字节。但这不是误导的部分。具有误导性的是暗示任何其他 Unicode 编码都不能编码所有其他代码点。虽然 UCS-2 可以保存它们但不能解释它们,但 UTF-16 和 UTF-32 都可以映射所有 Unicode 代码点,就像 UTF-8 一样。

并且它 [ed: UTF-8] 为 Unicode 文本的交换提供了事实上的标准编码。

这可能是真的,但从操作的角度来看完全不相关。

感觉任何 Unicode 字符都可以用 UTF-8 表示

再说一遍,没错,但完全不相关,因为 UTF-16 和 UTF-32 也映射所有 Unicode 代码点。

由于大多数文本都是英文的,因此表示形式几乎是 UCS-2 的两倍

根据具体情况,这很可能是正确的,您担心这种浪费的使用是正确的。但是,正如我在导致此问题 (UTF-8 Support, SQL Server 2012 and the UTF8String UDT) 的问题中提到的,如果大多数行可以放入 VARCHAR,但有些行必须是 NVARCHAR,则您有一些选项可以减少浪费的空间量。最好的选择是启用 ROW COMPRESSION 或 PAGE COMPRESSION(仅限企业版!)。从 SQL Server 2008 R2 开始,它们允许非 MAX NVARCHAR 字段使用“Unicode 标准压缩方案”,它至少与 UTF-8 一样好,在某些情况下甚至比 UTF-8 更好。 NVARCHAR(MAX) 字段不能使用这种花哨的压缩,但它们的 IN ROW 数据可以从常规 ROW 和/或 PAGE 压缩中受益。有关此压缩的说明和比较数据大小的图表,请参阅以下内容:原始 UCS-2 / UTF-16、UTF-8 和启用数据压缩的 UCS-2 / UTF-16。

SQL Server 2008 R2 - UCS2 compression what is it - Impact on SAP systems

另请参阅 Data Compression 的 MSDN 页面了解更多详细信息,因为存在一些限制(除了它仅在企业版中可用 - 但从 SQL Server 2016 开始所有 版本都可用, SP1 !!) 以及在某些情况下压缩可能会使事情变得更糟。

我知道磁盘很“便宜”

该陈述的真实性取决于人们如何定义“磁盘”。如果您说的是可以在商店购买现成的用于台式机/笔记本电脑的商品零件,那么可以肯定。但是,如果谈到将用于您的生产系统的企业级存储,那么请向控制预算的人解释他们不应该拒绝您想要的价值数百万美元的 SAN,因为它“便宜”。 " ;-)。

顺着 UCS-2 流向上游泳可能会遇到什么问题?

我想不到。好吧,只要您不遵循任何可怕的建议来执行诸如实现该 UDT 或将所有字符串转换为 VARBINARY 或对所有字符串字段使用 NVARCHAR(MAX) 之类的事情 ;-)。但在您可能担心的所有事情中,使用 UCS-2 / UTF-16 的 SQL Server 不应该是其中之一。

但是,如果由于某种原因,不支持 UTF-8 这个问题非常重要,那么您可能需要找到另一个支持 UTF-8 的 RDBMS。


2018 年 10 月 2 日更新

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

【讨论】:

我刚刚对这里所涵盖的内容进行了一些测试。如果你能看一下会很高兴,因为它是关于UCS-2nvarchar 字段的限制:***.com/questions/63715760/…【参考方案2】:

你所说的“顺着 UCS-2 流”是什么意思?

以下是您的选择:

使用新的 2012 _SC 排序规则 (https://msdn.microsoft.com/en-us/library/ms143726.aspx)。这个想法来自 srutzky。你应该看看他的回答。这是迄今为止最好的解决方案。

不推荐,但可以:

实施 UDT。这将是大量工作,并且您将失去收费支持(OR 映射,当然还有一些适用于本机类型的 SQL Server 功能)。 使用 varbinary(max):需要您进行自定义转换代码。没有范围索引。 使用 nvarchar(N) 并打开行压缩。从 SQL Server 2008 R2 开始,这将使用与 UTF-8 一样紧凑的编码。但这需要企业版。

查看 cmets 了解这些方法的严重缺陷。

【讨论】:

通过“上游”,我的意思是与 MS 为在 UCS-2 中编码字符串所做的设计决策作斗争。为什么推荐(最大)版本与(n)版本的列定义? (最大)不是可能更慢吗? rusanu.com/2010/03/22/… 如果您在其中放入相同的字符串,不同版本的速度同样快。如果您将较长的字符串放在较长的列中,那么性能当然会更差。在内部,count 类型参数被实现为一个 int16,它为每个这样的列添加前缀。我不知道更改最大计数会改变性能的情况。 我的理解是(MAX)变体的数据存储在行数据之外,可能会导致另一个磁盘寻道。我认为这是我分享的链接中性能数字的根源。您是否有支持性能相同的参考? 我手头没有参考资料,但为什么要在行外存储 1 字节字符串?当然,当它不适合行内时,它将被存储在行外。 @srutzky 我刚刚看了你的回答。我不知道 _SC 排序规则。这个答案似乎在 2012 年发布之前。我已经从我的答案中指出了你的答案。希望接受标记也会得到调整。

以上是关于UCS-2 和 SQL Server的主要内容,如果未能解决你的问题,请参考以下文章

UCS-2/UCS-4/UTF

细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4

细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4

细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4

PHP UTF-8到UCS-2 SMS

什么是UTF8编码?