2010-03-20 2 views
-2

Lorsque plusieurs scripts PHP s'exécutent en parallèle, chacun effectuant une requête UPDATE sur le même enregistrement dans la même table de manière répétée, est-il possible qu'il y ait un 'lag time' avant que la table soit mise à jour avec chaque requête?MySql Délai d'attente de requête/blocage?

J'ai essentiellement 5-6 instances d'un script PHP fonctionnant en parallèle, ayant été lancé via cron. Chaque script récupère tous les enregistrements dans la table items, puis les parcourt et les traite.

Cependant, pour éviter de traiter le même élément plusieurs fois, je stocke l'identifiant du dernier élément en cours de traitement dans une table distincte. Donc, voici comment fonctionne mon code:

function getCurrentItem() 
{ 
    $sql = "SELECT currentItemId from settings"; 
    $result = $this->db->query($sql); 
    return $result->get('currentItemId'); 
} 

function setCurrentItem($id) 
{ 
    $sql = "UPDATE settings SET currentItemId='$id'"; 
    $this->db->query($sql); 
} 

$currentItem = $this->getCurrentItem(); 

$sql = "SELECT * FROM items WHERE status='pending' AND id > $currentItem'"; 
$result = $this->db->query($sql); 
$items = $result->getAll(); 

foreach ($items as $i) 
{ 
    //Check if $i has been processed by a different instance of the script, and if so, 
    //leave it untouched. 
    if ($this->getCurrentItem() > $i->id) 
    continue; 

    $this->setCurrentItem($i->id); 
    // Process the item here 
} 

Malgré toutes les précautions, la plupart des articles sont traités plusieurs fois. Ce qui me fait penser qu'il y a un certain décalage entre les requêtes de mise à jour qui sont exécutées par le script PHP et quand la base de données met à jour l'enregistrement.

Est-ce vrai? Et si oui, quel autre mécanisme devrais-je utiliser pour m'assurer que les scripts PHP obtiennent toujours seulement les derniers currentItemId même quand il y a plusieurs scripts fonctionnant en parallèle? Serait l'aide d'un fichier texte au lieu de l'aide db?

Répondre

1

Si cela est exécuté en parallèle, il y a peu de mesures pour éviter les conditions de course.

script1: 

getCurrentItem() yields Id 1234 
...context switch to script2, before script 1 gets to run its update statement. 

script2: 
getCurrentItem() yields Id 1234 

Et les deux processus de scripts Id 1234

Vous souhaitez mettre à jour et vérifier l'état de l'élément d'une opération tout ou rien, vous n'avez pas besoin de la table des paramètres, mais vous seriez faire quelque chose comme ceci (code pseudo):

SELECT * FROM items WHERE status='pending' AND id > $currentItem 

foreach($items as $i) { 
rows = update items set status='processing' where id = $i->id and status='pending'; 
    if(rows == 0) //someone beat us to it and is already processing the item 
    continue; 
    process item.. 
update items set status='done' where id = $i->id; 
} 
+0

Dans votre code, d'où '$ rows' est-il défini? C'est à côté d'une requête de mise à jour. Pouvez-vous éléborer? –

+0

Eh bien, je ne connais pas beaucoup PHP. L'idée est que l'émission d'un UPDATE retournera le non. des lignes qui ont été affectées. si c'est 0, cela signifie que 'where status = 'pending'' ne correspond pas parce que quelqu'un d'autre a changé le statut entre-temps. – nos

+0

En plus de vérifier les lignes affectées par la requête, y a-t-il une autre solution plus infaillible? Comme en utilisant des fichiers texte ..? –

1

Qu'est-ce que vous avez besoin est pour tout fil pour pouvoir:

  • trouver un élément en attente
  • enregistrement que cet élément est maintenant en cours d'élaboration (dans le tableau settings)

Et il a besoin de faire les deux de ceux en une seule fois, sans aucun autre thread interférer à mi-chemin à travers.

Je recommande de mettre tout le SQL dans une procédure stockée ; ce sera capable de faire fonctionner le tout en une seule transaction, ce qui le rendra à l'abri des discussions concurrentes.