如果在单个请求中与 Oracle 和 PostgreSql 交互以处理超过 20 万条记录,如何提高 EF Core 性能
Posted
技术标签:
【中文标题】如果在单个请求中与 Oracle 和 PostgreSql 交互以处理超过 20 万条记录,如何提高 EF Core 性能【英文标题】:How to improve EF Core Performance if interacting with Oracle and PostgreSql in a single request to handle more than 200k records 【发布时间】:2021-05-11 05:18:18 【问题描述】:我有一个 .Net Core Web API 端点,它从 OracleDB 中提取数据并将其保存到 PostgreSQL DB 中。我正在使用最新的实体框架核心和 Oracle.ManagedDataAccess.Core 和 Npgsql.EntityFrameworkCore.PostgreSQL 来连接到各自的数据库。
在 API 用来连接到我的基础设施层的服务层内部,我有单独的方法可以调用:
调用 OracleRepository - 为了从 5 个表中提取记录(每个表在存储库内的 diff 方法中):
-
获取表 A 数据
获取表 B 数据.. 等等直到表 E
调用 PostgreSqlRepository - 为了使用 Code First 方法将每个表的数据(从 OracleDB 获取)存储到 PostgreDB 中(再次在存储库内的 diff 方法中)。
每张表的记录数:
A - 6.7k B - 113k C - 56k D - 5.8k E - 5.3k现在完成上述所有步骤大约需要 45 秒。任何提高性能的建议。 有没有办法从数据库异步获取数据并存储它?我在我的 .net Core 应用程序中为 Oracle 和 Postgre 存储库以及所有其他服务使用了瞬态生命周期。
注意:每次我在插入数据之前截断我的 PostgreSQL 表(使用 EfCore 的 RemoveRange 方法)。
【问题讨论】:
如果你想更快,避免那些额外的层并编写原始 SQL。对 PostgreSQL 使用COPY
,否则使用准备好的语句。
@LaurenzAlbe 我正在使用原始 SQL 从 Oracle 中提取数据,但在 PostgreSQL 的情况下我首先使用代码。
@LaurenzAlbe 你也能解释一下PostgreSQL中的COPY和prepared statements吗?任何参考都会有所帮助,因为我是 PostgreSQL 新手。
这里是指向COPY
和prepared statements 的文档链接。请注意,每个 API 都有自己的方法来使用准备好的语句,您不必使用原始 SQL 来完成。
EF Core 不是为了性能,而是为了最简单的 CRUD。我建议使用第三方库github.com/linq2db/linq2db.EntityFrameworkCore,它会在几秒钟内插入记录(它也支持PostgreSQL COPY)。
【参考方案1】:
在关系数据库中批量加载表时,需要注意一些事项。
默认情况下,每个INSERT
查询都会生成一个database transaction。数据库的ACID 规则要求事情正常工作,即使许多并发数据库会话正在查询您正在加载的表。所以,自动提交是很耗时的。
您可以在批量加载时解决此问题。最好的方法是使用COPY 命令。要使用它,您将从源数据库 (Oracle) 中提取数据并将其写入文件系统上的临时 CSV 平面文件中。这已经足够快了。然后,您将使用这样的查询将文件复制到表中。这旨在围绕交易内容进行优化。
COPY A FROM `/the/path/to/your/file.csv` DELIMITER ',' CSV HEADER;
如果您不能使用 COPY,请了解这一点。如果您在显式事务中批量处理 INSERT,则效果会更好。
START TRANSACTION;
INSERT INTO A (col, col) VALUES (val, val);
INSERT INTO A (col, col) VALUES (val, val);
INSERT INTO A (col, col) VALUES (val, val);
INSERT INTO A (col, col) VALUES (val, val);
INSERT INTO A (col, col) VALUES (val, val);
COMMIT;
事情比你一个一个地插入要快得多。大约 100 行的批处理通常会让您的性能提高十倍。
在 EF Core 中,您可以使用类似这样的代码执行 transactions... 这是伪代码。它缺少异常处理和其他你需要的东西。
using var context = new WhateverContext();
const transactionLength = 100;
var rows = transactionLength;
var transaction = context.Database.BeginTransaction();
for each row in your input
context.Table.Add(new Row whatever );
if ( --rows <= 0 )
/* finish current transaction, start new one. */
context.SaveChanges();
transaction.Commit();
transaction.Dispose();
transaction = context.Database.BeginTransaction();
rows = transactionLength;
/* finish current transaction */
context.SaveChanges();
transaction.Commit();
transaction.Dispose();
【讨论】:
【参考方案2】:这里有偏见的答案。我发现使用PostgreSQLCopyHelper library 对我的一个非常相似的项目有效。当时我可以在不到 2 秒的时间内复制 600 万行。后来我成为了该项目的维护者。
【讨论】:
以上是关于如果在单个请求中与 Oracle 和 PostgreSql 交互以处理超过 20 万条记录,如何提高 EF Core 性能的主要内容,如果未能解决你的问题,请参考以下文章
Windows Server x64 中与 NHibernate 的 Oracle 驱动程序连接
如何在 Django API GET 请求中与 websocket 通信?
Postgresql 表连接方法介绍(和Oracle对比测试)