sql >> Base de Datos >  >> RDS >> PostgreSQL

Cómo marcar cierto número de filas en la tabla en acceso concurrente

En la respuesta relacionada a la que te refieres:

  • ACTUALIZACIÓN de Postgres... LÍMITE 1

El objetivo es bloquear uno fila a la vez. Esto funciona bien con o sin bloqueos de aviso, porque no hay posibilidad de bloqueo. - siempre que no intente bloquear más filas en la misma transacción.

Su ejemplo es diferente en el sentido de que desea bloquear 3000 filas a la vez . Hay es posibilidad de interbloqueo, excepto si todas las operaciones de escritura simultáneas bloquean filas en el mismo orden coherente. Por documentación:

Por lo general, la mejor defensa contra los interbloqueos es evitarlos asegurándose de que todas las aplicaciones que utilizan una base de datos adquieran bloqueos en varios objetos en un orden coherente.

Implemente eso con ORDER BY en su subconsulta.

UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM  ( 
   SELECT id
   FROM   cargo_item
   WHERE  state='NEW' AND job_id is null 
   ORDER  BY id
   LIMIT  3000
   FOR UPDATE
   ) sub
WHERE  item.id = sub.id;

Esto es seguro y confiable, siempre y cuando todos las transacciones adquieren bloqueos en el mismo orden y no se esperan actualizaciones simultáneas de las columnas de orden. (Lea el cuadro amarillo de "PRECAUCIÓN" al final de este capítulo en el manual). Por lo tanto, esto debería ser seguro en su caso, ya que no actualizará el id columna.

Efectivamente, solo un cliente a la vez puede manipular filas de esta manera. Las transacciones simultáneas intentarían bloquear las mismas filas (bloqueadas) y esperar a que finalice la primera transacción.

Bloqueos de aviso son útiles si tiene muchas o muy largas transacciones simultáneas (no parece que las tenga). Con solo unos pocos, será más económico en general usar la consulta anterior y hacer que las transacciones simultáneas esperen su turno.

Todo en uno ACTUALIZACIÓN

Parece que el acceso concurrente no es un problema per se en su configuración. La concurrencia es un problema creado por su solución actual.

En su lugar, hazlo todo en una sola UPDATE . Asigna lotes de n números (3000 en el ejemplo) a cada UUID y actualice todo a la vez. Debería ser el más rápido.

UPDATE cargo_item c
SET    job_id = u.uuid_col
     , job_ts = now()
FROM  (
   SELECT row_number() OVER () AS rn, uuid_col
   FROM   uuid_tbl WHERE  <some_criteria>  -- or see below
   ) u
JOIN (
   SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id 
   FROM   cargo_item
   WHERE  state = 'NEW' AND job_id IS NULL
   FOR    UPDATE   -- just to be sure
   ) c2 USING (rn)
WHERE  c2.item_id = c.item_id;

Puntos principales

  • La división entera trunca. Obtiene 1 para las primeras 3000 filas, 2 para las siguientes 3000 filas. etc.

  • Elijo filas arbitrariamente, podrías aplicar ORDER BY en la ventana de row_number() para asignar ciertas filas.

  • Si no tiene una tabla de UUID para enviar (uuid_tbl ), use un VALUES expresión para suministrarlos. Ejemplo.

  • Obtiene lotes de 3000 filas. Al último lote le faltarán 3000 si no encuentra un múltiplo de 3000 para asignar.