tipos tables tabla pesimista manejar lock lectura ejemplo datos concurrencia bloqueo bloquear mysql locking web-crawler mysql-error-1093

tables - ¿Cómo bloqueo la lectura/escritura en las tablas MySQL para que pueda seleccionar y luego insertar sin que otros programas lean/escriban en la base de datos?



tipos de bloqueo base de datos (5)

Estoy ejecutando muchas instancias de un webcrawler en paralelo.

Cada rastreador selecciona un dominio de una tabla, inserta esa url y una hora de inicio en una tabla de registro y luego comienza a rastrear el dominio.

Otros rastreadores paralelos verifican la tabla de registro para ver qué dominios ya se están rastreando antes de seleccionar su propio dominio para rastrear.

Necesito evitar que otros rastreadores seleccionen un dominio que otro rastreador haya seleccionado pero que aún no tenga una entrada de registro. Mi mejor estimación de cómo hacer esto es bloquear la base de datos de todas las demás lecturas / escrituras mientras un rastreador selecciona un dominio e inserta una fila en la tabla de registro (dos consultas).

¿Cómo diablos se hace esto? Me temo que esto es terriblemente complejo y se basa en muchas otras cosas. Por favor ayúdame a comenzar.

Este código parece una buena solución (vea el error a continuación, sin embargo):

INSERT INTO crawlLog (companyId, timeStartCrawling) VALUES ( ( SELECT companies.id FROM companies LEFT OUTER JOIN crawlLog ON companies.id = crawlLog.companyId WHERE crawlLog.companyId IS NULL LIMIT 1 ), now() )

pero sigo recibiendo el siguiente error mysql:

You can''t specify target table ''crawlLog'' for update in FROM clause

¿Hay una manera de lograr lo mismo sin este problema? He intentado un par de maneras diferentes. Incluyendo esto:

INSERT INTO crawlLog (companyId, timeStartCrawling) VALUES ( ( SELECT id FROM companies WHERE id NOT IN (SELECT companyId FROM crawlLog) LIMIT 1 ), now() )


Bueno, las cerraduras de mesa son una forma de lidiar con eso; pero esto hace que las solicitudes paralelas sean imposibles. Si la tabla es InnoDB, podría forzar un bloqueo de fila en su lugar, utilizando SELECT ... FOR UPDATE dentro de una transacción.

BEGIN; SELECT ... FROM your_table WHERE domainname = ... FOR UPDATE # do whatever you have to do COMMIT;

Tenga en cuenta que necesitará un índice en el domainname de domainname (o la columna que use en la cláusula WHERE) para que esto funcione, pero esto tiene sentido en general y supongo que lo tendrá de todos modos.


Me inspiré en la respuesta de @ Eljakim y comencé este nuevo hilo en el que descubrí un gran truco. No implica bloquear nada y es muy sencillo.

INSERT INTO crawlLog (companyId, timeStartCrawling) SELECT id, now() FROM companies WHERE id NOT IN ( SELECT companyId FROM crawlLog AS crawlLogAlias ) LIMIT 1


Probablemente no quieras cerrar la mesa. Si lo hace, tendrá que preocuparse por los errores de captura cuando los otros rastreadores intenten escribir en la base de datos, que es lo que estaba pensando cuando dijo "... terriblemente complejo y se basa en muchas otras cosas".

En su lugar, probablemente debería envolver el grupo de consultas en una transacción de MySQL (consulte http://dev.mysql.com/doc/refman/5.0/en/commit.html ) de esta manera:

START TRANSACTION; SELECT @URL:=url FROM tablewiththeurls WHERE uncrawled=1 ORDER BY somecriterion LIMIT 1; INSERT INTO loggingtable SET url=@URL; COMMIT;

O algo parecido a eso.

[editar] Me di cuenta de que es probable que puedas hacer todo lo que necesites en una sola consulta y ni siquiera tengas que preocuparte por las transacciones. Algo como esto:

INSERT INTO loggingtable (url) SELECT url FROM tablewithurls u LEFT JOIN loggingtable l ON l.url=t.url WHERE {some criterion used to pick the url to work on} AND l.url IS NULL.



Yo no usaría el bloqueo, o las transacciones.

La forma más fácil de hacerlo es INSERTAR un registro en la tabla de registro si aún no está presente, y luego verificar ese registro.

Suponga que tiene tblcrawels (cra_id) que se llena con sus rastreadores y tblurl (url_id) que se llena con las URL, y una tabla tbllogging (log_cra_id, log_url_id) para su archivo de registro.

Ejecutaría la siguiente consulta si el rastreador 1 quiere comenzar a rastrear la url 2:

INSERT INTO tbllogging (log_cra_id, log_url_id) SELECT 1, url_id FROM tblurl LEFT JOIN tbllogging on url_id=log_url WHERE url_id=2 AND log_url_id IS NULL;

El siguiente paso es verificar si este registro ha sido insertado.

SELECT * FROM tbllogging WHERE log_url_id=2 AND log_cra_id=1

Si obtiene algún resultado, el rastreador 1 puede rastrear esta URL. Si no obtiene ningún resultado, significa que otro rastreador se insertó en la misma línea y ya está rastreando.