2012-02-04 10 views
10

Tengo un script PHP que ejecuta una consulta SELECT y luego borra inmediatamente el registro. Hay varias máquinas que hacen ping al mismo archivo php y obtienen datos de la misma tabla. Cada máquina remota se ejecuta en un trabajo cron.SELECCIONAR luego inmediatamente ELIMINAR registro mysql

Mi problema es que a veces no se puede eliminar lo suficientemente rápido ya que algunas de las máquinas hacen ping al mismo tiempo.

Mi pregunta es, ¿cómo puedo SELECCIONAR un registro de una base de datos y eliminarlo antes de que la siguiente máquina lo agarre? Por ahora acabo de agregar un pequeño retraso, pero no está funcionando muy bien. Intenté usar una transacción, pero no creo que se aplique aquí.

He aquí un fragmento de ejemplo de mi guión:

<?php 

$query = "SELECT * FROM `queue` LIMIT 1"; 
$result = mysql_query($query) or die(mysql_error()); 

while($row = mysql_fetch_array($result)){ 
    $email = $row['email']; 
    $campaign_id = $row['campaign']; 
} 

$queryx = "DELETE FROM `queue` WHERE `email` = '".$email."'"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

apreciar realmente la ayuda.

+2

¿cómo no se aplica? eso suena * exactamente * como para lo que una transacción es buena. – mpen

+0

¿Se le permite usar procedimientos almacenados? – dvicino

+0

@Mark - ¿Las transacciones también evitan que 'SELECT's funcione? Me pregunto si este no es el [email protected] - Si estos son ejecutados por 'cron', ¿cuál es el punto de' die (mysql_error()) '? ¿Por qué no registrar el error en un archivo o algo así? –

Respuesta

-3

Ponga sus consultas de eliminación dentro del ciclo while, solo en el caso de que quiera aumentar el límite de su selección.

<?php 
$query = mysql_query("SELECT * FROM `queue` LIMIT 1") or die(mysql_error()); 

while($row = mysql_fetch_array($query)){ 
    mysql_query("DELETE FROM `queue` WHERE `email` = '" . $row['email'] . "' LIMIT 1") or die(mysql_error()); 
} 
?> 

El código anterior sería lo mismo que correr:

mysql_query("DELETE FROM `queue` LIMIT 1") or die(mysql_error()); 

Tenga cuidado de usar su consulta de eliminación, si el campo de correo electrónico está en blanco, se eliminará todas las filas que tienen un correo electrónico en blanco. Agregue LIMIT 1 a su consulta de eliminación para evitar que se eliminen varias filas.

Para añadir un retardo aleatorio, se puede añadir un sleep a la parte superior de la secuencia de comandos,

por ejemplo:

<?php 
$seconds = mt_rand(1,10); 
sleep($seconds); 
?> 
+1

No sé si esto eliminará el problema de condición/contención de la carrera, simplemente "acorta" la cantidad de actividad "posible". Podría funcionar en algunos casos, pero aún podría haber anomalías. Tenga en cuenta, también, ya que hay un 'LIMIT 1', el' while() 'es superfluo. Así que no sé si esto realmente haría algo productivo al final ... –

+0

Si alguna vez decide cambiar el SELECT a más de 1 resultado, la consulta de eliminación solo se ejecutará en el primer resultado. Así que, al final, es más productivo y no tiene un "recurso diferente" que tener el DELETE fuera del ciclo. –

+0

"Si"? Hay muchas si. Si el OP está haciendo una cola, me imagino que solo debe ser uno. El punto sigue siendo el mismo (y se amplifica si se obtienen muchos resultados para los resultados procesados ​​posteriores). –

6

así me gustaría utilizar bloqueos de tabla read more here

de bloqueo es seguro y se aplica a una sesión de cliente. Un bloqueo de tabla solo protege contra lecturas o escrituras inapropiadas en otras sesiones.

1

ejecuta una consulta de actualización que cambiará la clave antes de realizar la selección. Haga la selección con esta nueva clave, que solo se conoce en la misma sesión.
Si la tabla es innoDB, el registro está bloqueado, y cuando se liberará, el otro elemento seleccionado no encontrará el registro.

+1

[SELECCIONAR ... PARA ACTUALIZAR Y SELECCIONAR ... BLOQUEAR EN EL MODO COMPARTIR Bloquear lecturas] (http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html). –

+0

Solo la pregunta que tengo con mi comentario anterior, ¿puedes 'DELETE' desde el modo' SELECT ... FOR UPDATE'? ¿O primero tienes que desbloquear (y por lo tanto de vuelta a donde empezamos)? –

3

Debe utilizar subconsulta de la siguiente manera ...

<?php 

$queryx = "DELETE FROM `queue` WHERE `email` IN (SELECT email FROM `queue` LIMIT 1)"; 
$resultx = mysql_query($queryx) or die(mysql_error()); 

?> 

* Nota: Siempre seleccionar sólo los campos que desee ... tratar de evitar select * ... esto va a ralentizar el rendimiento

+0

Interesante. Si el objetivo es borrar todas las filas en cola por correo electrónico "aleatorio", esto podría funcionar. –

+0

Además, el 'SELECT *' es un gran consejo. 'SELECT email' debería ser suficiente. –

+0

No puede usar LIMIT dentro de una subconsulta. De lo contrario, esto era exactamente lo que estaba tratando de hacer. – john

4

Si está utilizando MariaDB 10:

DELETE FROM `queue` LIMIT 1 RETURNING * 

Documentation.

+0

Esto es genial, pero solo funciona en MariaDB 10.0.5 o posterior –