2010-03-17 14 views
13

tengo una tabla de recursos (digamos carros) que quiero afirmar atómicamente. Luego quiero información sobre qué recurso acabo de reclamar.cómo reclamar atómicamente una fila o recurso usando ACTUALIZAR en mysql

Si hay un límite de un recurso por un usuario, que pueda hacer el siguiente truco:

UPDATE cars SET user = 'bob' WHERE user IS NULL LIMIT 1 
SELECT * FROM cars WHERE user = 'bob' 

De esta manera, reclamo el recurso de forma atómica y luego puedo ver qué fila acabo reivindica.

Esto no funciona cuando 'bob' puede reclamar varios automóviles. Me doy cuenta de que puedo obtener una lista de los coches que ya ha reclamado Bob, reclamar otro, y luego SELECT nuevamente para ver qué ha cambiado, pero eso me parece harto.

Lo que me pregunto es, ¿hay alguna forma de ver qué filas acabo de actualizar con mi última ACTUALIZACIÓN?

En su defecto, ¿hay algún otro truco para reclamar atómicamente una fila? Realmente quiero evitar el uso del nivel de aislamiento SERIALIZABLE. Si hago algo como esto:

1 SELECT id FROM cars WHERE user IS NULL 
2 <here, my PHP or whatever picks a car id> 
3 UPDATE cars SET user = 'bob' WHERE id = <the one i picked> 

habría REPEATABLE READ ser suficiente aquí? En otras palabras, ¿podría garantizarse que algunas otras transacciones no reclamarán la fila que mi software seleccionó durante el paso 2?

+0

encontré esto: http://stackoverflow.com/questions/1821142/atomically-mark-and-return-a-group-of-rows-in-database pero fue preguntado sobre el servidor SQL - no puedo encontrar un equivalente mySQL –

Respuesta

8

ACTUALIZAR autos SET user = 'bob' WHERE id = 123 Y el usuario IS NULL;

La consulta de actualización devuelve el número de filas modificadas. Si no ha actualizado ninguna, sabrá que el automóvil ya ha sido reclamado por otra persona.

O bien, puede usar SELECCIONAR ... PARA ACTUALIZAR.

+1

gracias por informarme sobre SELECT ... PARA ACTUALIZAR - es más o menos exactamente lo que estaba buscando. –

1

Una cosa que puede usar es un SELECT FOR UPDATE. Esto le permitirá seleccionar, conocer lo que seleccionó y luego actualizar esos valores. El bloqueo se libera cuando la transacción se completa.

<?php 
$link = mysqli_connect("localhost", "my_user", "my_password", "test"); 

mysqli_autocommit($link, FALSE); 

$result = mysqli_query($link, "SELECT id FROM cars WHERE user IS NULL"); 

// do something with the results 

mysqli_query($link, "UPDATE cars SET user = 'bob' WHERE id = <the one that was picked>"); 

mysqli_commit($link); 

mysqli_close($link); 
?> 
+2

Su ejemplo es incorrecto, necesita agregar "PARA ACTUALIZAR" en la consulta de ACTUALIZACIÓN. También muy importante: Esto solo funciona con InnoDB como motor de almacenamiento, no con MyISAM. El tipo de almacenamiento se puede mostrar con "mostrar el estado de la tabla;". Puede cambiar el motor de almacenamiento con "ALTER TABLE tablename ENGINE = INNODB;" – seb

1
update cars 
set @id = id, user= 'bob' 
where user is null 

se garantiza que sea atómica y @id le dirá lo que fue la última fila informado.

+0

esto es realmente inteligente. ciertamente usaré esto en algún momento, aunque aquí creo que prefiero seleccionar ... para la actualización –

+0

Por alguna razón, esto me da el error 1064 (ver http://stackoverflow.com/questions/32615884/leasing-jobs-atomic- update-and-get-from-a-mysql-database). ¿Está permitido establecer variables definidas por el usuario en la parte 'SET' de' UPDATE'? –

+0

Parece que necesita utilizar un truco para que su sugerencia funcione, ya que no se permite establecer una variable de usuario de esta manera. Detalles aquí: http://stackoverflow.com/questions/32615884/leasing-jobs-atomic-update-and-get-from-a-mysql-database/32617172#32617172 –

0

No estoy seguro de por qué hay tanta información errónea en estas respuestas, pero la respuesta es directa (incluso si se trata de un tema SQL avanzado). Esto es lo que significa "bloquear" en prácticamente cualquier RDBMS. La sintaxis exacta depende del proveedor y la versión, y algunos ofrecen una sintaxis que intenta ocultar el bloqueo del usuario (generalmente cuando tanto la selección como la actualización están en la misma consulta).

Para MySQL, primero usaría el SELECT ... FROM ... FOR UPDATE; que le dice a la base de datos que establezca un bloqueo exclusivo en cada registro que devuelve.

Importante ¡No bloquee más filas de las que necesita! Realice la consulta "SELECCIONAR PARA ACTUALIZAR" lo más detallada posible, con un uso liberal de las cláusulas "DÓNDE" y "LIMITAR".

Posteriormente, cuando la misma conexión a la base de datos emite un UPDATE ... en las mismas filas que se bloquearon anteriormente, ese bloqueo se libera y otros pueden acceder a esa fila una vez más.

Digamos que tiene una cola de trabajos, con un campo "ESTADO" que se usa para establecer el estado de progreso de cada trabajo. 0 es para en cola, 1 es para en progreso, y 2 está para listo, 3 es para fallado, etc.

Cada corredor puede obtener atómicamente un trabajo para ejecutar (para que no haya dos corredores que intenten realizar el mismo trabajo) emitiendo el siguiente:

SELECT ID, * FROM JOBS WHERE STATUS = 0 LIMIT 1 FOR UPDATE;

continuación

UPDATE JOBS SET STATUS = 1 WHERE JOBS.ID = X;

entonces se puede ejecutar el trabajo, y actualizar la base de datos cuando se hace:

UPDATE JOBS SET STATUS = [2|3] WHERE JOBS.ID = X;

Importante

De la documentación de MySQL:

Todos los bloqueos establecidos por LOCK IN SHARE MODE y consultas de actualización son liberados cuando la transacción se confirma o se deshace.

SELECT FOR UPDATE no bloquea los registros si la confirmación automática está habilitada. Deshabilitar la confirmación automática o (preferiblemente) usando START TRANSACTION; SELECT ... FROM ... FOR UPDATE; UPDATE ...; END TRANSACTION;

Cuestiones relacionadas