Lo que desea es SELECCIONAR ... PARA ACTUALIZAR desde el contexto de una transacción. SELECCIONAR PARA ACTUALIZAR coloca un bloqueo exclusivo en las filas seleccionadas, como si estuviera ejecutando ACTUALIZAR. También se ejecuta implícitamente en el nivel de aislamiento READ COMMITTED, independientemente de lo que se establezca explícitamente. Solo tenga en cuenta que SELECCIONAR ... PARA ACTUALIZAR es muy malo para la concurrencia y solo debe usarse cuando sea absolutamente necesario. También tiene una tendencia a multiplicarse en una base de código a medida que las personas cortan y pegan.
Aquí hay una sesión de ejemplo de la base de datos Sakila que muestra algunos de los comportamientos de las consultas FOR UPDATE.
Primero, para que seamos claros, configure el nivel de aislamiento de la transacción en REPEATABLE READ. Esto normalmente no es necesario, ya que es el nivel de aislamiento predeterminado para InnoDB:
session1> SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
session1> BEGIN;
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
En la otra sesión, actualice esta fila. Linda se casó y cambió su nombre:
session2> UPDATE customer SET last_name = 'BROWN' WHERE customer_id = 3;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
De vuelta en la sesión 1, porque estábamos en REPEATABLE READ, Linda sigue siendo LINDA WILLIAMS:
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | WILLIAMS |
+------------+-----------+
1 row in set (0.00 sec)
Pero ahora, queremos acceso exclusivo a esta fila, por lo que llamamos PARA ACTUALIZAR en la fila. Observe que ahora recuperamos la versión más reciente de la fila, que se actualizó en la sesión 2 fuera de esta transacción. Eso no es REPETIBLE LEÍDO, eso es LEÍDO COMPROMETIDO
session1> SELECT first_name, last_name FROM customer WHERE customer_id = 3 FOR UPDATE;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| LINDA | BROWN |
+------------+-----------+
1 row in set (0.00 sec)
Probemos el bloqueo establecido en la sesión1. Tenga en cuenta que session2 no puede actualizar la fila.
session2> UPDATE customer SET last_name = 'SMITH' WHERE customer_id = 3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Pero aún podemos seleccionarlo
session2> SELECT c.customer_id, c.first_name, c.last_name, a.address_id, a.address FROM customer c JOIN address a USING (address_id) WHERE c.customer_id = 3;
+-------------+------------+-----------+------------+-------------------+
| customer_id | first_name | last_name | address_id | address |
+-------------+------------+-----------+------------+-------------------+
| 3 | LINDA | BROWN | 7 | 692 Joliet Street |
+-------------+------------+-----------+------------+-------------------+
1 row in set (0.00 sec)
Y aún podemos actualizar una tabla secundaria con una relación de clave externa
session2> UPDATE address SET address = '5 Main Street' WHERE address_id = 7;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
session1> COMMIT;
Otro efecto secundario es que aumenta en gran medida su probabilidad de causar un punto muerto.
En su caso específico, probablemente desee:
BEGIN;
SELECT id FROM `items` WHERE `status`='pending' LIMIT 1 FOR UPDATE;
-- do some other stuff
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `id`=<selected id>;
COMMIT;
Si la pieza "hacer otras cosas" es innecesaria y no necesita mantener información sobre la fila, entonces SELECCIONAR PARA ACTUALIZAR es innecesario y derrochador y, en su lugar, puede ejecutar una actualización:
UPDATE `items` SET `status`='working', `updated`=NOW() WHERE `status`='pending' LIMIT 1;
Espero que esto tenga sentido.
items
DONDEstatus
= 'pendiente' LÍMITE 1 PARA LA ACTUALIZACIÓN;" y ambos ven la misma fila, entonces uno bloqueará al otro. Tenía la esperanza de que de alguna manera podría pasar por alto la fila bloqueada y pasar al siguiente elemento que estaba pendiente ...