2009-12-06 2 views
0

Nous utilisons NHibernate pour mettre à jour une table de comptes avec un solde pour un utilisateur. Un test qui nous contrecarre est la suivante:La colonne version du serveur SQL met à jour plus que ce qu'elle devrait


var s = NHibernateHelper.OpenSession(); 
var q = s.CreateQuery("Update Account set Balance=? where UserId=? and Version=?"); 
var acc = s.Load(1); 
ITransaction tx = null; 
for (int j = 0; j < NUM_UPDATES; j++) 
{ 
    int rowcount = 0; 
    var tx = s.BeginTransaction(); 
    do 
    { 
     s.Refresh(acc); 
     q.SetDouble(0, acc.Balance + _amount).SetInt32(1, 1).SetBinary(2, acc.Version); 
     rowcount = q.ExecuteUpdate(); 
    } while (rowcount <= 0); 
    tx.Commit(); 
} 

ce code est en cours d'exécution à la fois dans un seul fil et dans plusieurs threads simultanément (d'où le versioning). il s'exécute correctement, mais - le numéro de version est incrémenté d'un nombre supérieur au nombre réel de mises à jour (NUM_UPDATES) d'une certaine valeur.

par exemple. si nous effectuons 16 mises à jour, le numéro de version est incrémenté de 16 ou 17. Si nous effectuons 1000 mises à jour, le numéro de version est incrémenté de 1004.

Si 16 threads exécutent ce même code (sur une machine à 4 coeurs) avec 1000 mises à jour chacune, puis nous obtenons beaucoup de tentatives évidemment, et la version est incrémentée de 16064 (1004 * 16).

Une idée de ce qui cause cela? Y a-t-il une cause d'alarme?

+0

peut-être une question stupide - où le numéro de version est incrémenté? –

+2

Ne pas jeter d'objets jetables, utiliser une session pour plusieurs transactions et utiliser une instruction de mise à jour manuscrite pour quelque chose que nhibernate doit gérer manuellement sont des causes d'alarme. – Paco

+0

@Paco, c'est un cas de test très simplifié que nous avons concocté juste pour tracer ce problème. nous mettons toujours nos produits jetables à disposition, etc. merci pour nitpicking, bien que ... :) – Shachar

Répondre

4

Si la colonne version est de type timestamp ou rowversion, elle est mise à jour à l'aide d'un compteur global. Ce compteur global est incrémenté chaque fois qu'une ligne quelconque d'une table contenant une colonne rowversion est insérée ou mise à jour. Ce n'est donc pas un sujet de préoccupation. Cela reflète simplement d'autres activités dans la base de données.

0

Read here plus sur rowvwersion (timestamp) dans le serveur SQL. Choses à savoir:

  • N'a rien à voir avec le temps; autre que le plus grand nombre a été généré plus tard.
  • Le compteur est global-base de données.
  • Il est auto-changé, vous ne le changez pas.
  • Peut être utilisé dans les requêtes pour détecter les changements de données, lors de la synchronisation, etc ..
0

Puisque vous construisez vos propres requêtes, vous pouvez incrémenter atomiquement une colonne dans une instruction UPDATE:

UPDATE Account SET Balance = Balance + @amount WHERE UserId = @uid 

Cela permettra également d'économiser les frais généraux de création et de validation d'une transaction distincte.

Questions connexes