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

Sybase Discussion :

[T-SQL]verrou sur une ligne avant un select


Sujet :

Sybase

  1. #1
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut [T-SQL]verrou sur une ligne avant un select
    Bonjour à tous,

    je fouine partout depuis 2 jours dans les forums et les doc mais sans succès.
    Voici le code (fortement simplifié) de la procédure stockée qui permet de faire des débits sur un compte (je ne met pas la gestion des erreurs par exemple) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    select @NOUVEAU_SOLDE=SOLDE from COMPTE where ID = @ID
    begin transaction trans
    -- pleins de tests pour modifier @NOUVEAU_SOLDE et faire un débit
    update COMPTE set SOLDE = @NOUVEAU_SOLDE where ID = @ID
    -- pleins d'autres insert, notemment dans des historiques
    commit ou rollback selon divers tests
    Il y a un problème d'exclusion mutuelle au niveau du SELECT :
    Thread_1 recupere NOUVEAU_SOLDE_1
    Thread_2 recupere NOUVEAU_SOLDE_1
    Thread_1 modifie sa valeur NOUVEAU_SOLDE_1 en NOUVEAU_SOLDE_2
    Thread_2 modifie sa valeur NOUVEAU_SOLDE_1 en NOUVEAU_SOLDE_3
    Thread_1 update SOLDE avec NOUVEAU_SOLDE_2, puis commit
    Thread_2 update SOLDE avec NOUVEAU_SOLDE_3, puis commit

    le calcul de la nouvelle valeur NOUVEAU_SOLDE_2 est perdue et écrasée par le Thread_2. Donc le débit fait par Thread_1 n'est jamais répercuté sur le compte client :-(

    Il aurait fallut que Thread_2 patiente jusqu'au commit de Thread_1 pour qu'il utilise NOUVEAU_SOLDE_2 au lieu de NOUVEAU_SOLDE_1.

    J'ai trouvée une solution que je trouve très moche. La voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    begin transaction trans
    update COMPTE set SOLDE = ( select SOLDE from COMPTE where ID = @ID ) where ID = @ID
    select @NOUVEAU_SOLDE=SOLDE from COMPTE where ID = @ID
    -- pleins de tests pour modifier @NOUVEAU_SOLDE
    update COMPTE set SOLDE = @NOUVEAU_SOLDE where ID = @ID
    -- pleins d'autres insert
    commit ou rollback selon divers tests
    Le premier update sert uniquement à mettre un verrou sur la ligne que je vais modifier. Ca fonctionne très bien. Le second thread attend que la transaction se termine avant d'entrer dans le premier update.
    Mais y-a-t-il une solution propre et standard ?

    Dans mes recherches, j'ai donc exploré pleins de techniques, trouvées sur les forums et autres doc :
    - débuter la transaction avant le select, en mettant un "at isolation" sur le select, mais j'ai un deadlock
    - mettre un holdlock sur le select, mais ça ne change rien

    Il y a aussi la solution du curseur sur le SELECT, avec un "FOR UPDATE". Mais ce select renvoit soit une ligne, soit aucune ligne. Donc faire un curseur me parait un peu lourd. De plus j'ai peur que ça utilise trop de ressources. En effet, cette procédure est appelée sur la journée en moyenne 900 fois par secondes, et atteint en pic deux fois par jour 1400 fois par secondes. Et pour la table COMPTE, la voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    1> sp_spaceused COMPTE
    2> go
     name                 rowtotal    reserved        data            index_size      unused
     -------------------- ----------- --------------- --------------- --------------- ---------------
     COMPTE           46223277    7002578 KB      4954592 KB      1989784 KB      58202 KB
    la solution envisagée pour le moment (et utilisée dans une autre procédure stockée qui gère d'autres types de comptes) est de placer les calculs erronés dans une table via un algo légèrement tarabiscoté, et de traiter cette table la nuit à l'aide d'un batch lorsque le traffic est le plus bas. Cette solution est pourrie et laisse passer quelques "débit gratuit". Il me faut donc une solution qui règle le problème d'accès concurrent "en live".

    PS : merci d'avoir été jusqu'au bout de mon explication ;-)

  2. #2
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 307
    Détails du profil
    Informations personnelles :
    Âge : 65
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 307
    Points : 1 828
    Points
    1 828
    Par défaut
    Citation Envoyé par dinobogan
    J'ai trouvée une solution que je trouve très moche. La voici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    begin transaction trans
    update COMPTE set SOLDE = ( select SOLDE from COMPTE where ID = @ID ) where ID = @ID
    select @NOUVEAU_SOLDE=SOLDE from COMPTE where ID = @ID
    -- pleins de tests pour modifier @NOUVEAU_SOLDE
    update COMPTE set SOLDE = @NOUVEAU_SOLDE where ID = @ID
    -- pleins d'autres insert
    commit ou rollback selon divers tests
    Le premier update sert uniquement à mettre un verrou sur la ligne que je vais modifier.
    Cette solution me semble tout à fait raisonnable.

    Une alternative consiste à avoir une table de type sémaphore pour faire un lock "logique". Le problème est que cela risque de créer un "hot point", avec les problèmes de perfs associées.

    Le holdlock (ou isolation 3) aurait à priori dû marcher, pour autant que la transaction est démarrée avant le select:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    begin tran
    select ... from ... where ... at isolation 3
    -- on fait les modifs, etc.
    update ...
    commit
    Dans tous les cas j'éviterais l'utilisation d'un curseur pour ce genre de chose, surtout au vu du nombre d'exécutions par seconde que tu annonces.

    Michael

  3. #3
    Membre chevronné

    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    1 307
    Détails du profil
    Informations personnelles :
    Âge : 65
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2006
    Messages : 1 307
    Points : 1 828
    Points
    1 828
    Par défaut
    Après un petit test je vois que le "select ... at isolation 3" pose un verrou de type shared, ce qui ne sert évidemment à rien.

    Donc ta première solution semble la plus adaptée.

    Michael

  4. #4
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Merci beaucoup !
    Je garde donc ma solution pas super propre avec l'update

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

Discussions similaires

  1. Changer le type de verrou sur une base de données SQL Server 2008
    Par bigzouzou dans le forum Administration
    Réponses: 1
    Dernier message: 03/08/2011, 11h20
  2. Accès SQL dans fichier excel erreur sur une ligne
    Par troubleshooting dans le forum Excel
    Réponses: 3
    Dernier message: 17/11/2009, 14h46
  3. Sql serveur 2000: Résultat sur une ligne
    Par gamereplay dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 03/07/2007, 16h29
  4. [SQL Server] Filtré sur une table avant une jointure externe
    Par TangoZoulou dans le forum Langage SQL
    Réponses: 2
    Dernier message: 06/11/2006, 15h52
  5. [SQL] Conditions sur une date dans une requete
    Par poufouille dans le forum Bases de données
    Réponses: 4
    Dernier message: 17/03/2004, 14h25

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