SQL 服务器请求在本地数据库中插入 200 行需要 20 秒并且还在增长

SQL Server request inserting 200 rows in a local database takes 20 seconds and growing

我正在开发连接到本地 SQL 服务器数据库的控制台应用程序(C#,asp-core 2.1,Entity Framework Core),默认 (localdb)\MSSQLLocalDB (SQL Server 2016 v13.0) 随 Visual Studio 一起提供。

我面临的问题是将数据插入 table 需要相当长的时间。 table 有 400.000 行,6 列,我一次插入 200 个。

现在,请求需要 20 秒才能执行。而且这个执行时间不断增加。考虑到我还有 20.000 x200 行要插入,有必要弄清楚这个问题是从哪里来的!

一些事实:

所以,我的问题是:

这是有问题的 table 的 SQL 代码:

CREATE TABLE [dbo].[KfStatDatas] 
(
    [Id]           INT IDENTITY (1, 1) NOT NULL,
    [DistrictId]   INT           NOT NULL,
    [StatId]       INT           NOT NULL,
    [DataSourceId] INT           NOT NULL,
    [Value]        NVARCHAR(300) NULL,
    [SnapshotDate] DATETIME2(7)  NOT NULL
);

编辑 我 运行 SQL Server Management Studio,我发现请求正在减慢整个过程。这是插入请求。

但是,通过查看 Entity Framework 创建的 SQL 请求,它看起来像是在进行内部连接并遍历整个 table,这可以解释为什么处理时间随着table的增加而增加。

我可能漏掉了一点,但为什么您需要枚举整个 table 来添加行?

正在执行原始请求:

SELECT [t].[Id] 
FROM [KfStatDatas] t
INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])
ORDER BY [i].[_Position]

编辑和解决方案

我最终找到了问题,这是一个愚蠢的错误:我的 Id 字段没有声明为主键!因此系统必须为每个插入的行遍历整个数据库。我添加了 PK,现在 200 行需要 100 毫秒,这个持续时间是 stable。

感谢您的宝贵时间!

没有 400K 行并不大。

从 .NET 插入大量行的最有效方法是使用 SqlBulkCopy。对于 400K 行,这应该需要几秒钟而不是几分钟。

通过批处理单个插入,在单个事务中执行整个批处理以提高吞吐量。否则,每个插入都是单独提交的,需要为每个插入将日志缓冲区同步刷新到磁盘以强化事务。

编辑:

我从您的评论中看出您正在使用 Entity Framework。 This answer 可以帮助您将 SqlBulkCopy 与 EF 结合使用。

我认为您可能只是缺少主键。您已向 EF 声明 Id 是实体键,但您在 table 上没有唯一索引来强制执行此操作。

当 EF 想要获取插入的 ID 时,如果没有索引,它的开销很大。所以这个查询

SELECT t.id from KfStatDatas t
inner join @inserted0 i 
  on t.id = i.id
order by i._Position

执行 38K 次逻辑读取,平均耗时 16 秒。

所以尝试:

ALTER TABLE [dbo].[KfStatDatas]
ADD CONSTRAINT PK_KfStatDatas
PRIMARY KEY (id)

顺便说一句,你确定这是 EF6 吗?这看起来更像是 EF Core 批量插入。