2009-03-20 6 views
0

J'ai une énorme liste d'URL dans une table MySQL InnoDB, et des processus de travail qui interrogent MySQL pour un ensemble d'URL à traiter. Les URL doivent immédiatement être marquées comme étant traitées, afin que les autres processus de travail ne gaspillent pas de ressources en commençant à traiter les mêmes.Bonne façon d'utiliser MySQL pour assigner des tâches aux processus de travail

Actuellement, je fais d'abord pour obtenir des URL:

SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100 

Puis dans le code I boucle à travers chacun naïvement ces urls à marquer comme étant traitée:

UPDATE urls SET task_assigned = NOW() WHERE url = ? COLLATE utf8_bin 

Je suis parfaitement conscient à quel point c'est stupide et inefficace. Plus important encore, il n'y a aucune garantie qu'un autre processus de travail n'essaierait pas d'obtenir une liste au milieu de mes UPDATEs. Quelle est la belle façon de faire ça? Devrais-je en faire une transaction, comment?

Répondre

2

Ce qui suit apparaît (d'un coup d'œil dans le manuel de MySQL 5) pour être disponible en MySQL; Je ne suis pas sûr que ce soit la meilleure approche, mais il est celui que je l'ai utilisé auparavant dans PostgreSQL:

BEGIN TRANSACTION; 
SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100 FOR UPDATE; 
UPDATE urls SET task_assigned = NOW() WHERE url IN [list of URLs] COLLATE utf8_bin; 
COMMIT; 

En fait, dans PostgreSQL je voudrais utiliser une seule instruction UPDATE avec la clause RENVOYER UPDATE prenant la place de le SELECT, mais c'est une extension spécifique à PostgreSQL. Un problème potentiel que je vois avec votre approche est dupliqué URLs: si l'URL http://www.example.com/ apparaît deux fois dans votre table, disons avec les ID 23 et 42, il sera retourné avec un de ces deux ID par le SELECT, mais la mise à jour affectera les deux rangées. Je ne sais pas si ce comportement a du sens dans votre demande; Je mettrais probablement une sorte de contrainte UNIQUE sur les URL afin que cela ne puisse pas arriver, et j'utiliserais alors une liste d'ID, pas d'URL, dans la clause IN (qui devrait être plus rapide).

+0

Merci. Cependant, pouvez-vous penser à une méthode purement SQL sans avoir à créer la [liste d'URL] séparées par des virgules dans le code? – Bemmu

+0

Eh bien, vous pouvez toujours simplement remplacer ce bit par une sous-requête (copier et coller l'instruction SELECT). Je ne sais pas si ça marcherait ... probablement mieux que la version du code, en fait. – kquinn

0

Peut-être devriez-vous d'abord sélectionner toutes les URL, puis utiliser les unités d'exécution pour les analyser de manière asynchrone?

+0

Actuellement, plusieurs ordinateurs traitent les URL, et j'utilise des requêtes HTTP pour transmettre les listes. – Bemmu