Puis-je compter sur la lecture des valeurs d'identité SQL Server dans l'ordre?


24

TL; DR: La question ci-dessous se résume à: lors de l'insertion d'une ligne, existe-t-il une fenêtre d'opportunité entre la génération d'une nouvelle Identityvaleur et le verrouillage de la clé de ligne correspondante dans l'index clusterisé, où un observateur externe pourrait voir un plus récent Identity valeur insérée par une transaction simultanée? (Dans SQL Server.)

Version détaillée

J'ai une table SQL Server avec une Identitycolonne appelée CheckpointSequence, qui est la clé de l'index cluster de la table (qui possède également un certain nombre d'index non cluster supplémentaires). Les lignes sont insérées dans la table par plusieurs processus et threads simultanés (au niveau de l'isolement READ COMMITTEDet sans IDENTITY_INSERT). En même temps, il existe des processus qui lisent périodiquement des lignes de l'index cluster, ordonnées par cette CheckpointSequencecolonne (également au niveau d'isolement READ COMMITTED, l' READ COMMITTED SNAPSHOToption étant désactivée).

Je compte actuellement sur le fait que les processus de lecture ne peuvent jamais "sauter" un point de contrôle. Ma question est: puis-je compter sur cette propriété? Et sinon, que pourrais-je faire pour que ce soit vrai?

Exemple: lorsque des lignes avec les valeurs d'identité 1, 2, 3, 4 et 5 sont insérées, le lecteur ne doit pas voir la ligne avec la valeur 5 avant de voir celle avec la valeur 4. Les tests montrent que la requête, qui contient une ORDER BY CheckpointSequenceclause ( et une WHERE CheckpointSequence > -1clause), bloque de manière fiable chaque fois que la ligne 4 doit être lue, mais pas encore validée, même si la ligne 5 a déjà été validée.

Je crois qu'au moins en théorie, il peut y avoir une condition de race ici qui pourrait faire rompre cette hypothèse. Malheureusement, la documentation sur Identityne dit pas grand-chose sur le Identityfonctionnement dans le contexte de plusieurs transactions simultanées, elle dit seulement "Chaque nouvelle valeur est générée en fonction de la valeur de départ et de l'incrément". et "Chaque nouvelle valeur pour une transaction particulière est différente des autres transactions simultanées sur la table." ( MSDN )

Mon raisonnement est, cela doit fonctionner en quelque sorte comme ceci:

  1. Une transaction est lancée (explicitement ou implicitement).
  2. Une valeur d'identité (X) est générée.
  3. Le verrou de ligne correspondant est pris sur l'index clusterisé en fonction de la valeur d'identité (sauf si l'escalade de verrous entre en jeu, auquel cas la table entière est verrouillée).
  4. La ligne est insérée.
  5. La transaction est validée (peut-être beaucoup de temps plus tard), donc le verrou est à nouveau retiré.

Je pense qu'entre les étapes 2 et 3, il y a une toute petite fenêtre où

  • une session simultanée pourrait générer la prochaine valeur d'identité (X + 1) et exécuter toutes les étapes restantes,
  • permettant ainsi à un lecteur venant exactement à ce moment de lire la valeur X + 1, sans la valeur de X.

Bien sûr, la probabilité de cela semble extrêmement faible; mais encore - cela pourrait arriver. Ou est-ce possible?

(Si vous êtes intéressé par le contexte: il s'agit de l'implémentation du moteur de persistance SQL de NEventStore. NEventStore implémente un magasin d'événements à ajouter uniquement où chaque événement obtient un nouveau numéro de séquence de point de contrôle croissant. Les clients lisent les événements du magasin d'événements classés par point de contrôle. afin d'effectuer des calculs de toutes sortes. Une fois qu'un événement avec le point de contrôle X a été traité, les clients ne prennent en compte que les événements "plus récents", c'est-à-dire les événements avec le point de contrôle X + 1 et plus. Par conséquent, il est essentiel que les événements ne puissent jamais être ignorés, car ils ne seraient plus jamais pris en compte. J'essaie actuellement de déterminer si l' Identityimplémentation de point de contrôle basée sur cette condition répond à cette exigence. Ce sont les instructions SQL exactes utilisées : schéma , requête du rédacteur ,Requête du lecteur .)

Si j'ai raison et que la situation décrite ci-dessus pourrait survenir, je ne vois que deux options pour y faire face, qui ne sont pas satisfaisantes:

  • Lorsque vous voyez une valeur de séquence de point de contrôle X + 1 avant d'avoir vu X, ignorez X + 1 et réessayez plus tard. Cependant, parce que cela Identitypeut bien sûr produire des lacunes (par exemple, lorsque la transaction est annulée), X pourrait ne jamais arriver.
  • Donc, même approche, mais acceptez l'écart après n millisecondes. Cependant, quelle valeur de n dois-je supposer?

De meilleures idées?


Avez-vous essayé d'utiliser Sequence au lieu de l'identité? Avec l'identité, je ne pense pas que vous puissiez prédire de manière fiable quel insert obtiendra une valeur d'identité particulière, mais cela ne devrait pas être un problème lors de l'utilisation d'une séquence. Bien sûr, cela change la façon dont vous faites les choses maintenant.
Antoine Hernandez

@SoleDBAGuy Une séquence ne rendrait-elle pas encore plus probable la condition de concurrence décrite ci-dessus? Je produis une nouvelle valeur de séquence X (en remplaçant l'étape 2 ci-dessus), puis j'insère une ligne (étapes 3 et 4). Entre 2 et 3, il est possible que quelqu'un d'autre produise la prochaine valeur de séquence X + 1, la valide, et un lecteur lit cette valeur X + 1 avant même que j'arrive à insérer ma ligne avec la valeur de séquence X.
Fabian Schmied

Réponses:


26

Lors de l'insertion d'une ligne, existe-t-il une fenêtre d'opportunité entre la génération d'une nouvelle valeur d'identité et le verrouillage de la clé de ligne correspondante dans l'index clusterisé, où un observateur externe pourrait voir une nouvelle valeur d'identité insérée par une transaction simultanée?

Oui.

L' allocation de valeurs d'identité est indépendante de la transaction utilisateur contenante . C'est l'une des raisons pour lesquelles les valeurs d'identité sont consommées même si la transaction est annulée. L'opération d'incrémentation elle-même est protégée par un verrou pour éviter la corruption, mais c'est l'étendue des protections.

Dans les circonstances spécifiques de votre implémentation, l'attribution d'identité (un appel à CMEDSeqGen::GenerateNewValue) est effectuée avant même que la transaction utilisateur pour l'insertion ne soit rendue active (et donc avant que les verrous ne soient pris).

En exécutant deux insertions simultanément avec un débogueur attaché pour me permettre de geler un thread juste après que la valeur d'identité est incrémentée et allouée, j'ai pu reproduire un scénario où:

  1. La session 1 acquiert une valeur d'identité (3)
  2. La session 2 acquiert une valeur d'identité (4)
  3. La session 2 effectue son insertion et valide (donc la ligne 4 est entièrement visible)
  4. La session 1 effectue son insertion et valide (ligne 3)

Après l'étape 3, une requête utilisant row_number sous un verrouillage de lecture validé a renvoyé ce qui suit:

Capture d'écran

Dans votre implémentation, cela entraînerait le non-respect de l'ID de point de contrôle 3.

La fenêtre de l'opportunité est relativement petite, mais elle existe. Pour donner un scénario plus réaliste que d'avoir un débogueur attaché: Un thread de requête en cours d'exécution peut générer le planificateur après l'étape 1 ci-dessus. Cela permet à un deuxième thread d'allouer une valeur d'identité, d'insérer et de valider, avant que le thread d'origine ne reprenne pour effectuer son insertion.

Pour plus de clarté, il n'y a pas de verrous ou d'autres objets de synchronisation protégeant la valeur d'identité après son allocation et avant son utilisation. Par exemple, après l'étape 1 ci-dessus, une transaction simultanée peut voir la nouvelle valeur d'identité à l'aide des fonctions T-SQL comme IDENT_CURRENTavant que la ligne existe dans la table (même non validée).

Fondamentalement, il n'y a pas plus de garanties concernant les valeurs d'identité que celles documentées :

  • Chaque nouvelle valeur est générée en fonction de la valeur de départ et de l'incrément actuels.
  • Chaque nouvelle valeur pour une transaction particulière est différente des autres transactions simultanées sur la table.

C'est vraiment ça.

Si un traitement FIFO transactionnel strict est requis, vous n'avez probablement pas d'autre choix que de sérialiser manuellement. Si l'application a moins d'exigences, vous avez plus d'options. La question n'est pas claire à 100% à cet égard. Néanmoins, vous pouvez trouver des informations utiles dans l'article de Remus Rusanu sur l' utilisation des tables comme files d'attente .


7

Comme Paul White a répondu tout à fait correctement, il existe une possibilité pour les lignes d'identité temporairement "ignorées". Voici juste un petit morceau de code pour reproduire ce cas pour vous-même.

Créez une base de données et une table de test:

create database IdentityTest
go
use IdentityTest
go
create table dbo.IdentityTest (ID int identity, c1 char(10))
create clustered index CI_dbo_IdentityTest_ID on dbo.IdentityTest(ID)

Effectuez des insertions et des sélections simultanées sur cette table dans un programme de console C #:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Threading;

namespace IdentityTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var insertThreads = new List<Thread>();
            var selectThreads = new List<Thread>();

            //start threads for infinite inserts
            for (var i = 0; i < 100; i++)
            {
                insertThreads.Add(new Thread(InfiniteInsert));
                insertThreads[i].Start();
            }

            //start threads for infinite selects
            for (var i = 0; i < 10; i++)
            {
                selectThreads.Add(new Thread(InfiniteSelectAndCheck));
                selectThreads[i].Start();
            }
        }

        private static void InfiniteSelectAndCheck()
        {
            //infinite loop
            while (true)
            {
                //read top 2 IDs
                var cmd = new SqlCommand("select top(2) ID from dbo.IdentityTest order by ID desc")
                {
                    Connection = new SqlConnection("Server=localhost;Database=IdentityTest;Integrated Security=SSPI;Application Name=IdentityTest")
                };

                try
                {
                    cmd.Connection.Open();
                    var dr = cmd.ExecuteReader();

                    //read first row
                    dr.Read();
                    var row1 = int.Parse(dr["ID"].ToString());

                    //read second row
                    dr.Read();
                    var row2 = int.Parse(dr["ID"].ToString());

                    //write line if row1 and row are not consecutive
                    if (row1 - 1 != row2)
                    {
                        Console.WriteLine("row1=" + row1 + ", row2=" + row2);
                    }
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }
        }

        private static void InfiniteInsert()
        {
            //infinite loop
            while (true)
            {
                var cmd = new SqlCommand("insert into dbo.IdentityTest (c1) values('a')")
                {
                    Connection = new SqlConnection("Server=localhost;Database=IdentityTest;Integrated Security=SSPI;Application Name=IdentityTest")
                };

                try
                {
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
                finally
                {
                    cmd.Connection.Close();
                }
            }
        }
    }
}

Cette console imprime une ligne pour chaque cas lorsqu'un des fils de lecture "manque" une entrée.


1
Beau code mais vous ne vérifiez que les identifiants consécutifs ( "// écrire la ligne si row1 et row ne sont pas consécutifs" ). Il peut y avoir des lacunes produites que votre code imprimera. Cela ne signifie pas que ces lacunes seront comblées plus tard.
ypercubeᵀᴹ

1
Étant donné que le code ne déclenche pas un scénario où IDENTITYdes écarts seraient générés (comme l'annulation d'une transaction), les lignes imprimées affichent en effet des valeurs "ignorées" (ou du moins, elles l'ont fait lorsque je l'ai exécuté et vérifié sur ma machine). Très bel échantillon de repro!
Fabian Schmied

5

Il est préférable de ne pas s'attendre à ce que les identités soient consécutives car il existe de nombreux scénarios qui peuvent laisser des lacunes. Il est préférable de considérer l'identité comme un numéro abstrait et de ne lui attribuer aucune signification commerciale.

Fondamentalement, des lacunes peuvent se produire si vous annulez des opérations INSERT (ou supprimez explicitement des lignes) et des doublons peuvent se produire si vous définissez la propriété de table IDENTITY_INSERT sur ON.

Des lacunes peuvent survenir lorsque:

  1. Les enregistrements sont supprimés.
  2. Une erreur s'est produite lors de la tentative d'insertion d'un nouvel enregistrement (annulé)
  3. Une mise à jour / insertion avec une valeur explicite (option identity_insert).
  4. La valeur incrémentielle est supérieure à 1.
  5. Une transaction est annulée.

La propriété d'identité sur une colonne n'a jamais garanti:

• Unicité

• Valeurs consécutives dans une transaction. Si les valeurs doivent être consécutives, la transaction doit utiliser un verrou exclusif sur la table ou utiliser le niveau d'isolement SERIALIZABLE.

• Valeurs consécutives après redémarrage du serveur.

• Réutilisation des valeurs.

Si vous ne pouvez pas utiliser de valeurs d'identité pour cette raison, créez une table distincte contenant une valeur actuelle et gérez l'accès à la table et à l'affectation des numéros avec votre application. Cela peut avoir un impact sur les performances.

https://msdn.microsoft.com/en-us/library/ms186775(v=sql.105).aspx
https://msdn.microsoft.com/en-us/library/ms186775(v=sql.110) .aspx


Je pense que les lacunes ne sont pas mon principal problème - mon principal problème est la visibilité croissante des valeurs. (C'est-à-dire que la valeur d'identité 7 ne doit pas être observable pour une requête ordonnée par cette valeur avant que la valeur d'identité 6 ne soit.)
Fabian Schmied

1
J'ai vu des valeurs d'identité s'engager comme: 1, 2, 5, 3, 4.
stacylaray

Bien sûr, cela est facilement reproductible, par exemple, en utilisant le scénario de la réponse de Lennart. La question qui me pose problème est de savoir si je peux observer cet ordre de validation lors de l'utilisation d'une requête avec une ORDER BY CheckpointSequenceclause (qui se trouve être l'ordre de l'index clusterisé). Je pense que cela se résume à la question de savoir si la génération d'une valeur d'identité est liée de toute façon aux verrous pris par l'instruction INSERT, ou s'il s'agit simplement de deux actions indépendantes effectuées par SQL Server l'une après l'autre.
Fabian Schmied

1
Quelle est la requête? Si vous utilisez la lecture validée, dans votre exemple, l'ordre par afficherait 1, 2, 3, 5 car ils ont été validés et 4 non, c'est-à-dire une lecture sale. En outre, votre explication de NEventStore déclare "Par conséquent, il est essentiel que les événements ne puissent jamais être ignorés, car ils ne seront plus jamais pris en compte."
stacylaray

La requête est donnée ci-dessus ( gist.github.com/fschmied/47f716c32cb64b852f90 ) - elle est paginée, mais se résume à un simple SELECT ... FROM Commits WHERE CheckpointSequence > ... ORDER BY CheckpointSequence. Je ne pense pas que cette requête se lirait au-delà de la ligne verrouillée 4, ou non? (Dans mes expériences, il bloque lorsque la requête essaie d'acquérir le verrou KEY pour la ligne 4.)
Fabian Schmied

1

Je soupçonne que cela peut occasionnellement entraîner des problèmes, des problèmes qui s'aggravent lorsque le serveur est sous forte charge. Considérons deux transactions:

  1. T1: insérez dans T ... - disons 5 insérez
  2. T2: insérez dans T ... - disons 6 insérez
  3. T2: validation
  4. Le lecteur voit 6 mais pas 5
  5. T1: commit

Dans le scénario ci-dessus, votre LAST_READ_ID sera 6, donc 5 ne sera jamais lu.


Mes tests semblent indiquer que ce scénario n'est pas un problème car Reader (étape 4) se bloquera (jusqu'à ce que T1 ait libéré ses verrous) lorsqu'il essaie de lire la ligne avec la valeur 5. Suis-je en train de manquer quelque chose?
Fabian Schmied

Vous avez peut-être raison, je ne connais pas très bien le mécanisme de verrouillage de SQL Server (d'où je soupçonne dans ma réponse).
Lennart

Dépend du niveau d'isolement du lecteur. Je peux voir les deux, bloquer ou voir seulement 6.
Michael Green

0

Exécution de ce script:

BEGIN TRAN;
INSERT INTO dbo.Example DEFAULT VALUES;
COMMIT;

Vous trouverez ci-dessous les verrous acquis et libérés tels qu'ils ont été capturés par une session d'événement étendu:

name            timestamp                   associated_object_id    mode    object_id   resource_type   session_id  resource_description
lock_acquired   2016-03-29 06:37:28.9968693 1585440722              IX      1585440722  OBJECT          51          
lock_acquired   2016-03-29 06:37:28.9969268 7205759890195415040     IX      0           PAGE            51          1:1235
lock_acquired   2016-03-29 06:37:28.9969306 7205759890195415040     RI_NL   0           KEY             51          (ffffffffffff)
lock_acquired   2016-03-29 06:37:28.9969330 7205759890195415040     X       0           KEY             51          (29cf3326f583)
lock_released   2016-03-29 06:37:28.9969579 7205759890195415040     X       0           KEY             51          (29cf3326f583)
lock_released   2016-03-29 06:37:28.9969598 7205759890195415040     IX      0           PAGE            51          1:1235
lock_released   2016-03-29 06:37:28.9969607 1585440722              IX      1585440722  OBJECT          51      

Notez le verrou RI_N KEY acquis immédiatement avant le verrou X pour la nouvelle ligne en cours de création. Ce verrou de plage de courte durée empêchera un insert simultané d'acquérir un autre verrou RI_N KEY car les verrous RI_N sont incompatibles. La fenêtre que vous avez mentionnée entre les étapes 2 et 3 n'est pas un problème car le verrouillage de plage est acquis avant le verrouillage de ligne sur la clé nouvellement générée.

Tant que vous SELECT...ORDER BYcommencez l'analyse avant les lignes nouvellement insérées souhaitées, je m'attendrais à ce que vous souhaitiez le READ COMMITTEDniveau d'isolement par défaut tant que l' READ_COMMITTED_SNAPSHOToption de base de données est désactivée.


1
Selon technet.microsoft.com/en-us/library/… , deux verrous RangeI_Nsont compatibles , c'est-à-dire qu'ils ne se bloquent pas (le verrou est principalement là pour bloquer sur un lecteur sérialisable existant).
Fabian Schmied

@FabianSchmied, intéressant. Cette rubrique est en conflit avec la matrice de compatibilité des verrous dans technet.microsoft.com/en-us/library/ms186396(v=sql.105).aspx , qui montre que les verrous ne sont pas compatibles. L'exemple d'insertion dans le lien que vous avez mentionné indique le même comportement que celui indiqué dans la trace dans ma réponse (verrouillage de plage d'insertion de courte durée pour tester la plage avant le verrouillage de clé exclusif).
Dan Guzman

1
En fait, la matrice dit "N" pour "pas de conflit" (pas pour "non compatible") :)
Fabian Schmied

0

D'après ma compréhension de SQL Server, le comportement par défaut est que la deuxième requête n'affiche aucun résultat tant que la première requête n'a pas été validée. Si la première requête effectue un ROLLBACK au lieu d'un COMMIT, alors vous aurez un ID manquant dans votre colonne.

Configuration de base

Table de base de données

J'ai créé une table de base de données avec la structure suivante:

CREATE TABLE identity_rc_test (
    ID4VALUE INT IDENTITY (1,1), 
    TEXTVALUE NVARCHAR(20),
    CONSTRAINT PK_ID4_VALUE_CLUSTERED 
        PRIMARY KEY CLUSTERED (ID4VALUE, TEXTVALUE)
)

Niveau d'isolement de la base de données

J'ai vérifié le niveau d'isolement de ma base de données avec la déclaration suivante:

SELECT snapshot_isolation_state, 
       snapshot_isolation_state_desc, 
       is_read_committed_snapshot_on
FROM sys.databases WHERE NAME = 'mydatabase'

Qui a renvoyé le résultat suivant pour ma base de données:

snapshot_isolation_state    snapshot_isolation_state_desc   is_read_committed_snapshot_on
0                           OFF                             0

(Il s'agit du paramètre par défaut pour une base de données dans SQL Server 2012)

Scripts de test

Les scripts suivants ont été exécutés à l'aide des paramètres client SQL Server SSMS standard et des paramètres SQL Server standard.

Paramètres de connexion client

Le client a été configuré pour utiliser le niveau d'isolation de transaction READ COMMITTEDconformément aux options de requête dans SSMS.

Requête 1

La requête suivante a été exécutée dans une fenêtre de requête avec le SPID 57

SELECT * FROM dbo.identity_rc_test
BEGIN TRANSACTION [FIRST_QUERY]
INSERT INTO dbo.identity_rc_test (TEXTVALUE) VALUES ('Nine')
/* Commit is commented out to prevent the INSERT from being commited
--COMMIT TRANSACTION [FIRST_QUERY]
--ROLLBACK TRANSACTION [FIRST_QUERY]
*/

Requête 2

La requête suivante a été exécutée dans une fenêtre de requête avec le SPID 58

BEGIN TRANSACTION [SECOND_QUERY]
INSERT INTO dbo.identity_rc_test (TEXTVALUE) VALUES ('Ten')
COMMIT TRANSACTION [SECOND_QUERY]
SELECT * FROM dbo.identity_rc_test

La requête n'est pas terminée et attend la libération du verrou eXclusive sur une PAGE.

Script pour déterminer le verrouillage

Ce script affiche le verrouillage se produisant sur les objets de base de données pour les deux transactions:

SELECT request_session_id, resource_type,
       resource_description, 
       resource_associated_entity_id,
       request_mode, request_status
FROM sys.dm_tran_locks
WHERE request_session_id IN (57, 58)

Et voici les résultats:

58  DATABASE                    0                   S   GRANT
57  DATABASE                    0                   S   GRANT
58  PAGE            1:79        72057594040549300   IS  GRANT
57  PAGE            1:79        72057594040549300   IX  GRANT
57  KEY         (a0aba7857f1b)  72057594040549300   X   GRANT
58  KEY         (a0aba7857f1b)  72057594040549300   S   WAIT
58  OBJECT                      245575913           IS  GRANT
57  OBJECT                      245575913           IX  GRANT

Les résultats montrent que la fenêtre de requête un (SPID 57) a un verrou partagé (S) sur la base de données un verrou eXlusive (IX) prévu sur l'objet, un verrou eXlusive (IX) prévu sur la page à insérer et un eXclusive verrou (X) sur la CLÉ insérée, mais pas encore validé.

En raison des données non validées, la deuxième requête (SPID 58) a un verrou partagé (S) au niveau de la BASE DE DONNÉES, un verrou partagé (IS) prévu sur l'objet, un verrou partagé (IS) prévu sur la page un partagé (S ) verrouiller la clé avec un statut de demande WAIT.

Sommaire

La requête dans la première fenêtre de requête s'exécute sans validation. Étant donné que la deuxième requête ne peut que des READ COMMITTEDdonnées, elle attend soit le délai d'expiration, soit la validation de la transaction dans la première requête.

Cela vient de ma compréhension du comportement par défaut de Microsoft SQL Server.

Vous devez observer que l'ID est en effet en séquence pour les lectures suivantes par les instructions SELECT si la première instruction COMMITs.

Si la première instruction fait un ROLLBACK, vous trouverez alors un ID manquant dans la séquence, mais toujours avec l'ID dans l'ordre croissant (à condition que vous ayez créé l'INDEX avec l'option par défaut ou ASC dans la colonne ID).

Mise à jour:

(Franchement) Oui, vous pouvez compter sur le bon fonctionnement de la colonne d'identité jusqu'à ce que vous rencontriez un problème. Il n'y a qu'un seul HOTFIX concernant SQL Server 2000 et la colonne d'identité sur le site Web de Microsoft.

Si vous ne pouviez pas vous fier à la mise à jour correcte de la colonne d'identité, je pense qu'il y aurait plus de correctifs ou de correctifs sur le site Web de Microsoft.

Si vous avez un contrat de support Microsoft, vous pouvez toujours ouvrir un dossier de conseil et demander des informations supplémentaires.


1
Merci pour l'analyse, mais ma question est de savoir s'il existe un intervalle de temps entre la génération de la Identityvaleur suivante et l'acquisition du verrou KEY sur la ligne (où les lectures / écritures simultanées pourraient tomber). Je ne pense pas que cela soit prouvé impossible par vos observations, car on ne peut pas arrêter l'exécution des requêtes et analyser les verrous pendant cette fenêtre de temps ultra-courte.
Fabian Schmied

Non, vous ne pouvez pas arrêter les déclarations, mais mon observation (lente) est ce qui se passe sur une base rapide / normale. Dès qu'un SPID acquiert un verrou pour insérer des données, l'autre ne pourra pas acquérir le même verrou. L'instruction plus rapide aura l'avantage d'avoir déjà acquis le verrou et l'ID en séquence. L'instruction suivante recevra l'ID suivant une fois le verrou libéré.
John aka hot2use

1
Normalement, vos observations correspondent aux miennes (et aussi à mes attentes) - c'est bon à savoir. Je me demande s'il y a des situations exceptionnelles où elles ne tiendront pas.
Fabian Schmied
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.