我正在尝试加载来自不同rdbms的2个巨大结果集(源和目标(,但是我挣扎的问题是使这2个巨大的结果设置在内存中。
考虑以下是从源和目标中提取数据的查询:
SQL Server- select Id as LinkedColumn,CompareColumn from Source order by LinkedColumn
oracle- select Id as LinkedColumn,CompareColumn from Target order by LinkedColumn
来源中的记录: 12377200
目标中的记录: 12266800
以下是我尝试使用一些统计数据的方法:
1(打开数据读取器的方法,用于读取源和目标数据:
Total jobs running in parallel = 3
Time taken by Job1 = 01:47:25
Time taken by Job1 = 01:47:25
Time taken by Job1 = 01:48:32
There is no index on Id Column.
在这里度过了主要时间: var dr = command.ExecuteReader();
问题:我还必须将commandtimeout
保留到0(infinity)
的超时问题,这是不好的。
2(块通过块阅读方法的读数源和目标数据:
Total jobs = 1
Chunk size : 100000
Time Taken : 02:02:48
There is no index on Id Column.
3(块通过块阅读方法进行阅读源和目标数据:
Total jobs = 1
Chunk size : 100000
Time Taken : 00:39:40
Index is present on Id column.
4(打开数据读取器读取源和目标数据的方法:
Total jobs = 1
Index : Yes
Time: 00:01:43
5(打开数据读取器读取源和目标数据的方法:
Total jobs running in parallel = 3
Index : Yes
Time: 00:25:12
我观察到,在LinkedColumn上有索引确实提高了性能,问题是我们正在处理可能没有索引的第三方RDBMS表。
我们希望使数据库服务器尽可能免费,因此数据读取器方法似乎不是一个好主意,因为会并行运行很多作业,这会在数据库服务器上造成太大的压力想要。
因此,我们想从源到目标的资源内存中获取记录,并进行1-1个记录比较以保持数据库服务器免费。
注意:我想在我的C#应用程序中执行此操作,并且不想使用SSIS或链接服务器。
更新:
Source Sql Query Execution time in sql server management studio: 00:01:41
Target Sql Query Execution time in sql server management studio:00:01:40
在内存中读取巨大结果的最佳方法是什么?
代码:
static void Main(string[] args)
{
// Running 3 jobs in parallel
//Task<string>[] taskArray = { Task<string>.Factory.StartNew(() => Compare()),
//Task<string>.Factory.StartNew(() => Compare()),
//Task<string>.Factory.StartNew(() => Compare())
//};
Compare();//Run single job
Console.ReadKey();
}
public static string Compare()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
var srcConnection = new SqlConnection("Source Connection String");
srcConnection.Open();
var command1 = new SqlCommand("select Id as LinkedColumn,CompareColumn from Source order by LinkedColumn", srcConnection);
var tgtConnection = new SqlConnection("Target Connection String");
tgtConnection.Open();
var command2 = new SqlCommand("select Id as LinkedColumn,CompareColumn from Target order by LinkedColumn", tgtConnection);
var drA = GetReader(command1);
var drB = GetReader(command2);
stopwatch.Stop();
string a = stopwatch.Elapsed.ToString(@"d.hh:mm:ss");
Console.WriteLine(a);
return a;
}
private static IDataReader GetReader(SqlCommand command)
{
command.CommandTimeout = 0;
return command.ExecuteReader();//Culprit
}
没有比数据标准更快的(我知道( 用于获取数据库记录。
使用大型数据库的挑战,在2秒内阅读1000万张记录非常好。
如果您想要更快的速度:
- jdwend的建议:
使用sqlcmd.exe和过程类以运行查询并将结果放入CSV文件中,然后将CSV读取到C#中。SQLCMD.EXE旨在比C#接口快速归档大型数据库并运行100倍。使用LINQ方法也比SQL客户端类
快得多
-
对您的疑问进行分析并同时合并结果:https://shahanayyub.wordpress.com/2014/03/30/how-to-to-to-to-load-lolge-large-dataset-indaset-in-datagridview/
-
最简单的(而IMO最适合选择 * ALL(就是将硬件扔给它:https://blog.codinghorror.com/hardware-is-cheap-programers-are-expention/
还要确保您在发布模式下测试您的基准。
这是我的模式使用。它将特定记录的数据设置为System.Data.DataTable
实例,然后关闭并尽快处理所有未管理的资源。模式还适用于System.Data
下的其他提供商,包括System.Data.OleDb
,System.Data.SqlClient
等。我相信Oracle Client SDK实现了相同的模式。
// don't forget this using statements
using System.Data;
using System.Data.SqlClient;
// here's the code.
var connectionstring = "YOUR_CONN_STRING";
var table = new DataTable("MyData");
using (var cn = new SqlConnection(connectionstring))
{
cn.Open();
using (var cmd = cn.CreateCommand())
{
cmd.CommandText = "Select [Fields] From [Table] etc etc";
// your SQL statement here.
using (var adapter = new SqlDataAdapter(cmd))
{
adapter.Fill(table);
} // dispose adapter
} // dispose cmd
cn.Close();
} // dispose cn
foreach(DataRow row in table.Rows)
{
// do something with the data set.
}
我想我会以不同的方式处理这个问题。
,但之前让我们做一些假设:
- 根据您的问题描述,您将从SQL Server和Oracle获取数据
- 每个查询将返回一堆数据
- 您没有指定在内存中获取所有数据的意义,
- 我假设您将处理的数据将被多次使用,并且您不会多次重复两个查询。
- 以及您将对数据进行的任何操作,可能不会同时向用户显示。
拥有这些基础要点,我将处理以下内容:
- 将这个问题视为数据处理
- 有第三个数据库或其他一些位置,其中包含辅助数据库表,您可以在其中存储2个查询的所有结果。
- 为了避免超时左右,请尝试使用以下方式获取数据(一次获取数千个(,然后保存在这些Aux DB表中,而不是在" RAM"内存中。
- 一旦您的逻辑完成所有数据加载(导入迁移(,您就可以开始处理。
- 数据处理是数据库引擎的关键点,它们是有效的,并且在很多年中的发展效率很大,因此不会花费时间来重新发明轮子。使用一些存储的过程将2个辅助表的"处理/过程/合并"仅为1.
- 现在您已经在第三个AUX表中拥有所有"合并"数据,现在您可以使用它来显示或需要使用其他数据。
如果要更快地阅读,则必须使用原始API来更快地获取数据。避免像Linq这样的框架,并依靠DataReader。尝试检查天气您需要肮脏的读取(sql Server中的(nolock((之类的东西。
如果您的数据非常大,请尝试实现部分读取。就像为数据索引一样。也许您可以将条件放在日期到 - 到所有选定的一切。
之后,您必须考虑使用系统中的螺纹并行化流程。实际上是从Job 1获得的1个线程,另一个线程从Job 2获得。
技术性,我认为那里这里是一个更根本的问题。
select [...] order by LinkedColumn
我确实观察到,在LinkedColumn上有索引确实可以提高性能,但问题是我们正在处理可能具有索引或可能没有索引的第三方RDBMS表。
我们想让数据库服务器尽可能免费
如果您无法确保DB在该列上具有基于树的索引,则意味着数据库将非常忙于对您的数百万个元素进行分类。它很慢,饿了。在SQL语句中摆脱order by
,并在应用程序端执行以获取结果更快并减少DB的负载...或确保DB具有这样的索引!!!
...取决于这种提取是常见的还是罕见的操作,您需要在DB中执行适当的索引,或者只是获取所有索引并将其全部删除并对客户端进行排序。
我多年前也有类似的情况。在我查看问题之前,使用SQL在2个系统之间移动数据花了5天。
。我采用了另一种方法。
我们将数据从源系统提取到了少数文件中,这些文件代表了扁平的数据模型并在每个文件中安排了数据,因此当我们从文件中读取时,它们都自然而然地以适当的顺序流动。然后,我编写了一个Java程序,该程序处理了这些扁平的数据文件,并为目标系统生成了单个表负载文件。因此,例如,源提取物的源系统少于十二个数据文件,该数据文件变成了目标数据库的30至40左右的加载文件。
该过程将在短短几分钟内运行,我整合了完整的审计和错误报告,我们可以快速在源数据中发现问题和差异,修复它们并再次运行处理器。
拼图的最后一部分是我写的一个多线程实用程序,该实用程序在目标甲骨文数据库中执行了每个负载文件的平行批量负载。该实用程序为每个表创建了一个Java进程,并使用了Oracle的散装表负载程序,以快速将数据推入Oracle DB。
所有人都说并完成了为期5天的SQL-SQL转移数百万张记录的转移,仅使用Java和Oracle的散装负载功能,将数百万张记录变成了30分钟。而且没有错误,我们说明了系统之间传输的每个帐户的每一分钱。
因此,也许在SQL框外思考并使用Java,文件系统和Oracle的散装装载机。并确保您在固态硬盘驱动器上进行文件IO。
如果您需要处理Java的大数据库结果集,则可以选择JDBC为您提供所需的低级控制。另一方面,如果您已经在应用程序中使用了ORM,那么回到JDBC可能会暗示一些额外的痛苦。您将失去诸如浏览域模型等时乐观锁定,缓存,自动获取之类的功能。幸运的是,大多数Orms(例如Hibernate(都有一些选择来帮助您。虽然这些技术并不新鲜,但有几种可能性可供选择。
一个简化的示例;假设我们有一个具有100.000个记录的表(映射到类" Demoentity"(。每个记录由一个列(映射到示例性的属性"属性"(组成,保留了一些随机字母数字的数据约为〜2KB。JVM使用-XMX250M运行。假设250MB是可以分配给系统上JVM的总体最大内存。您的工作是阅读当前在表中的所有记录,进行一些不进一步指定的处理,最后存储结果。我们假设由我们的批量操作产生的实体未经修改