使用 SAX 解析器解析 XML 数据并将其保存到 mysql 本地主机 (JAVA) 时速度很慢

SLOW SPEED in using SAX Parser to parse XML data and save it to mysql localhost (JAVA)

我正在 JAVA 为我当前的程序编程,但有问题。

我必须解析一个 1.60 GB 的大 .rdf 文件(XML 格式), 然后将解析后的数据插入到 mysql 本地主机服务器。

谷歌搜索后,我决定在我的代码中使用 SAX 解析器。 许多站点鼓励使用 SAX 解析器而不是 DOM 解析器, 说 SAX 解析器比 DOM 解析器快得多。

但是,当我执行使用 SAX 解析器的代码时,我发现 我的程序执行得太慢了。 我实验室的一位前辈告诉我,速度慢的问题可能已经发生 来自文件 I/O 进程。

在'javax.xml.parsers.SAXParser.class'的代码中, 'InputStream' 用于文件输入,相比之下,这可能会使作业变慢 使用 'Scanner' class 或 'BufferedReader' class.

我的问题是.. 1. SAX 解析器是否适合解析大型 xml 文档?

My program took 10 minutes to parse a 14MB sample file and insert data
to mysql localhost.
Actually, another senior in my lab who made a similar program 
as mine but using DOM parser parses the 1.60GB xml file and saves data
in an hour.
  1. 如何使用 'BufferedReader' 而不是 'InputStream', 在使用 SAX 解析器库时?

这是我向 Whosebug 提出的第一个问题,因此任何类型的建议都将不胜感激并有所帮助。感谢阅读。


收到初步反馈后添加的部分 我应该上传我的代码来澄清我的问题,我为此道歉..

package xml_parse;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Readxml extends DefaultHandler {

    Connection con = null;
    String[] chunk; // to check /A/, /B/, /C/ kind of stuff.

    public Readxml() throws SQLException {
        // connect to local mysql database
        con = DriverManager.getConnection("jdbc:mysql://localhost/lab_first",
                "root", "2030kimm!");
    }

    public void getXml() {

        try {
            // obtain and configure a SAX based parser
            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();

            // obtain object for SAX parser
            SAXParser saxParser = saxParserFactory.newSAXParser();

            // default handler for SAX handler class
            // all three methods are written in handler's body
            DefaultHandler default_handler = new DefaultHandler() {
                String topic_gate = "close", category_id_gate = "close",
                        new_topic_id, new_catid, link_url;
                java.sql.Statement st = con.createStatement();

                public void startElement(String uri, String localName,
                        String qName, Attributes attributes)
                        throws SAXException {
                    if (qName.equals("Topic")) {
                        topic_gate = "open";
                        new_topic_id = attributes.getValue(0);

                        // apostrophe escape in SQL query
                        new_topic_id = new_topic_id.replace("'", "''");

                        if (new_topic_id.contains("International"))
                            topic_gate = "close";

                        if (new_topic_id.equals("") == false) {
                            chunk = new_topic_id.split("/");
                            for (int i = 0; i < chunk.length - 1; i++)
                                if (chunk[i].length() == 1) {
                                    topic_gate = "close";
                                    break;
                                }
                        }

                        if (new_topic_id.startsWith("Top/"))
                            new_topic_id.replace("Top/", "");

                    }
                    if (topic_gate.equals("open") && qName.equals("catid"))
                        category_id_gate = "open";

                    // add each new link to table "links" (MySQL)
                    if (topic_gate.equals("open") && qName.contains("link")) {
                        link_url = attributes.getValue(0);
                        link_url = link_url.replace("'", "''"); // take care of
                                                                // apostrophe
                                                                // escape

                        String insert_links_command = "insert into links(link_url, catid) values('"
                                + link_url + "', " + new_catid + ");";

                        try {
                            st.executeUpdate(insert_links_command);
                        } catch (SQLException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }

                }

                public void characters(char ch[], int start, int length)
                        throws SAXException {
                    if (category_id_gate.equals("open")) {
                        new_catid = new String(ch, start, length);

                        // add new row to table "Topics" (MySQL)
                        String insert_topics_command = "insert into topics(topic_id, catid) values('"
                                + new_topic_id + "', " + new_catid + ");";
                        try {
                            st.executeUpdate(insert_topics_command);
                        } catch (SQLException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }

                public void endElement(String uri, String localName,
                        String qName) throws SAXException {
                    if (qName.equals("Topic"))
                        topic_gate = "close";
                    if (qName.equals("catid"))
                        category_id_gate = "close";
                }
            };
            // BufferedInputStream!!
            String filepath = null;
            BufferedInputStream buffered_input = null;

            /*
             * // Content filepath =
             * "C:/Users/Kim/Desktop/2016여름/content.rdf.u8/content.rdf.u8";
             * buffered_input = new BufferedInputStream(new FileInputStream(
             * filepath)); saxParser.parse(buffered_input, default_handler);
             * 
             * // Adult filepath =
             * "C:/Users/Kim/Desktop/2016여름/ad-content.rdf.u8"; buffered_input =
             * new BufferedInputStream(new FileInputStream( filepath));
             * saxParser.parse(buffered_input, default_handler);
             */
            // Kids-and-Teens
            filepath = "C:/Users/Kim/Desktop/2016여름/kt-content.rdf.u8";
            buffered_input = new BufferedInputStream(new FileInputStream(
                    filepath));
            saxParser.parse(buffered_input, default_handler);

            System.out.println("Finished.");
        } catch (SQLException sqex) {
            System.out.println("SQLException: " + sqex.getMessage());
            System.out.println("SQLState: " + sqex.getSQLState());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这是我程序的全部代码..

我昨天的原始代码尝试了文件 I/O,如下所示 (而不是使用 'BufferedInputStream')

saxParser.parse("file:///C:/Users/Kim/Desktop/2016여름/content.rdf.u8/content.rdf.u8",
             default_handler);

我希望我的程序在使用后能提高一些速度 'BufferedInputStream',但是速度一点都没有提高。 我无法找出导致速度问题的瓶颈。 非常感谢。

代码中读取的rdf文件大小约为14MB,需要大约 我的电脑执行这段代码需要 11 分钟。

Are SAX parsers good for parsing large-scale xml documents?

是的,很明显,SAX 和 StAX 解析器是解析大 XML 文档的最佳选择,因为它们内存不足,CPU 消费者,而 DOM 加载的解析器则不是这样将所有内容都存入内存,这在这种情况下显然不是正确的选择。

响应更新: 关于你的代码,你的缓慢问题更多地与你如何将数据存储在数据库中有关。您当前的代码以自动提交模式执行查询,而您应该使用事务模式以获得更好的性能,因为您有大量数据要插入,请阅读 this for a better understanding. To reduce the round trips between the database and your application you should also consider using batch update like in this good example.

使用 SAX 解析器,您应该能够毫不费力地达到 1Gb/分钟的解析速度。如果解析 14Mb 需要 10 分钟,那么要么你做错了什么,要么时间花在了 SAX 解析以外的事情上(例如数据库更新)。

您可以继续使用 SAX 解析器,并使用 BufferedInputStream 而不是 BufferedReader(因为您不需要猜测 XML 的字符集编码)。

通常 XML 可能会读取额外的文件:DTDs 等。例如,(X)HTML 有大量的命名实体。使用 XML catalog 在本地保存这些远程文件会大有帮助。

也许你可以关闭验证。

您还可以使用 gzip compression 比较网络流量与计算能力。通过设置 headers 并检查 headers,GZipInputStream-by-case 可能更有效(或无效)。