如何快速将大量XML数据存入我的数据库?

时间:2009-06-16 21:25:57

标签: .net xml

我使用.NET来解析大约2000万行(1.56 GB)的XML文件,从数据中构建LINQ对象,然后将其插入到SQL数据库中。这花了很长时间。

为了提高性能,我正在考虑要求管道分隔文件。我也想知道Perl是否会更快。有没有人建议加快这个过程?

10 个答案:

答案 0 :(得分:4)

我认为您无法在该文件上提高LINQ性能,令您满意。我的建议是使用XmlTextReader并逐节点读取文件并自行处理数据。

答案 1 :(得分:4)

如果您可以以管道分隔格式获取数据,则可以使用SQL工具将其直接导入数据库。我想,你可以使用BCP来做到这一点。

为什么选择文件路径 - > LINQ对象 - > DB?
此外,尝试分割文件,而不是一个包含2000万条记录的大文件。

答案 2 :(得分:3)

在你修理之前,找出哪个部分很慢。这里有三个主要部分:

  • 解析XML
  • 制作LINQ对象
  • 将其插入数据库

哪一个人占用了所有的时间?您可能怀疑它正在解析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代码进行导入。

如果您选择后者,我建议:

  1. 仅使用ADO.NET并删除LINQ to SQL内容;
  2. 在数据库服务器上运行导入程序 - 消除任何网络流量问题;
  3. XML解析器不太可能是任何瓶颈,但如果是,请考虑使用简单的模式检查直接解析XML而不是完全兼容的解析器 - 当然,这需要XML格式简单,并以逐行方式布局。
  4. 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);
            }
        }