PDA

View Full Version : [Java] Problema transazioni


satoshi2005
06-04-2007, 15:12
Salve gente,
Ho un problema con un progetto che sto realizzando.
Il programma dovrebbe scaricare un file XML di circa 7Mb da internet, contenente tutti i programmi che vanno in onda su S*y da oggi ai prossimi 7 giorni. Poi dovrebbe parsarlo e inserire i dati nel database (sono circa 27'000 records).

Ovviamente per inserire 27'000 records l'uso delle transazioni è ovvio. Il problema è che non appena faccio partire il programma, lui scarica il file da internet (e fin lì non ha problemi), poi non appena inizia con il parsing, la CPU sale (ovviamente) e la RAM sale costantemente fino a raggiungere un errore "java.lang.OutOfMemoryError: Java heap space". Prima di far partire il programma la RAM occupata è circa 530Mb. Facendo partire il programma sale fino a circa 1Gb, dopodiché dà errore (perché ho 1Gb di RAM). Il processo che occupa la RAM è mysql.
Ora, con le transazioni le query vengono salvate in memoria e inserite nel db non appena riceve il "commit". Ma com'è possibile che le transazioni occupano 500Mb di memoria quando il file prima di essere parsato è solo 7Mb??

Vi allego una parte di codice che magari potrebbe aiutarvi..

public class Main
{
public static Palimpsest palimpsest;
public static Connection db;
protected static String db_username = "username";
protected static String db_password = "";
public static void main(String[] args)
{
try
{
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/database", Main.db_username , Main.db_password);
db.setAutoCommit(false);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.commit();
db.close();
} catch (Exception e)
{
e.printStackTrace ();
}
}

Il DB è fatto nel seguente modo:

Tabella day: date (INT 11), clearDate (VARCHAR 28, p.es. Friday, April 6 2007)
Tabella category: id (INT 1), date (INT 11) (relazionato con day.date), shortName (VARCHAR 15), name (VARCHAR 30)
Tabella channel: id (INT 4), id_category (INT 1) (relazionato con category.id), date_category (INT 11) (relazionato con category.date), name (VARCHAR 32)
Tabella program: id (INT 8), time (INT 11), id_channel (INT 4) (relazionato con channel.id), genre (VARCHAR 64), name (VARCHAR 128), image (VARCHAR 128), description (TEXT)

La connessione che usa per fare qualsiasi query è sempre la stessa (è static), quindi faccio sempre "Main.db.prepareStatement(...)"

Qualcuno può aiutarmi?
Grazie, sato

^TiGeRShArK^
06-04-2007, 17:12
Salve gente,
Ho un problema con un progetto che sto realizzando.
Il programma dovrebbe scaricare un file XML di circa 7Mb da internet, contenente tutti i programmi che vanno in onda su S*y da oggi ai prossimi 7 giorni. Poi dovrebbe parsarlo e inserire i dati nel database (sono circa 27'000 records).

Ovviamente per inserire 27'000 records l'uso delle transazioni è ovvio. Il problema è che non appena faccio partire il programma, lui scarica il file da internet (e fin lì non ha problemi), poi non appena inizia con il parsing, la CPU sale (ovviamente) e la RAM sale costantemente fino a raggiungere un errore "java.lang.OutOfMemoryError: Java heap space". Prima di far partire il programma la RAM occupata è circa 530Mb. Facendo partire il programma sale fino a circa 1Gb, dopodiché dà errore (perché ho 1Gb di RAM). Il processo che occupa la RAM è mysql.
Ora, con le transazioni le query vengono salvate in memoria e inserite nel db non appena riceve il "commit". Ma com'è possibile che le transazioni occupano 500Mb di memoria quando il file prima di essere parsato è solo 7Mb??

Vi allego una parte di codice che magari potrebbe aiutarvi..

public class Main
{
public static Palimpsest palimpsest;
public static Connection db;
protected static String db_username = "username";
protected static String db_password = "";
public static void main(String[] args)
{
try
{
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/database", Main.db_username , Main.db_password);
db.setAutoCommit(false);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.commit();
db.close();
} catch (Exception e)
{
e.printStackTrace ();
}
}

Il DB è fatto nel seguente modo:

Tabella day: date (INT 11), clearDate (VARCHAR 28, p.es. Friday, April 6 2007)
Tabella category: id (INT 1), date (INT 11) (relazionato con day.date), shortName (VARCHAR 15), name (VARCHAR 30)
Tabella channel: id (INT 4), id_category (INT 1) (relazionato con category.id), date_category (INT 11) (relazionato con category.date), name (VARCHAR 32)
Tabella program: id (INT 8), time (INT 11), id_channel (INT 4) (relazionato con channel.id), genre (VARCHAR 64), name (VARCHAR 128), image (VARCHAR 128), description (TEXT)

La connessione che usa per fare qualsiasi query è sempre la stessa (è static), quindi faccio sempre "Main.db.prepareStatement(...)"

Qualcuno può aiutarmi?
Grazie, sato
Non è ke apri una sessione al DB x ogni riga del file da caricare? :mbe:
Altrimenti non mi spiego proprio come fai ad usare 500 MB di ram :mbe:

satoshi2005
06-04-2007, 18:26
Non è ke apri una sessione al DB x ogni riga del file da caricare? :mbe:
Altrimenti non mi spiego proprio come fai ad usare 500 MB di ram :mbe:
No, la connessione la apre una sola volta..

PGI-Bis
06-04-2007, 19:05
Prova a lanciare l'applicazione con -Xaprof. Del genere:

java -Xaprof pippo.Main > profile.txt

nel profilo troverai un elenco delle classi create, del numero di istanze e di quanta memoria queste occupano (per categoria). Per dirla breve, alla fine vedi chi si pappa tutta 'sta memoria.

sottovento
07-04-2007, 07:01
.Ovviamente per inserire 27'000 records l'uso delle transazioni è*¯vvio.

Perche'? Secondo me dipende da quanto vuoi fare, e secondo me non hai problemi di integrita' dei dati se non le usi. E' solo un'opinione, ovviamente, ma potresti darci un'occhiata...

In questi casi, da bravo pilota, mi preparo una checklist da seguire:
1 - Hai provato a fare il commit() record per record e vedere cosa succede? Facendo una sola commit(), probabilmente l'area di rollback e' cresciuta un pochino...
2 - Sei sicuro di aprire e chiudere gli Statement? Tutti gli Statement?
3 - Sei sicuro che il problema non sia il parser? Se stai usando DOM, per questa applicazione, potrebbe essere benissimo lui...
4 - Hai provato con i suggerimenti di PGI-Bis?

satoshi2005
07-04-2007, 09:19
Perche'? Secondo me dipende da quanto vuoi fare, e secondo me non hai problemi di integrita' dei dati se non le usi. E' solo un'opinione, ovviamente, ma potresti darci un'occhiata...

In questi casi, da bravo pilota, mi preparo una checklist da seguire:
1 - Hai provato a fare il commit() record per record e vedere cosa succede? Facendo una sola commit(), probabilmente l'area di rollback e' cresciuta un pochino...
2 - Sei sicuro di aprire e chiudere gli Statement? Tutti gli Statement?
3 - Sei sicuro che il problema non sia il parser? Se stai usando DOM, per questa applicazione, potrebbe essere benissimo lui...
4 - Hai provato con i suggerimenti di PGI-Bis?

Con le transazioni ci metti molto di meno.. E non vorrei che l'utente debbe aspettare troppo!

1. Si, con l'autocommit si crasha lo stesso
2. Ecco cosa ho dimenticato! Ahahah
3. Com'è possibile che sia lui se la stringa più grande che gestisce è di 7Mb?
4. Devo ancora provare, ma penso che mi hai già risolto il problema!

Grazie mille! Sato

satoshi2005
07-04-2007, 09:57
Devo rifare tutto! Mi si è rotto l'hard-disk! NOOOOOOOOOOOOOOO

satoshi2005
18-04-2007, 09:05
Ok ho rifatto tutto, il problema era appunto perché non chiudevo gli statements (e nemmeno i resultset), dunque grazie!
Ora però ho un altro problema..
Se metto:
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/satoepg", Main.db_username , Main.db_password);
db.setAutoCommit(false);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.commit();
db.close();

Ci mette tanto quanto:
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/satoepg", Main.db_username , Main.db_password);
db.setAutoCommit(true);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.close();

(sempre tra i 15 e i 20 secondi ad inserire 7'500 dati).. non è normale no?

Grazie, sato

edit: Vi ricordo: La connessione che usa per fare qualsiasi query è sempre la stessa (è static), quindi faccio sempre "Main.db.prepareStatement(...)"

Angus
18-04-2007, 11:17
Ok ho rifatto tutto, il problema era appunto perché non chiudevo gli statements (e nemmeno i resultset), dunque grazie!
Ora però ho un altro problema..
Se metto:
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/satoepg", Main.db_username , Main.db_password);
db.setAutoCommit(false);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.commit();
db.close();

Ci mette tanto quanto:
Class.forName("com.mysql.jdbc.Driver");
db = DriverManager.getConnection("jdbc:mysql://127.0.0.1/satoepg", Main.db_username , Main.db_password);
db.setAutoCommit(true);
palimpsest = new Palimpsest();
palimpsest.updateDays();
db.close();

(sempre tra i 15 e i 20 secondi ad inserire 7'500 dati).. non è normale no?

Grazie, sato

edit: Vi ricordo: La connessione che usa per fare qualsiasi query è sempre la stessa (è static), quindi faccio sempre "Main.db.prepareStatement(...)"

Prova a chiudere solo i ResultSet e a riutilizzare la PreparedStatement (se non lo facessi già :sofico: ). Prendi in considerazione anche l'utilizzo di addBatch().