C# Linq To SQL 插入单个大行性能问题

Posted

技术标签:

【中文标题】C# Linq To SQL 插入单个大行性能问题【英文标题】:C# Linq To SQL insert a single big row performance issue 【发布时间】:2022-01-08 23:49:30 【问题描述】:

我有一个程序使用旧的 Linq To SQL 将 ASP.NET 应用程序连接到 SQL Server DB。 ASP.NET 应用程序和 SQL Server 实例位于同一台计算机上,并且两个“环境”都已更新(IIS 10、NET Framework 4.8 和 SQL Server 2019)。

在软件中,我必须处理带有客户订单的虚拟购物车。 Cart 有很多字段,其中一个是 nvarchar 并包含“购物车文档”,通常只有几 KB,但有时可能达到几 MB(永远不会超过 10MB)

当我更新 2-3MB 范围内的文档字符串,然后更新包含它的单行时,更新操作非常非常慢(2-2,5 秒)。 这里更新代码:

    protected void Upsert(CartDto cart, bool isValidationUpsert = false )
    
        lock (_sync)
        
            if ((cart?.Id ?? 0) <= 0)
                throw new ExtendedArgumentException("cartId");

            using (var dbContext = ServiceLocator.ConnectionProvider.Instace<CartDataContext>())
            
                var repository = new CartRepository(dbContext);
                
                var existingCart = repository.Read(crt => crt.ID == cart.Id).FirstOrDefault();
                if (existingCart == null)
                
                    existingCart = new tbl_set_Cart();
                    existingCart.Feed(cart);

                    repository.Create(existingCart);
                
                else
                
                    existingCart.Feed(cart);
                    repository.Update(existingCart);
                

                dbContext.SubmitChanges(); //<<--- This speecifi operation will take 2-2,5s previous instructions take a neglectable time
            
        
    

我不知道为什么,也不知道如何在这种情况下提高性能

--编辑: 正如建议的那样,如果我将 SQL 代码直接运行到 SQL Server 上(使用 SSMS 连接和执行代码),我已经分析了数据库上的操作并经历了相同的延迟 (~2,5) 事件。

这里是 SQL 代码和性能统计:

DECLARE @p0 AS INT = [cart_id];
DECLARE @p1 AS INT = [entry_count];
DECLARE @p2 AS NVARCHAR(MAX) = '..document..';

UPDATE [dbo].[tbl_set_Cart]
SET [ITEMS_COUNT] = @p1, [ITEMS] = @p2
WHERE [ID] = @p0

这是我的表格架构,你什么都看不到,这很简单:

/****** Object:  Table [dbo].[tbl_set_Cart]    Script Date: 02/12/2021 15:44:07 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[tbl_set_Cart](
    [ID] [int] NOT NULL,
    [AS400_CUSTOMER_COD] [nvarchar](50) NOT NULL,
    [AS400_LISTIN] [int] NOT NULL,
    [VALUE] [nvarchar](max) NOT NULL,
    [DELIVERY_COSTS] [nvarchar](max) NOT NULL,
    [ITEMS_COUNT] [int] NOT NULL,
    [ITEMS] [nvarchar](max) NOT NULL,
    [KIND] [int] NOT NULL,
    [CHECKOUT_INFO] [nvarchar](max) NOT NULL,
    [ISSUES] [nvarchar](max) NOT NULL,
    [LAST_CHECK] [datetime] NOT NULL,
    [USER_ID] [int] NOT NULL,
    [IMPERSONATED_USER_ID] [int] NOT NULL,
    [OVERRIDE_PRICES] [bit] NOT NULL,
    [HAS_ISSUE] [bit] NOT NULL,
    [IS_CONFIRMED] [bit] NOT NULL,
    [IS_COLLECTED] [bit] NOT NULL,
    [_METADATA] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_tbl_set_Cart] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

【问题讨论】:

除了一些代码外,没有任何人可以帮助您。但是这个“购物车文件”到底是什么东西?一个购物车应该不过是两张桌子。 CartHeader 和 CartItems。如果您通过 LINQ 推送 10MB 字符串,我并不感到惊讶。对我来说听起来像是架构问题。 @SeanLange : 如何通过 LinqToSql 快速推送几 MB 的数据?因为让我感到惊讶的是这样做的速度很慢。我知道插入多条记录会很慢(一次执行一行),但是为什么在不涉及网络的带有 SSD 磁盘的体面服务器上传输几 MB 会很慢? 对它进行基准测试,例如在 SqlCommand 中运行直接 INSERT,这样你就知道 L2S 的错误有多大 这涉及到一个网络,除非您的应用程序与您的 sql 服务器在同一个物理机器上运行并且没有人远程连接到该应用程序。我仍然说 10mb 的购物车听起来像是一个设计问题,除非有成千上万的独特物品被购买。 @CaiusJard:好主意,刚刚在 SSMS 中测试了 linq to sql 生成的代码(对文本的简单更新)。正如我在应用程序中所经历的那样,所花费的时间为 2.5 秒。那么似乎这是 SQL Server 更新大字符串所需的时间?怎么可能? 【参考方案1】:

在 DBA Stack Overflow 用户(此处为讨论 https://dba.stackexchange.com/questions/303400/sql-server-how-to-upload-big-json-into-column-performance-issue/303409#303409)的帮助下更深入地调查了 DB 分析后,发现问题可能与磁盘有关。

由于某些生产系统遇到了与我的开发机器相同的问题,我询问如何提高性能并收到了存储我的数据压缩版本的美丽提示。 数据不会太大(在我的扫描仪中),对于运行时压缩/解压缩时的内存来说太慢了,这大大减少了时间(使用了 LZMA)。 从 2,5s 到 ~0,3 是一个非常好的改进。

感谢大家的宝贵帮助和提示。

【讨论】:

以上是关于C# Linq To SQL 插入单个大行性能问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 LINQ to SQL 在计算列上获得不同的值?

C# 中的 LINQ to SQL 查询

LINQ to SQL / LINQ to Collections 性能

使用 linq-to-sql 进行批量插入

从 C# 保存到 SQL 中的 DATE 类型列 - Linq to SQL

C# LINQ to SQL 对象名称无效