如何在java或php中解析一个非常大的xml文件并插入到mysql DB中

时间:2011-02-14 18:52:31

标签: java php xml sax

我正在尝试将一个庞大的xml文件解析到我的MySQL数据库中。该文件是4.7克。 我知道,它疯了。

数据来自:http://www.discogs.com/data/(最新专辑xml是700mb压缩,4.7gb解压缩)

我可以使用java或php来解析和更新数据库。我认为java是更明智的想法。

我需要找到一种方法来解析xml而不填充我的4gb ram,并将其加载到db中。

这样做最聪明的方法是什么?我听说过SAX解析器,我在想正确的方向吗?

目前,我不关心从这些网址下载图片,我只想要数据库中的数据。我还没有设计这些表,但我现在对xml方面更感兴趣。

我使用php的fread()来打开文件的前1000个叮咬,所以至少我可以看到它的样子,这里是文件中第一张专辑结构的样本:

<releases>
<release id="1" status="Accepted">
    <images>
        <image height="600" type="primary" uri="http://s.dsimg.com/image/R-1-1193812031.jpeg" uri150="http://s.dsimg.com/image/R-150-1-1193812031.jpeg" width="600" />
        <image height="600" type="secondary" uri="http://s.dsimg.com/image/R-1-1193812053.jpeg" uri150="http://s.dsimg.com/image/R-150-1-1193812053.jpeg" width="600" />
        <image height="600" type="secondary" uri="http://s.dsimg.com/image/R-1-1193812072.jpeg" uri150="http://s.dsimg.com/image/R-150-1-1193812072.jpeg" width="600" />
        <image height="600" type="secondary" uri="http://s.dsimg.com/image/R-1-1193812091.jpeg" uri150="http://s.dsimg.com/image/R-150-1-1193812091.jpeg" width="600" />
    </images>
    <artists>
        <artist>
            <name>Persuader, The</name>
        </artist>
    </artists>
    <title>Stockholm</title>
    <labels>
        <label catno="SK032" name="Svek" />
    </labels>
    <formats>
        <format name="Vinyl" qty="2">
            <descriptions>
                <description>12"</description>
            </descriptions>
        </format>
    </formats>
    <genres>
        <genre>Electronic</genre>
    </genres>
    <styles>
        <style>Deep House</style>
    </styles>
    <country>Sweden</country>
    <released>1999-03-00</released>
    <notes>Recorded at the Globe studio in Stockholm. The titles are the names of Stockholm's districts.</notes>
    <master_id>5427</master_id>
    <tracklist>
        <track>
            <position>A</position>
            <title>Östermalm</title>
            <duration>4:45</duration>
        </track>
        <track>
            <position>B1</position>
            <title>Vasastaden</title>
            <duration>6:11</duration>
        </track>
        <track>
            <position>B2</position>
            <title>Kungsholmen</title>
            <duration>2:49</duration>
        </track>
        <track>
            <position>C1</position>
            <title>Södermalm</title>
            <duration>5:38</duration>
        </track>
        <track>
            <position>C2</position>
            <title>Norrmalm</title>
            <duration>4:52</duration>
        </track>
        <track>
            <position>D</position>
            <title>Gamla Stan</title>
            <duration>5:16</duration>
        </track>
    </tracklist>
</release>

感谢。

7 个答案:

答案 0 :(得分:2)

你显然需要一个流式API而不是一个DOM,它需要将整个文档保存在内存中。 Java支持SAX和Stax。我自己从未使用过Stax,但听说它比SAX更容易使用,但仍然有效。

确保将工作拆分为多个事务:数据库将无法在单个事务中支持尽可能多的插入语句。

答案 1 :(得分:1)

如果我在哪里使用PHP解析它,我会分两步完成:

  1. 每隔几个<release>点击文件并使该minifile成为有效的XML。
  2. 分别解析每个生成的文件
  3. 如果速度不重要,那么PHP实际上会更好,因为在PHP中解析文本/ XML很容易。

答案 2 :(得分:1)

我前段时间遇到类似的问题。这里是导入大约28MB文件的脚本巫婆的一部分,而不是将整个数据读入内存。它可能应该工作:)。它由XML节点读取,在内存中只保留XML的一小部分。脚本将需要很少的修改来满足您的需求。

$reader = new XMLReader();
$reader->open(<path_to_large_xml_file>);    
while ($reader->read()) {
    switch ($reader->nodeType) {
        case (XMLREADER::ELEMENT):
        if ($reader->localName == "Table") {

            $node = $reader->expand();
            $dom = new DomDocument();
            $n = $dom->importNode($node,true);
            $dom->appendChild($n);
            $sxe = simplexml_import_dom($n);

            $Data = array();
            $DataColumns = array();

            foreach ($columns as $key => $column)
            {

                if (in_array($key,$DateColumns))
                {
                    $DateArray = explode('/',substr(trim($sxe->$column),0,10));   
                    $ValueColumn = date('Y-m-d H:i:s',mktime(0,0,0,$DateArray[1],$DateArray[0],$DateArray[2]));
                    $Data[] = '\''.$ValueColumn.'\'';
                    $DataColumns[] = $key;

                    if ($SplitDateInsert == 'enabled')
                    {
                        $Data[] = '\''.$DateArray[2].'\'';
                        $Data[] = '\''.$DateArray[1].'\'';
                        $Data[] = '\''.$DateArray[0].'\'';

                        $DataColumns[] = $key.'_year';
                        $DataColumns[] = $key.'_month';
                        $DataColumns[] = $key.'_day';                            
                    }

                } else {
                    $ValueColumn = addslashes(trim($sxe->$column));
                    $Data[] = '\''.$ValueColumn.'\'';
                    $DataColumns[] = $key;
                }                   

            }               
                $SQL = "INSERT INTO {$tableName} (".implode(',',$DataColumns).") VALUES (".implode(',',$Data).")";                  
                $db->query($SQL);                       

        } // END IF table
    }
}

答案 3 :(得分:0)

假设MySQL在这方面具有类似Oracle的功能,为什么不让DB处理解析呢?在oracle中,你可以只注册XMLSchema,创建一个结构化的XMLType表(可能比clob更有用),然后插入文件。

从来没有将它用于任何那么大的东西,但是我不明白为什么它不应该工作,而且只需几行代码即可完成。您只需要具有MySQL经验的人来讲述详细信息的工作原理。

答案 4 :(得分:0)

我建议在Java上使用Stax。或者,更简单的是,StaxMate为访问增加了更多便利。

但是你究竟需要用XML做什么?要在数据库中流式传输它,有一些方法可以将BLOB作为流处理(虽然mySQL JDBC驱动程序因怪癖而臭名昭着)所以这应该是可行的。

答案 5 :(得分:0)

4.7 GB不是疯了,只是轻微的。如果您使用64位Java,则扩展VTD-XML应该是最有效且易于使用的选项。

答案 6 :(得分:0)

您还没有说过要对XML进行哪些处理。您可以考虑使用Saxon的流模式XSLT(它需要Saxon-EE产品,这需要花钱) - 如果处理本质上是一个“突发模式流”练习依次处理每个“释放”元素,那么它应该'太难了。当然,您也可以使用低级SAX或StaX解析,但这几乎肯定需要开发和调试更多的代码。