跳至页脚内容
Iron Academy Logo
C# 常见问题

Entity Framework开发人员在 SQL 中使用 nvarchar(max) 的危险性

Tim Corey
10m 27s

在处理 SQL 中的 nvarchar 时,开发人员经常会忽略这种数据类型对性能的影响--尤其是在 C# 中使用 Entity Framework 工作时。 在名为"Entity Framework开发人员在 SQL 中使用 nvarchar(max) 的危险"的 10 分钟重点视频中,Tim Corey 探讨了在 SQL Server 数据库中使用 nvarchar(max) 作为字符串字段默认值的影响。

本文是对 Tim 视频的详细解释,仅使用他的演示和推理,并附有示例和性能比较。 如果您依赖于 nvarchar(max) 而不了解它在引擎盖下的工作原理,这将会让您大开眼界。

了解问题:Entity Framework中的默认行为

Tim 首先描述了一个常见的 Entity Framework 场景:C# 开发人员定义了一个带有 FirstName 和 LastName 等字段的模型。 在 SQL Server 中使用迁移自动创建表格时,生成的模式会将这些字符串字段默认设置为 nvarchar(max)。

正如 Tim 所解释的那样,出现这种情况是因为 Entity Framework 不知道应该分配多长的字符串,所以选择了安全的方法--默认分配最大长度。 这意味着每个 nvarchar 列最多允许 2^31-1 个字符,最大存储容量为千兆字节。

这一决定看似方便,却隐藏着危险的性能代价。

两个表的示例设置:nvarchar(max) 与固定长度

为了突出重点,Tim 制作了两张相同的表格:

  • 用户:用 nvarchar(50) 表示姓和名。

  • UsersToTheMax:相同字段使用 nvarchar(max)。

在 2:39 处,Tim 解释了他如何使用 Dapper 在两个表中填充 100 万条相同的行,确保只有 nvarchar 数据类型不同。

这种设置使他能够在固定长度的 Unicode 列和可变长度的 max 列之间进行一致的比较。

比较查询和执行计划

Tim 在两个表上都使用了以下 SQL 查询:

SELECT * FROM dbo.Users ORDER BY LastName;
SELECT * FROM dbo.UsersToTheMax ORDER BY LastName;

在 3:34 处,他启用了实际执行计划,以分析 SQL Server 在执行这些查询时的内部操作。

注意:本测试并不涉及跨机器的总执行时间--Tim 强调对同一服务器上的相同数据查询进行比较,以区分 nvarchar(max) 对性能的影响。

令人震惊的结果

执行计划揭示了一个重大差异:

  • 对 nvarchar(50) 的查询仅使用了批量成本的 2%。

  • 对 nvarchar(max) 的查询使用了高达 98% 的成本。

正如 Tim 所说,这意味着最大查询的 SQL Server 处理成本要高出 50 倍--尽管列数据条目相同且相对较小。

在 CPU 时间方面:

  • 排序 nvarchar(50) 需要 107 毫秒。

  • 排序 nvarchar(max) 需要 339ms。

但最大的区别在于具体的并行操作:

  • 固定长度:0.43 秒

  • 最大长度:22.17 秒

即使数据相同,速度也要慢 50 倍以上。

内存消耗差异

Tim 深入探讨了内存授予--SQL Server 为每个查询分配了多少内存:

  • nvarchar(50) 查询:340MB

  • nvarchar(max) 查询:641MB

这本身就是一个警示,但在测试未缓存的列时,影响会更大:

  • 固定长度为 FirstName:357MB

  • FirstName 的最大长度: 8.5GB

由于 SQL Server 不知道 nvarchar 值在定义为 max 时可以有多大,因此会预留较大的内存块来容纳最大值,从而导致内存增加。

为什么 nvarchar(max) 如此昂贵?

9:15 时,Tim 解释了根本原因。 nvarchar(max) 数据类型:

  • 支持多达 2^31-1 个 Unicode 字符,占用多达 2GB 的存储空间。

  • 要求 SQL Server 在不合适的情况下将值存储在行外,使用指针而不是直接存储在行内。

  • 不能以与固定长度列相同的方式编制索引。

因此

  • 您不能为 nvarchar(max) 列建立索引,这意味着 SQL Server 必须对整个数据集进行排序或过滤,而不能进行优化。

  • 这将影响对 nvarchar(max) 字段进行 ORDER BY、WHERE 或 JOIN 等操作。

这种行为会导致大量内存使用、CPU 负载和速度减慢--仅仅是因为选择了错误的字符数据长度。

Tim 的最终建议

正如蒂姆在最后所说的那样

"在 Entity Framework 查询中,确保指定所有字符串的大小"。

在定义字符串属性时,应始终使用最大字符数,如 nvarchar(100) 或 nvarchar(255),具体取决于预期数据。 这一细微改动可确保

  • 优化存储空间

  • 支持索引

  • 降低查询成本

  • 更好的性能一致性

通过设置适当的长度,可以提高数据库模式的效率,避免懒惰的默认设置带来的隐患。

结论

Tim Corey 的视频提供了一个重要的教训:在 SQL 中使用 nvarchar(max) 作为字符串字段的默认长度可能会削弱性能,而您甚至没有意识到这一点。 SQL Server 会分配过多内存、跳过索引并增加 CPU 成本,即使是对于像名称或地址这样的普通 Unicode 文本条目也是如此。

收获? 了解 nvarchar 数据类型,避免使用 max 数据类型,除非您确实需要它来存储大型文档或长度可变的内容。

通过指定字符串大小,您不仅可以节省字节和内存,还可以使您的Entity Framework和 SQL 代码更加高效、可扩展和健壮。根据 Tim 的指导,您可以确保您的应用程序不会因设计而变得缓慢。

对于任何在 .NET Standard 中使用数据库的人来说,这是一个最佳实践,应该成为您标准工具包的一部分。 查看 Tim 的频道,了解更多 SQL 相关视频。

Hero Worlddot related to Entity Framework开发人员在 SQL 中使用 nvarchar(max) 的危险性
Hero Affiliate related to Entity Framework开发人员在 SQL 中使用 nvarchar(max) 的危险性

分享您的所爱,赚取更多收入

您为使用 .NET、C#、Java、Python 或 Node.js 的开发人员创建内容吗?将您的专业知识转化为额外收入!

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我