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

  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 : 53
    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 : 53
    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
    OK willoy et merci pour tes conseils. j'ai commencé un truc un peu similaire pour être atomique.
    J'ai créé une classe unique gérant les insert/last_insert_id que j'ai mise en static synchronized.
    Tous les threads actifs ayant à faire la même opération (insert/retrieve last insert id), ils passeront les uns derrière les autres, ce qui garantira l'unicité car n'étant pas en mode autocommit(default) donc écriture immédiate sur disque des modifications, j'aurais le bon résultat tout de suite.
    Certes cela bouffe un eu le parallélisme mais bon ..
    je te tiens au courant de l'avancée ..
    Juste une question, cela est normal comme fonctionnement en multi thread transactionnel d'avoir ce type de pb ?
    merci encore pour ton aide

  8. #8
    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 : 53
    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 oui a partir du moment ou tes applis (ou threads) fonctionnent dans des clusters différents, il faut se poser ce genre de questions.

    Cela dit, peut-etre existe-t-il des solutions toute faites qui permettent de réaliser cela de façon quasi-automatique grâce à un paramétrage adéquat.

    Regarde du coté de Spring, peut-être y trouveras-tu quelquechose

  9. #9
    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 que tu peux quand même t'en sortir en précisant le niveau d'isolation de tes transactions :

    Citation Envoyé par le Net
    TRANSACTION_SERIALIZABLE : La transaction a les privilèges exclusifs en lecture/ecriture sur des données en les bloquant; les autres transactions ne peuvent ni lire ni écrire sur ces données. C'est le niveau d'isolation le plus contraignant empêchant également les " phantom reads ".
    Ce mode doit être utilisé pour les systèmes stratégiques qui exigent une parfaite isolation transactionnelle. Il assure que les données lues ont été validées, que la lecture reste identique au fil du temps et que de mystérieuses données validées n'apparaissent pas dans la base en cours de traitement en raison de transactions simultanées.

    Ce niveau d'isolation doit être utilisé avec parcimonie, car il induit un coût certain. Si toutes les opérations sont réalisées dans ce mode, les performances de la base de données ont tendance à se dégrader très rapidement jusqu'à l'immobilisation éventuelle.
    de plus force peut être l'autocommit à true pour que les données soient validées dès que possible ...

  10. #10
    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 In de ton aide.

    Dieu ce que tu m'indiques me fait trembler "... l'immobilisation éventuelle"

    J'ai analysé ce que tu m'avais proposé plus haut en regardant ce lien (excellent au demeurant) http://java.developpez.com/faq/jdbc/...defTransaction

    Je découvre de nouvelles pistes/possibilité dont je ne comprends pas tjrs les tenants/aboutissants donc piano dans les implantations, bcp de tests avant d'intégrer cela chez le client (en PROD je reprécise )

    Comme dit avec Willoy, le multi accès (par multi thread) doit être totalement géré par programme pour éviter ce genre de pb, un peu surpris tout de même.

    Merci encore pour ton aide, je te tiens au courant de mes recherches/tests

  11. #11
    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
    comme promis je vous livre les tests résultants, étonnants et logiques en fait :

    contexte de test:
    on part d'une table vide
    maxthread(=10) threads écrivant "simultanément" maxtransaction(=20) dans une même table

    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
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
     
    class Dbmt
    {
      ...
     
       void go()
       {
          int maxth=20,maxtx=10;
            Tinsert ti[]=new Tinsert [maxth];
            for (int i=0;i<maxth;i++) 
              {
                System.out.println("create thread -> "+i);
                ti[i]=new Tinsert (this, i*100, connection, maxtx,db); 
              }
            for (int i=0;i<maxth;i++) 
              {
                System.out.println("start thread -> "+i);
                ti[i].start();
              }
        ...
       }
      int getLastId (Statement statement)
      {
        int max=-1;
        ResultSet rs=null;
        try
        {
          try
            {
              rs = statement.executeQuery("SELECT LAST_INSERT_ID()");
              if (rs.first())
              {
                max = rs.getInt(1);
              }
            }
          finally
            {
              rs.close();
            }
        }
        catch (Exception e) 
        {
          e.printStackTrace();
        }
        return max;
      }
     
      synchronized int insertQuery (Statement statement, String query )
      {
        try
          {
            statement.executeUpdate(query); 
          }
        catch (Exception e)
          {
            e.printStackTrace();
          }
        return getLastId (statement);
      }
     
    }
     
    class Tinsert extends Thread
    {
      ..
     
      public void run ()
      {
        Random random=new Random();
        for (int i=0;i<_maxtx;i++)
          {
            String value;
            int j=_id+i;
            value="'pipo_"+j+"','"+j+"'";
            try
              {
                int sleep=/** /20;/**/10+random.nextInt(100);/**/
                int retcode=0;
                /**///1° method
                retcode=_statement.executeUpdate("INSERT INTO "+_table+" (name,jobid) VALUES ("+value+")");
                System.out.println(_id+":"+i+": insert: retcode="+retcode+" lastid="+_parent.getLastId(_statement));
                /** ///2° method
                int lastid=_parent.insertQuery(_statement, "INSERT INTO "+_table+" (name,jobid) VALUES ("+value+")");
                System.out.println(_id+":"+i+": insert: retcode="+retcode+" lastid="+lastid+" sleep="+sleep);
                /**/
                //yield();
                Thread.sleep(sleep);
              }
            catch (Exception e)
              {
                //e.printStackTrace();
              }
            finally
            {
            }
          }
        System.out.println(_id+": end processing");
        _parent.decthreadactif();// notify parent which waits..
      }
    }
    stucture de la table
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    statement.executeUpdate
            ("CREATE TABLE "+db+" ( " 
              + "id   integer NOT NULL AUTO_INCREMENT,"
              + "name char(255) NOT NULL," 
              + "jobid integer," 
              + "PRIMARY KEY (id)"
              + ")"
              );
    résultat des courses
    1° test Avec un sleep constant (pour laisser passer un peu de jus) j'obtiens une distribution de l'exécution uniforme des 1..N threads avec un lastid uniforme (oups ca foire bien -> même last id après chaque insert)
    0:0: insert: retcode=1 lastid=20
    100:0: insert: retcode=1 lastid=20
    200:0: insert: retcode=1 lastid=20
    300:0: insert: retcode=1 lastid=20
    400:0: insert: retcode=1 lastid=20
    500:0: insert: retcode=1 lastid=20
    600:0: insert: retcode=1 lastid=20
    700:0: insert: retcode=1 lastid=20
    800:0: insert: retcode=1 lastid=20
    900:0: insert: retcode=1 lastid=20
    1000:0: insert: retcode=1 lastid=20
    1100:0: insert: retcode=1 lastid=20
    1200:0: insert: retcode=1 lastid=20
    1300:0: insert: retcode=1 lastid=20
    1400:0: insert: retcode=1 lastid=20
    1500:0: insert: retcode=1 lastid=20
    1600:0: insert: retcode=1 lastid=20
    1700:0: insert: retcode=1 lastid=20
    1800:0: insert: retcode=1 lastid=20
    1900:0: insert: retcode=1 lastid=20

    2° test Avec un sleep joueur (simulation d'un temps de traitement différent entre 2 thread) on obtient une distribution de l'exécution moins uniforme des 1..N threads avec un lastid moins constant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int sleep=/** /20;/**/10+random.nextInt(100);/**/
    traces au niveau de la 7° transaction, les threads sont désordonnés et le lastid récupéré commence à perdre sa constance (normal je laisse à mysql le temps de répercuter les inserts ou ?)
    ...
    300:7: insert: retcode=1 lastid=144
    1800:7: insert: retcode=1 lastid=144
    1300:6: insert: retcode=1 lastid=145
    1600:7: insert: retcode=1 lastid=146
    700:7: insert: retcode=1 lastid=150
    1700:7: insert: retcode=1 lastid=157
    600:7: insert: retcode=1 lastid=161
    1500:7: insert: retcode=1 lastid=162
    400:7: insert: retcode=1 lastid=163
    1200:7: insert: retcode=1 lastid=163
    100:7: insert: retcode=1 lastid=163
    500:7: insert: retcode=1 lastid=164
    1100:7: insert: retcode=1 lastid=164
    900:7: insert: retcode=1 lastid=164
    1000:7: insert: retcode=1 lastid=164
    0:7: insert: retcode=1 lastid=164
    800:7: insert: retcode=1 lastid=164
    1400:7: insert: retcode=1 lastid=164
    1900:7: insert: retcode=1 lastid=164
    200:7: insert: retcode=1 lastid=165
    ....

    3° test On garde le sleep aléatoire et on utilise la methode 2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
                /** /
                retcode=_statement.executeUpdate("INSERT INTO "+_table+" (name,jobid) VALUES ("+value+")");
                System.out.println(_id+":"+i+": insert: retcode="+retcode+" lastid="+_parent.getLastId(_statement));
                /**/
                int lastid=_parent.insertQuery(_statement, "INSERT INTO "+_table+" (name,jobid) VALUES ("+value+")");
                System.out.println(_id+":"+i+": insert: retcode="+retcode+" lastid="+lastid+" sleep="+sleep);
                /**/
    traces dés le début. Le lastid suit et est unique (yes c mon pb de base tout de même ), les threads sont moyennement schedulés aléatoirement (surtout au début), le synchronized réinjecte de l'ordre mais le sleep donne de l'enthropie au système (on le voit sur le dernier block à la fin, les threads commencent à être désordonnés)
    ...
    0:0: insert: retcode=0 lastid=1 sleep=30
    100:0: insert: retcode=0 lastid=2 sleep=36
    200:0: insert: retcode=0 lastid=3 sleep=11
    300:0: insert: retcode=0 lastid=4 sleep=96
    400:0: insert: retcode=0 lastid=5 sleep=71
    500:0: insert: retcode=0 lastid=6 sleep=25
    600:0: insert: retcode=0 lastid=7 sleep=28
    700:0: insert: retcode=0 lastid=8 sleep=63
    800:0: insert: retcode=0 lastid=9 sleep=65
    900:0: insert: retcode=0 lastid=10 sleep=53
    1000:0: insert: retcode=0 lastid=11 sleep=96
    1100:0: insert: retcode=0 lastid=12 sleep=109
    1200:0: insert: retcode=0 lastid=13 sleep=60
    1300:0: insert: retcode=0 lastid=14 sleep=60
    1400:0: insert: retcode=0 lastid=15 sleep=70
    ...
    0:8: insert: retcode=0 lastid=163 sleep=10
    500:8: insert: retcode=0 lastid=164 sleep=41
    300:8: insert: retcode=0 lastid=165 sleep=61
    400:8: insert: retcode=0 lastid=166 sleep=17
    700:8: insert: retcode=0 lastid=167 sleep=58
    600:8: insert: retcode=0 lastid=168 sleep=70
    900:8: insert: retcode=0 lastid=169 sleep=22
    1100:8: insert: retcode=0 lastid=170 sleep=51
    800:8: insert: retcode=0 lastid=171 sleep=67
    1200:8: insert: retcode=0 lastid=172 sleep=10
    1000:8: insert: retcode=0 lastid=173 sleep=105
    1300:8: insert: retcode=0 lastid=174 sleep=35
    1400:8: insert: retcode=0 lastid=175 sleep=42
    1700:8: insert: retcode=0 lastid=176 sleep=93
    1600:8: insert: retcode=0 lastid=177 sleep=24
    1500:8: insert: retcode=0 lastid=178 sleep=68
    1900:8: insert: retcode=0 lastid=179 sleep=44
    1800:8: insert: retcode=0 lastid=180 sleep=23

    bon j'ai atteint mon but (bon lastid entre 2 tx) mais ca m'a l'air pas très optimal comme système (sur mon portable surtout), je me demande ce que cela donnerait sur des tx rapides, si qqu1 a une autre soluce je suis preneur pour virer ce goulet d'étranglement de synchronized....

  12. #12
    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
    Excuse moi, mais as tu essayé de mettre le type de transaction de ta connection à "serializable" et de ne pas toucher au reste du code ?

    Je serais curieux des résultats ...

    sinon, juste en passant, tu devrait utiliser des PreparedStatement, ça améliorerait les perfs puisque ta requête SQL ne serait pas recompilée à chaque fois par le moteur de la base ....

  13. #13
    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 In, je vais tester cette piste de transaction serializable ainsi que voir avec un preparedstatement / performances.
    je te tiens au courant de la suite de l'aventure
    ...

  14. #14
    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 In, je reviens avec des informations / ce problème. J'ai essayé les transaction sérializables, les prepared statements, batch.....

    preparedstatement (PS)
    1:J'ai créé au début un PS au niveau du thread maître qui lance ceux dédiés à chaque transaction en le passant en paramètre -> les données se télescopent, cela me paraît normal puisque plusieurs thread peuvent faire des PS.setxxx au même moment donc au moment de l'exécution du query mayonnaise dans la table
    2: au niveau de chaque thread -> meilleure performance mais bon vu que je l'exécute que deux fois par thread qui process une transaction, l'effet n'est certainement pas majeure / perf
    3: j'ai créé au niveau du thread maître autant de PS que de sous-thread que je lancerai (un max de 20 simultanés) que je passe en paramètres. comme ils sont créés au début, pas de temps perdus en création les perf sont encore un 'poil' meilleures

    transaction_serializable
    n'amène strictement rien si ce n'est une exécution plus lente et 'curieuse'

    Les batch n'autorisent pas le SELECT donc stop
    J'ai fait une commande groupée insert/select suivi d'un execute/commit avec ma connection en autocommit à false -> mon bug lastid ré apparaît.

    Dernière idée (force brute) de ouf mais à ce point j'ai créé 20 connections avec 20 PS dessus et le bloc INSERT/SELECT LAST_INSERT_ID en NON synchronized dans chaque thread, comme je m'en doutais le bug disparaît puisque j'utilise 20 canaux différents, mais le temps de latence cette fois se situe au niveau de mysql car ca rame autant mais le LASTID est bon

    tous les tests se font sur mon portable.
    pour 20threads*20transactions
    en mode non synchronisés, c limite instantané (4 sec en moyenne)
    en mode synchronisés, c limite (16 sec en moyenne)

    voilà pour les news
    ce qui me tue dans cette histoire, c que le concept de base ne me paraît pas ahurissant, g x threads qui décident d'écrire une ligne et récupère la position dans la table qui devient une clé unique de recherche, si il y en a y qui écrivent en rafale, mysql attend de toutes les écrire avant de répondre à chaque thread le total écrit plutôt que l'intermédiaire qui s'incrémente.

    est-ce qu'il n'existe pas un moyen d'avoir une clé unique plutôt que ce lastid en rajoutant un colonne autoincrement qui me providerait une clé unique ?
    je suis pour les soluce simples qui marchent

    merci d'avance à tous ceux qui me filent des pistes

  15. #15
    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

  16. #16
    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