problème bloquant avec "SELECT LAST_INSERT_ID()"
Bonjour à tous. je reprends un soft écrit en java/jdbc/mysql et donc un bon bug (pour moi:cry:) d'entrée à corriger et là c bloquant / limite grave car le truc tourne en production....
Contexte
Le but est de traiter des transactions (x par minute).
Dans le but d'être performant, ces traitements ont été parallélisés.
Thread principal
Ouverture de la connection sur mysql
Tant que (pas fin)
Attente d'une transaction
Traitement de la transaction (connection)
Clôture de la connection
Sortie
Thread de traitement
Création statement
statement.executeUpdate ("insert... <transaction in table>")
Récupération de la dernière position dans la table qui devient la ref unique de la transaction
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| try
{
rs = stm.executeQuery("SELECT LAST_INSERT_ID()");
if (rs.first())
{
txid= rs.getInt(1);
}
else
{
txid = -1;
}
}
catch (SQLException e)
{
..
}
system.out.println ("transaction id="+txid); //transaction id=28232 |
Traitements ...
Clôture statement
Sortie
Le pb est que pour deux transactions arrivant en même temps, dans les deux threads on obtient le même transaction id, ce qui est rare le restant du temps car il y a à peu près 1 transaction toutes les 5 secondes, mais qd on va arriver à plusieurs par seconde je crains d'entrée le résultat.
En fait le 2° récupère le transaction id du premier et là c la mayonnaise.
Il faudrait que le premier insert s'exécute totalement pour que le 2° récupère le rang mis à jour (je cherche quelque chose pour que ce soit l'un derrière l'autre style criticalsection ou equivalent ou mettre un commit en fait je ne sais pas je chercher cherche ..)
Pour info solution retenue du post SELECT LAST_INSERT_ID() plus bas
Salut à tous et merci à In & Willoy pour leur aide, ce post fait juste suite en conclusion à celui qui s'intitule "problème bloquant avec SELECT LAST_INSERT_ID()" plus bas pour ceux que cela interesse.
Voici la solution retenue. Comme on a un grand nombre de transactions à traiter et que le synchronized sur la BD plante complètement les performances, la solution retenue est mixte.
Un synchronized sur une fonction "rapide" dont la valeur de départ est initialisée par une table et aprés on évolue avec un fichier properties. le SELECT LAST_INSERT_ID() sur un INTEGER est remplacé par moitié par le nb de jours depuis une date pivot (ce qui donne une relation temporelle et pas juste un simple compteur sans sens) et un compteur de transactions pour l'autre moitié. Certainement pas la panacée pour les esthètes mais rapide et simple..
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| final static DateTimeFormatter datefmt = DateTimeFormat.forPattern("yyyyMMdd");// see "Class DateTimeFormat" javadocs for symbol supported
final static String DATEREF_DEFVALUE="20090101";
final static String DATE_REF ="dateref";
final static String DATE_RAZ ="dateraz";
final static String JOB_NUMBER="jobnumber";
synchronized public static String getUniqueDBJobId()
{
Properties prop;
String dbjobid =""; // result key
int nbdays =0; // delta (dateact-dateref) in days
int jobnb =0; // jobnb
String dateref, // date reference -> "20090101"
dateraz, // last raz counter date -> "20090419"
dateact; // day date -> "20090420"
// load from "jobnumber.properties"
prop = loadProperties(JOBNUMBER_FILE);
// extract values: if file ! exist we set default values
dateact=new DateTime().toString("yyyyMMdd"); // "20090420" date of the current day
dateref=prop.getProperty(DATE_REF,DATEREF_DEFVALUE); // "20090101"
dateraz=prop.getProperty(DATE_RAZ,dateact); // "20090419"
jobnb=new Integer (prop.getProperty(JOB_NUMBER, "0")).intValue();
// if date raz & act are differents, we reset job number & set new date raz values
//System.out.println("dateact="+dateact+" dateref="+dateref);
if (!dateraz.equals(dateact))
{
dateraz=dateact;
jobnb=0;
}
// we advance counter -> counter+1 (0->1 ,...., n -> n+1 )
jobnb++;
//update properties: all values because file can disappears
prop.setProperty(DATE_REF, dateref);
prop.setProperty(DATE_RAZ, dateraz);
prop.setProperty(JOB_NUMBER, new Integer(jobnb).toString());
// save to "jobnumber.properties"
saveProperties(prop, JOBNUMBER_FILE, "unique job number");
//compute final key
DateTime dtref = datefmt.parseDateTime(dateref);
DateTime dtact = datefmt.parseDateTime(dateact);
Days d = Days.daysBetween(dtref, dtact);
nbdays = d.getDays();
DecimalFormat df = new DecimalFormat("00000"); // 1, 456, 9865, 99999 -> "00001", "00456", "09865", "99999"
dbjobid=nbdays+df.format (jobnb); // "120"+"00001" -> "12000001"
return dbjobid;
} |
ce qui donne en trace
Citation:
..
dateref=20090101 dateraz=20090418 dateact=20090418 key=10700036
dateref=20090101 dateraz=20090418 dateact=20090418 key=10700037
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800001
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800002
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800003
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800004
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800005
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800006
dateref=20090101 dateraz=20090419 dateact=20090419 key=10800007
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900001
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900002
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900003
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900004
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900005
dateref=20090101 dateraz=20090420 dateact=20090420 key=10900006
dateref=20090101 dateraz=20090421 dateact=20090421 key=11000001
dateref=20090101 dateraz=20090421 dateact=20090421 key=11000002
dateref=20090101 dateraz=20090421 dateact=20090421 key=11000003
...
...
hope this helps