Comment analyser de gros fichiers XML (50 Go) en Java
Actuellement im essayant d'utiliser un Parser SAX, mais sur les 3/4 dans le fichier, il vient se bloque complètement, j'ai essayé d'allouer plus de mémoire, etc, mais ne pas obtenir des améliorations.
Est-il un moyen pour accélérer le processus? Une meilleure méthode?
Dépouillé à nu les os, donc j'ai maintenant le code suivant et lors de l'exécution en ligne de commande ça ne fonctionne toujours pas aller aussi vite que je le voudrais.
De l'exécution avec "java -Xms-4096m -Xmx8192m -jar reader.jar" je reçois un GC surcharge de dépassement des limites autour de l'article de 700000
Principal:
public class Read {
public static void main(String[] args) {
pages = XMLManager.getPages();
}
}
XMLManager
public class XMLManager {
public static ArrayList<Page> getPages() {
ArrayList<Page> pages = null;
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
File file = new File("..\\enwiki-20140811-pages-articles.xml");
PageHandler pageHandler = new PageHandler();
parser.parse(file, pageHandler);
pages = pageHandler.getPages();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return pages;
}
}
PageHandler
public class PageHandler extends DefaultHandler{
private ArrayList<Page> pages = new ArrayList<>();
private Page page;
private StringBuilder stringBuilder;
private boolean idSet = false;
public PageHandler(){
super();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
stringBuilder = new StringBuilder();
if (qName.equals("page")){
page = new Page();
idSet = false;
} else if (qName.equals("redirect")){
if (page != null){
page.setRedirecting(true);
}
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (page != null && !page.isRedirecting()){
if (qName.equals("title")){
page.setTitle(stringBuilder.toString());
} else if (qName.equals("id")){
if (!idSet){
page.setId(Integer.parseInt(stringBuilder.toString()));
idSet = true;
}
} else if (qName.equals("text")){
String articleText = stringBuilder.toString();
articleText = articleText.replaceAll("(?s)<ref(.+?)</ref>", " "); //remove references
articleText = articleText.replaceAll("(?s)\\{\\{(.+?)\\}\\}", " "); //remove links underneath headings
articleText = articleText.replaceAll("(?s)==See also==.+", " "); //remove everything after see also
articleText = articleText.replaceAll("\\|", " "); //Separate multiple links
articleText = articleText.replaceAll("\\n", " "); //remove new lines
articleText = articleText.replaceAll("[^a-zA-Z0-9- \\s]", " "); //remove all non alphanumeric except dashes and spaces
articleText = articleText.trim().replaceAll(" +", " "); //convert all multiple spaces to 1 space
Pattern pattern = Pattern.compile("([\\S]+\\s*){1,75}"); //get first 75 words of text
Matcher matcher = pattern.matcher(articleText);
matcher.find();
try {
page.setSummaryText(matcher.group());
} catch (IllegalStateException se){
page.setSummaryText("None");
}
page.setText(articleText);
} else if (qName.equals("page")){
pages.add(page);
page = null;
}
} else {
page = null;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
stringBuilder.append(ch,start, length);
}
public ArrayList<Page> getPages() {
return pages;
}
}
source d'informationauteur Joe Maher
Vous devez vous connecter pour publier un commentaire.
Votre code d'analyse est probablement très bien, mais le volume de données que vous êtes en train de charger est probablement trop grand pour tenir dans la mémoire que
ArrayList
.Vous besoin d'une sorte de pipeline pour transmettre les données à sa destination réelle, sans jamais
le stocker dans la mémoire à la fois.
Ce que j'ai parfois fait pour ce genre de situation est semblable à la suivante.
Créer une interface pour le traitement d'un seul élément:
Offre une mise en œuvre de cette à la
PageHandler
par le biais d'un constructeur:Envoyer des données à ce processeur au lieu de le mettre dans la liste:
Bien sûr, vous pouvez faire votre interface poignée de morceaux de plusieurs enregistrements, plutôt qu'une, et ont le
PageHandler
collecter localement les pages dans une petite liste et envoyer périodiquement la liste pour le traitement et l'effacer de la liste.Ou (peut-être mieux), vous pourriez mettre en œuvre les
PageProcessor
interface tel que défini ici, et de construire en logique qu'en mémoire tampon les données et les transmet pour traitement complémentaire dans les morceaux.Ne Roby a une approche un peu penser à la démarche que j'ai suivie de la création d'un générateur de code conçu pour résoudre ce problème particulier (une première version a été conçue en 2008). Fondamentalement, chaque
complexType
a sonJava POJO
équivalent et des gestionnaires pour le type particulier sont activés lorsque le contexte des changements à cet élément. J'ai utilisé cette approche pour le SEPA, transaction bancaire et par exemple, discogs (30 GO). Vous pouvez spécifier quels sont les éléments que vous souhaitez traiter au moment de l'exécution, de manière déclarative à l'aide de propriétés d'un fichier.XML2J utilise la cartographie de
complexTypes
de Pojo Java, d'une part, mais vous permet de spécifier les événements que vous souhaitez écouter.E. g.
L'essentiel est dans la troisième ligne. Le détachement permet de s'assurer de comptes individuels ne sont pas ajoutés à la liste des comptes. Afin de ne pas déborder.
Dans votre code vous avez besoin pour mettre en œuvre la méthode process (par défaut, le générateur de code génère une méthode vide:
Noter que
XMLEvent.END
marques de la balise de fermeture d'un élément. Ainsi, lors du traitement, il est complet. Si vous avez de la relier (à l'aide d'un FK) à son objet parent dans la base de données, vous pouvez traiter lesXMLEvent.BEGIN
pour le parent, de créer un espace réservé dans la base de données et l'utilisation de sa clé pour ranger avec chacun de ses enfants. Dans le finalXMLEvent.END
vous serait alors mise à jour de la mère.Noter que le générateur de code génère tout ce dont vous avez besoin. Vous avez juste à mettre en œuvre cette méthode et bien sûr la DB colle le code.
Il y a des échantillons pour obtenir vous avez commencé. Le générateur de code génère même votre POM fichiers, de sorte que vous pouvez immédiatement après génération à construire votre projet.
Le processus par défaut de la méthode est comme ceci:
Téléchargements:
Première
mvn clean install
la base (il doit être dans le local maven repo), puis le générateur. Et n'oubliez pas de configurer la variable d'environnementXML2J_HOME
suivant les instructions dans le guide d'utilisation.