Question

J'ai une table simple avec une clé primaire. La plupart des opérations de lecture par une ligne d'extraction de la valeur exacte de la clé.

Les données dans chaque rangée maintient une certaine relation avec des rangées avant et après dans l'ordre des clés. Alors, quand je insérer une nouvelle ligne que je dois lire les 2 lignes entre lesquelles il va entrer, faire un peu de calcul, puis à insérer.

Le souci, clairement, est que dans le même temps une autre connexion peut ajouter une ligne avec une valeur clé dans le même intervalle. Je suis couvert si elle est exactement la même valeur de la clé que la deuxième insert échouerait, mais si la valeur de clé est différente, mais dans le même intervalle, la relation peut être rompu.

La solution semble être de verrouiller toute la table pour l'écriture quand je décide d'ajouter une nouvelle ligne, ou (si possible, que je doute) pour verrouiller un intervalle de valeurs clés. Pourtant, je préfère que les transactions en lecture seule ne seraient pas bloqués à ce moment-là.

J'utilise ODBC avec libodbc ++ wrapper C ++ dans le programme client et IBM DB2 version gratuite (bien que le choix DB peut encore changer). C'est ce que je pensais de faire:

  • lancer la connexion dans la validation automatique et le mode d'isolation par défaut
  • quand le besoin d'ajouter une nouvelle ligne, jeu d'auto-validation en mode faux et isolement sérialisé
  • lire les lignes avant et après la nouvelle valeur de clé
  • calculer et à insérer la nouvelle ligne
  • commit
  • retour vers la validation automatique et le mode d'isolation par défaut

Est-ce que cela le travail? Est-ce que d'autres transactions sont autorisés à lire en même temps? Y at-il d'autres / de meilleures façons de le faire?

BTW, je ne vois pas dans le libodbc ++ i / f un moyen de spécifier une transaction en lecture seule. Est-il possible dans odbc?

EDIT: Merci pour les réponses très utiles, j'ai eu du mal d'une sélection.

Était-ce utile?

La solution

Si votre base de données est en mode SERIALIZABLE, vous n'aurez pas de problème du tout. Étant donné une clé K, pour obtenir les clés précédentes et suivantes vous devez exécuter les requêtes suivantes:

select key from keys where key > K order by key limit 1;      # M?
select key from keys where key < K order by key desc limit 1; # I?

Les travaux ci-dessus dans MySQL. Cette requête équivalente fonctionne dans DB2 (des commentaires):

select key from keys where key = (select min(key) from keys where key > K);
select key from keys where key = (select max(key) from keys where key < K);

Les premiers ensembles de la requête jusqu'à un verrouillage de gamme qui empêche d'autres opérations de l'insertion d'une clé supérieure à K et inférieur ou égal à M.

Les seconds ensembles de la requête jusqu'à un verrouillage de gamme qui empêche d'autres opérations à partir de l'insertion d'une clé inférieure à K et supérieur ou égal à I.

L'index unique sur la clé primaire K empêche d'être inséré deux fois. Donc, vous êtes complètement couvert.

est ce que les transactions sont sur le point; de sorte que vous pouvez écrire votre code comme si la base de données entière est verrouillée.

Note: Ceci nécessite une base de données qui prend en charge une vraie sérialisation. Heureusement, le fait DB2. d'autres SGBD que le soutien véritable sérialisation: SQLServer et MySQL / InnoDB. SGBD qui ne le font pas: Oracle, PostgreSQL

Autres conseils

Si votre base de données et un moteur de stockage permettent que, vous devez émettre SELECT FOR UPDATE pour les deux lignes que vous essayez d'insérer entre.

en conflit avec une SELECT FOR UPDATE concurrente.

L'inconvénient est que le verrouillage de lignes 10 et 12 (pour insérer 11) empêchera également sélectionner 8 et 10 (pour insérer 9).

InnoDB dans MySQL peut également placer un verrou de next-key sur l'index, qui est verrouillage de l'enregistrement d'index et l'écart entre l'enregistrement suivant.

Dans ce cas, vous auriez seulement besoin d'émettre un SELECT FOR UPDATE sur la première ligne et donc insérer simultanément une rangée avant.

Cependant, cela nécessite de forcer l'index et fournir une condition de range sur l'indice qui peut ou peut ne pas être possible en fonction de votre requête.

Votre approche générale est correcte. Mais vous devez utiliser une instruction SELECT qui couvre les deux lignes et toutes les lignes possibles entre les deux. Par exemple:

SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10

Dans les systèmes de base de données avec verrouillage pessimiste et le niveau d'isolement transaction sérialisable, cette instruction SELECT devrait empêcher de nouvelles lignes à insérer qui changerait le résultat de SELECT.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top