如何提高XML读写性能

5
我有两个独立运行的.NET应用程序(可以以任何顺序启动,或者可能只有一个正在运行),它们使用XML作为数据存储。因此,这两个应用程序都可以读写XML文件。
为了使数据保持更新,我在读写操作之前每次从磁盘加载XML文件。我使用XPath查询来查询特定节点。
现在这种方法存在性能问题,因为其中一个应用程序每秒钟会有读写XML的请求(使用轮询方式,无法更改)。我不确定到底是什么导致了性能问题,但我认为是连续的读写操作。
我尝试使用.NET 4.0中的内存映射文件,但我被限制使用.NET 3.5而不是更高版本。
有人能帮我解决这个问题吗?
注意:XML节点具有一些共同的属性,不同数量的属性和一个ID,我将其用于XPath查询。

3
您确定无法实现其他数据源,比如SQL Server Compact吗?为什么不能更改它? - Ricardo Souza
你考虑过在此用途中使用除了 XML 以外的其他东西吗?也许是 memcached(http://memcached.org/)?SQL? 如果你必须使用 XML,请尝试使用 XmlReader 和 XmlWriter。 - eyossi
4
如果您无法更改第一份申请,那么您的选择是非常有限的。糟糕的设计会导致糟糕的性能。 - Filip
没有接受重大设计更改,因此必须至少在一段时间内坚持使用 XML。我无法更改第一个应用程序,因为它是遗留的,并且预计要按照那样工作。 - user1447725
另外需要补充的是,这个 XML 文件并不是很大,只有大约 60 个节点,每个节点大约有 10 个属性。 - user1447725
5个回答

3

如果你确定性能下降是由于I/O操作,而且你无法改变应用程序,那么你所能做的真的很少。

第一个解决方案是在不更改现有应用程序代码的情况下使用RAM磁盘。如果他们将该文件用作共享内存,则可以在不进行任何其他更改的情况下完成。如果数据是持久的,则可能需要在每次写入后执行后台复制到另一个介质。性能不会像真正的共享内存那样好,但至少你不必等待缓慢的I/O操作。

第二个解决方案仅涉及必须读取数据的应用程序中的更改:通常,解析XML文件非常慢(特别是如果你正在使用XmlDocument而文件不是很小)。在这种情况下,使用XmlReader,你必须使你的读取代码更加复杂,并忘记XPath查询,但它的性能将比XmlDocument好几倍,并且不会因文件大小而减慢速度。

小的(或者不那么小的)更新:如果第二个应用程序的代码(我猜测是读取文件的那个应用程序)可以更改,那么您可以做一些小修改来提高其性能。首先,不要每次都读取文件。检查其时间戳,为该文件注册FileSystemWatcher或任何其他内容,但不要每次读取/解析文件。当您完成此操作后,可以向前迈进一步:仅在更改时读取/解析文件,在后台(另一个线程)准备好您的XmlDocument,并将其提供给轮询请求。如果请求被间隔,它们甚至可能看到非常快的响应时间(但需要针对典型文件对XmlDocument XPath查询进行性能分析)。 编辑here 您可以找到由Microsoft提供的RAM磁盘。它非常简单和朴素,但通常您/我们不需要更多。而且,这是DDK的一个示例,所以您也会得到源代码(在这种情况下......只是为了好玩)。

+1 - RAM磁盘是使用这种设计不良的应用程序可以做的少数事情之一,无论它有多么糟糕的设计。 - Jirka Hanika
你所说的 RAM 磁盘是指共享文件/内存文件吗? - user1447725
不,RAM磁盘是一种位于内存中的虚拟磁盘。应用程序将视其为普通驱动器(添加链接)。 - Adriano Repetti
有没有使用C#在.NET中实现RAM磁盘的链接? - user1447725
如果您不需要什么“特别”的东西,可以使用Microsoft在DDK中提供的RAM磁盘:http://support.microsoft.com/kb/257405(这是一个DDK示例,因此您也将获得源代码)。 - Adriano Repetti

2

XML并不适用于大量查询。如果需要进行此操作,请考虑使用数据库。 SQL Server Compact 可能是一个不错的选择。但如果您必须使用XML且需要处理大文件并提高性能,请考虑使用XmlReader/XmlWriter,它们不会将整个文件加载到内存中且速度较快。


1
如果他们不能改变应用程序,数据库怎么能帮助呢? - Jirka Hanika
确实,我没有注意到这个限制。如我在答案中提到的那样,XmlReader/XmlWriter 是在这种情况下应该采用的方法。 - Darin Dimitrov

2

不要每次都读取XML文件,只在第一次读取时同时获取文件的最后修改时间。

每当需要知道数据是否是最新的时候,只需检查文件的修改时间,仅当它确实发生了变化时才重新读取文件。


如果该应用程序确实每秒钟写入文件一次,那么这并没有太大帮助。但是在编写此类代码之前可以轻松检查。 - Jirka Hanika

2
不要轮询文件。读取并将其保留在内存中,然后使用FileSystemWatcher仅在文件改变时重新加载它。
或者,读取修改时间戳,并仅在时间戳更改时重新加载文件。
此外,在读取文件时,请确保以非排他方式锁定它,以便其他读取器不被阻塞。

1
尝试独占打开文件。其他应用程序可能会崩溃,但是如果它没有崩溃,你可以确定一件事情:在共享文件上,它不能在一个周期内调用太多的I/O负载,因为所有访问尝试都将立即失败。
希望它只需等待一秒钟并重试,这对您来说应该很有效。
using (Stream iStream = File.Open("myfile.xml",
            FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
    ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接