IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JDBC Java Discussion :

problème bloquant avec "SELECT LAST_INSERT_ID()"


Sujet :

JDBC Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Par défaut 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) 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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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 ..)

  2. #2
    in
    in est déconnecté
    Membre Expert Avatar de in
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    1 612
    Détails du profil
    Informations personnelles :
    Localisation : France, Finistère (Bretagne)

    Informations forums :
    Inscription : Avril 2003
    Messages : 1 612
    Par défaut
    Je pense qu'il faut que tu regarde du côté des options transactionnelles de ton objet connection. (cf la javadoc de l'interface Connection)

    Citation Envoyé par javadoc
    TRANSACTION_NONE
    A constant indicating that transactions are not supported.
    TRANSACTION_READ_COMMITTED
    A constant indicating that dirty reads are prevented; non-repeatable reads and phantom reads can occur.
    TRANSACTION_READ_UNCOMMITTED
    A constant indicating that dirty reads, non-repeatable reads and phantom reads can occur.
    TRANSACTION_REPEATABLE_READ
    A constant indicating that dirty reads and non-repeatable reads are prevented; phantom reads can occur.
    TRANSACTION_SERIALIZABLE
    A constant indicating that dirty reads, non-repeatable reads and phantom reads are prevented.
    PS : utiliser les termes "urgent" et autres dans un intitulé de post conduit souvent à ce que les réponses viennent très lentement ...

  3. #3
    Membre confirmé Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Par défaut
    Merci de ta réponse, je creuse dans la foulée et autant pour moi pour les termes employés, je vais dorénavant faire le cool en disant que tout est normal et qu'il n'y a pas le feu même si c le cas

    Je te fournis le code d'init de ma connection plus bas à tous hasards ... mais ça me semble bien basic comme code.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    public class TableConnect
    {
    <div style="margin-left:40px">private Connection      	connection;
     
    public TableConnect (String driver, 
                                 String host,
      		             String port, 
                                 String ts,
                                 String user, 
                                 String pwd) 
              throws SQLException, InstantiationException, IllegalAccessException, ClassNotFoundException 
      {
    	  driver  = driver;
    	  host    = host;
    	  port    = port;
    	  db      = ts;
    	  user    = user;
    	  pwd     = pwd;
    	  url     = "jdbc:mysql://" + host + ":" + port + "/" + db + "?user=" +user + "&password=" + pwd;
     
        Class.forName(driver).newInstance();
        connection = DriverManager.getConnection(url);
      }
     
      public Connection getConnection()
      {
        return connection;
      }
     
      protected void finalize() throws Throwable 
      {
        if (connection!=null) connection.close();
        super.finalize();
      }
      </div>...

  4. #4
    Membre Expert Avatar de willoi
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2006
    Messages
    1 355
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 355
    Par défaut
    Et ou sont-elles déclarées tes transactions ?

    Qu'utilise tu pour gérer cela ? Un transaction manager ?

  5. #5
    Membre confirmé Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Par défaut
    Salut et merci, chaque transaction est analysée par un parser, un record correspondant à la table est construit, et un insert est fait par un statement.executeUpdate(<insert record in table>) suivi du statement.executeQuery("SELECT LAST_INSERT_ID()") problématique.

    Un moteur est sensé être parallèle. le fait qu'il y ait deux fois le même ID retourné sous entend que le 2° insert parallèle n'est pas encore fait ou que le 1° ne l'est pas encore ou tout et n'importe quel autre cas.

    Le truc qui me gêne dans ce code, c le fait qu'après le premier insert du 1° thread, et avant le last_insert_id, le 1° thread peut être interrompu par le 2° thread qui fait son insert, le 1° reviendrait avec un last_id = au second insert et après le 2° thread commence à être exécuté ...

    Ne peut on pas agglomérer les deux requêtes en les rendant atomiques au sein de chaque thread pour s'assurer du last_insert_id ?

  6. #6
    Membre Expert Avatar de willoi
    Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2006
    Messages
    1 355
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 355
    Par défaut
    Ben tu peux faire en sorte que tes requetes soient placées dans une queue.
    Dans ce cas, tu pourras les exceuter dans une meme super transactions ou dans une transaction exisatente (cf les paramètres cités par in)
    Mais c'est peut-etre assez complexe à mettre en oeuvre.

    Autre possibilité, travailler avece les Lock de base de données afin d'empecher un nouvel insert tant qu'une transaction est en train de creer des enregistrements.

    Enfin bref, en conclusion, il te faut gérer la syncronisation de tes écritures en base de données.

  7. #7
    Membre confirmé Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Par défaut 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 : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    ..
    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

  8. #8
    Membre confirmé Avatar de r2d2abc
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    212
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 212
    Par défaut
    Encore lui vous devez vous dire eh oui , en creusant encore un peu car la soluce apportée en interne en passant par un fichier externe ne me satisfaisait pas, je trouvais troublant de passer par un fichier property pour définir une clé unique et donc j'ai essayé la fonction getGeneratedKeys eh bé sacrée surprise, cette fonction propre au statement est donc synchronisée avec la requête et renvoie les colonnes autoincrément modifiées donc exit le fichier externe de property et la fonction monolithique synchronisée insert/select last..id(), il suffit juste de changer
    /** ///1° method
    statement.executeUpdate(query);
    /**///2° method
    statement.executeUpdate(query,Statement.RETURN_GENERATED_KEYS);
    ResultSet keygen= statement.getGeneratedKeys();
    if (keygen.next())
    {
    lasttxid=keygen.getInt(1);
    }
    keygen.close();
    ...
    et cela marche au poil ...


    ...
    hope this helps

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 18/08/2008, 08h57
  2. Problème Insert avec un Select
    Par LhIaScZkTer dans le forum Langage SQL
    Réponses: 3
    Dernier message: 18/06/2007, 11h57
  3. Select et insert avec des caracteres speciaux (quote ')
    Par Paco75 dans le forum Requêtes
    Réponses: 2
    Dernier message: 25/10/2006, 14h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo