我使用.NET来解析大约2000万行(1.56 GB)的XML文件,从数据中构建LINQ对象,然后将其插入到SQL数据库中。这花了很长时间。
为了提高性能,我正在考虑要求管道分隔文件。我也想知道Perl是否会更快。有没有人建议加快这个过程?
答案 0 :(得分:4)
我认为您无法在该文件上提高LINQ性能,令您满意。我的建议是使用XmlTextReader并逐节点读取文件并自行处理数据。
答案 1 :(得分:4)
如果您可以以管道分隔格式获取数据,则可以使用SQL工具将其直接导入数据库。我想,你可以使用BCP来做到这一点。
为什么选择文件路径 - > LINQ对象 - > DB?
此外,尝试分割文件,而不是一个包含2000万条记录的大文件。
答案 2 :(得分:3)
在你修理之前,找出哪个部分很慢。这里有三个主要部分:
哪一个人占用了所有的时间?您可能怀疑它正在解析XML,但在浪费大量时间之前验证它。似乎每次我认为我知道答案,我错了:)我不能建议.NET分析器,但Stackoverflow似乎有答案。
我不是一个.NET家伙,但如果Perl能提供的任何东西都会明显加快,我真的会感到惊讶。这不是语言问题,因为即使是快速的Perl东西也是公共库的接口。
答案 3 :(得分:2)
管道分隔文件可能有助于您提高性能。如果您的任务受CPU限制,则可以将文件拆分为n
块(其中n
是您拥有的处理器数),并并行运行作业。
我怀疑Perl的运行速度会比.NET快,但您可能会让代码运行得更快。
答案 4 :(得分:2)
这是一个激进的想法,老实说我不知道它是否会改善你的表现,但它有很好的机会这样做。我敢打赌,你实例化你的上下文对象一次,然后使用它来插入所有记录,对吧?这样做意味着上下文将跟踪所有这些对象,直到它被处置,并且可能解释随着时间推移而降低的性能。
现在,您可以清除上下文缓存,但我有一个更加坚定的想法。这些上下文对象被设计为在实例化时具有最小的开销(无论如何在文档中都这样说,我没有测试过该断言),因此如果在每次迭代中实例化上下文,它可能会有所帮助。即在创建对象的同时重新创建上下文。或者,这是一个更好的主意,维护数据对象的内部列表,并在每n次迭代中将列表提交到单独的方法以提交到数据库。该方法应该实例化并处理上下文。有意义吗?
答案 5 :(得分:1)
首先,我很想听听你在处理XML时谈论行数。在大多数情况下,没有换行符的XML文档仍然是同一个文档。线条并不重要。
其次,你没有说你正在使用哪种RDBMS,但我会假设SQL Server 2005或2008.在这种情况下,如果这是一个你需要经常重复的过程,那么我建议你这样做这在SQL Server Integration Services(SSIS)中。 SSIS针对此类事情进行了优化。特别是,它实际上可以读取XML文件,执行每行修改,并将批量准备好的行同时写入数据库。
唯一可能的问题是文件的大小(顺便说一句,你说它是2000万行,但是这有多少MB?)。 SSIS尝试立即将整个文档加载到内存中。这可能是个问题。为了解决这个问题,我发现创建自己的自定义Source组件相对容易。它查看了文档的根元素,并一次返回一个子元素(及其后代)。使用XmlReader.ReadSubTree方法很容易做到这一点,该方法返回一个新的XmlReader,它只处理当前元素及其所有子元素。
答案 6 :(得分:1)
Brian有正确的想法 - 在确定破坏之前不需要开始破解。
另一个疯狂的伎俩 - 看看你是否可以在数据库服务器上运行它,从而避免任何网络延迟。我们有一些相当庞大的批处理过程,仅从位置变化中就有了很大的性能提升。
答案 7 :(得分:1)
importing the Stack Overflow data dump上的这个问题可能会对您有所帮助。您可以将数据写入CSV文件或使用XMLTextReader的文件,然后使用数据库的批量重要机制非常快速地导入数据。
答案 8 :(得分:1)
首先,确定完成导入过程的可接受时间。
然后,正如Brian和Wyatt建议的那样,做一些分析大部分时间花在哪里。我猜测LINQ to SQL的东西(尽管猜测性能问题在哪里本身就是一个非常冒险的事情)。
您可以遵循两条路线,即使用属于MS SQL服务器的ETL工具进行导入,或者继续使用某些自定义.NET代码进行导入。
如果您选择后者,我建议:
Perl将比.NET慢,因为.NET是编译的,而Perl则不是。
答案 9 :(得分:0)
如果您发现数据访问是瓶颈,您可能希望尝试使用SqlBulkCopy插入批量的xml对象。我有xml对象,每个对象有18个节点,这对我有用。
// read xml file into datatable
DataSet ds = new DataSet();
DataTable callList = new DataTable();
string AppDataPath = ConfigurationManager.AppSettings["AppDataPath"];
string dbSchema = AppDataPath + "/" + "CLBulkInsertSchema.xml";
//Create a FileStream to the XML Schema file in Read mode
FileStream finschema = new FileStream(dbSchema, FileMode.Open,
FileAccess.Read, FileShare.Read);
//Read the Schema into the DataSet
ds.ReadXml(finschema);
//Close the FileStream
finschema.Close();
//Create a FileStream to the Xml Database file in Read mode
FileStream findata = new FileStream(tempFilePathName, FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);
//Read the DataBase into the DataSet
ds.ReadXml(findata);
//Close the FileStream
findata.Close();
DataTable callList = ds.Tables["PhoneBook"];
string conn = ConfigurationManager.ConnectionStrings["dbConnectionString"].ToString();
using (SqlConnection connection =
new SqlConnection(conn))
{
connection.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName =
"dbo.CallBatchItems";
// mappings required because we're skipping the BatchItemId column
// and letting SQL Server handle auto incrementing of primary key.
// mappings not required if order of columns is exactly the same
// as destination table definition.
bulkCopy.ColumnMappings.Add("BatchId", "BatchId");
bulkCopy.ColumnMappings.Add("FullName", "FullName");
...
// Write from the source to the destination.
bulkCopy.WriteToServer(callList);
}
}