2 votes

Encapsulation des transactions d'écriture multi-processus

Je dispose d'un scénario de base de données (j'utilise Oracle) dans lequel plusieurs processus insèrent des données dans une table et un seul processus effectue une sélection. La table est essentiellement utilisée comme un espace de stockage intermédiaire, auquel plusieurs processus (appelés les rédacteurs ci-dessous) écrivent des événements de journal, et à partir duquel un seul processus (appelé le lecteur ci-dessous) lit les événements pour un traitement ultérieur. Le lecteur doit lire tous les événements insérés dans la table.

Actuellement, cela se fait en attribuant à chaque enregistrement inséré un identifiant d'une séquence ascendante. Le lecteur sélectionne périodiquement un bloc d'entrées de la table où l'identifiant est supérieur à l'identifiant le plus élevé du bloc précédemment lu. Par exemple :

SELECT
  *
FROM
  TRANSACTION_LOG
WHERE
  id > (
    SELECT
      last_id
    FROM
      READER_STATUS
   );

Le problème avec cette approche est que, comme les rédacteurs opèrent simultanément, les lignes ne sont pas toujours insérées dans l'ordre selon leur identifiant attribué, même si ces identifiants sont attribués dans l'ordre séquentiel ascendant. En d'autres termes, une ligne avec l'identifiant 100 est parfois écrite après un enregistrement avec l'identifiant 110, car le processus d'écriture de la ligne avec l'identifiant 110 a commencé après les processus écrivant l'enregistrement d'identifiant 100, mais a été validé en premier. Cela peut entraîner le fait que le lecteur manque la ligne avec l'identifiant 100 s'il a déjà lu la ligne avec l'identifiant 110.

Forcer les rédacteurs à verrouiller exclusivement la table résoudrait le problème car cela les obligerait à insérer séquentiellement et également à attendre pour tout engagement en attente pour le lecteur. Cependant, cela ne serait probablement pas très rapide.

Je pense que cela suffirait pour le lecteur d'attendre pour tout engagement en attente des processus rédacteurs. Autrement dit, les rédacteurs peuvent continuer à fonctionner simultanément tant que le lecteur ne lit pas tant que tous les rédacteurs n'ont pas terminé.

Ma question est la suivante :
Comment puis-je indiquer à mon processus lecteur d'attendre pour tout engagement en attente de mes processus rédacteurs ? Toutes suggestions alternatives à ce problème sont également les bienvenues.

1voto

AJ. Points 4916

Problème intéressant. Il semble que vous êtes en train de construire une belle solution.
J'espère pouvoir aider.

Quelques suggestions...

Statut de l'écrivain

Vous pourriez créer une table, WRITER_STATUS, qui possède un champ last_id : Chaque écrivain met à jour cette table avant d'écrire avec l'ID sur lequel il va écrire dans le journal, mais seulement si son ID est supérieur à la valeur actuelle de last_id.

Le lecteur vérifie également cette table et sait maintenant si des écrivains n'ont pas encore écrit.

Journal du lecteur

Cela pourrait être plus efficace.
Après que le lecteur ait fait une lecture, il vérifie s'il y a des trous dans les enregistrements qu'il a récupérés.
Il enregistre ensuite tous les ID manquants dans une table MISSING_IDS et pour sa prochaine lecture, il fait quelque chose comme

SELECT *
FROM   TRANSACTION_LOG
WHERE  id > (SELECT last_id
             FROM   READER_STATUS)
OR     id IN ( SELECT id from MISSING_IDS )

1voto

csgero Points 2061

Vous voudrez peut-être placer un verrou exclusif sur la table dans le processus de lecture. Cela attendra que tous les écrivains aient terminé et libéré leurs verrous de ligne, vous pouvez donc être sûr qu'il n'y a pas de transactions d'écriture en attente.

1voto

Bill Karwin Points 204877

Je ne ferais aucun verrouillage, cela peut interférer avec la concurrence et le débit.

Vous n'avez pas besoin non plus de la table Reader_Status, si vous gardez une trace des lignes de journal que vous avez traitées une par une.

Voici ce que je ferais : ajoutez une nouvelle colonne à votre table de journal. Appelez-la "traité" par exemple. Faites-en un booléen, par défaut à faux (ou un petit entier, par défaut à 0, ou autre). Les Writers utilisent la valeur par défaut lors de leur insertion.

Lorsque le Reader interroge pour le prochain bloc d'enregistrements à traiter, il interroge les lignes où le traité est faux et la valeur de l'identifiant est basse.

SELECT * FROM Transaction_Log
WHERE traité = 0
ORDER BY id
LIMIT 10;

En les traitant, le Reader utilise UPDATE pour changer le traité de faux à vrai. Ainsi, la prochaine fois que le Reader interroge pour un bloc d'enregistrements, il est sûr de ne pas obtenir des lignes qu'il a déjà traitées.

UPDATE Transaction_Log
SET traité = 1
WHERE id = ?;  -- faites ceci pour chaque ligne traitée

Cette mise à jour ne devrait pas entrer en conflit avec les opérations d'INSERT effectuées par les Writers.

Si des lignes sont validées hors séquence par d'autres Writers, le Reader les verra la prochaine fois qu'il interroge, s'il les traite toujours dans l'ordre de la colonne id de la plus basse à la plus haute valeur.

0voto

Constantin Points 12185

Comme vous connaissez last_id traité par Reader, vous pouvez demander l'élément de travail suivant de cette manière :

select * from Transaction_log where id = (
  select last_id + 1 /* ou tout autre incrément que votre séquenceur a */
    from Reader_status)

0voto

Rejeev Divakaran Points 1162

Je suis d'accord avec la solution de AJ ( lien ). De plus, les suggestions suivantes peuvent aider à réduire le nombre de trous.

1) Utilisez Séquence Oracle pour créer l'identifiant et utilisez auto-incrément comme suit

INSERT INTO transaction_table VALUES(id__seq.nextval, );

2) Utilisez autoCommit(true) pour que l'insertion soit validée immédiatement.

Ces deux étapes réduiront considérablement le nombre de trous. Il existe toujours une possibilité que certaines insertions aient été lancées en premier mais validées plus tard et qu'une opération de lecture se soit produite entre-temps.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X