2010-06-24 12 views
6

Tengo un problema con que se invoque el mismo procedimiento almacenado exactamente al mismo tiempo con exactamente los mismos parámetros.Problema de concurrencia de inserción - Entorno multiproceso

El objetivo del procedimiento almacenado es recuperar un registro si existe o crear y recuperar el registro si no existe.

El problema es que ambos hilos están comprobando la existencia del registro y el informe falso y luego ambos están insertando un nuevo registro, creando un duplicado en la base de datos.

Intenté pegar las operaciones dentro de una transacción pero esto simplemente produjo cientos de puntos muertos.

¿Hay alguna forma de que pueda verificar la existencia del registro de forma segura para que el segundo hilo no lo lea hasta que el primero haya terminado su inserción? No tengo control sobre los hilos, solo sobre los procesos almacenados que están ejecutando.

Cualquier ayuda sería apreciada,

Gracias.

+0

Produjo interbloqueos, ¿qué están haciendo exactamente estas transacciones; no deben ser tan simples como dices. ¿Qué RDBMS estás usando? – BobbyShaftoe

+0

Por el contrario, el procedimiento es muy simple: comprueba si el registro ya existe, lo crea si no y luego selecciona el registro. La única razón por la que tenemos un problema es que hay muchos hilos concurrentes (hasta 200). – DJCasey

+0

Ah, se me olvidó decir, usando SQL Server 2008. – DJCasey

Respuesta

0

Problema al hacer una selección y luego insertar, normalmente hay un bloqueo de lectura para la selección y luego un bloqueo de escritura en la inserción. Sin una transacción, el tiempo de muchas actualizaciones a menudo permitirá que ocurran múltiples insertos, como puede ver. En una transacción, el primer bloqueo de lectura detendrá que otros procesos obtengan un bloqueo de escritura y, si más de un proceso obtiene un bloqueo de lectura, ninguno puede obtener un bloqueo de escritura y se produce un interbloqueo.

En este caso, modificaría el código de inserción para que los índices solo permitan que una inserción funcione, es decir, usted tiene una clave única y solo un proceso no podrá insertar los datos para no obtener duplicados. El proceso de actualización se encuentra entonces en una transacción ya sea

1) hacer la pieza de inserción primera y tratar con una excepción o error si se intenta insertar un duplicado

o 2) hacer un LOCK espera (Sybase y SQL Server) al hacer la selección primero - por lo tanto, el primero en bloquear obtiene el permiso completo para insertarlo si es necesario

o 3) Posiblemente utilice el comando de combinación si el RDBMS lo permite. Esto realiza la comprobación e inserta todo en un comando, pero siempre cambiará la base de datos.

EDIT: Creo que no existe una alternativa real a 1 si necesita asegurarse de que haya un solo registro insertado como prueba para eso en una transacción.

el costo se puede reducir verificando la existencia forst en una transacción y luego haciendo la inserción y marcando otra transacción. Por lo tanto, en la mayoría de los casos, solo tiene un seleccionar y en los otros casos, obtiene la inserción y comprobación lentas completas, pero esto debería ocurrir con menos frecuencia.

+0

Ya había probado la primera solución pero ralentizó demasiado el proceso; también he intentado con un bloqueo de espera, pero esto no solucionó el problema ya que ambos hilos ya tienen un bloqueo compartido en la tabla y ninguno puede elevarse. No estoy seguro sobre el comando de fusión, pero si siempre modifica la base de datos, entonces no es lo que quiero. – DJCasey

0

No estoy seguro de si SQL Server lo tiene. Pero en MySQL y en Oracle puedes obtener el bloqueo de escritura mientras haces una selección usando para actualizar la sintaxis.

select * 
from table 
for update 

Dado que otros hilos también necesitan bloqueo de escritura mientras seleccionan, esperarán hasta que el primer hilo complete la transacción.

+0

Esto tiene el mismo efecto que una transacción: crea bloqueos en la tabla. Esta sintaxis particular, sin una instrucción where, en realidad bloqueará toda la tabla. SQL Server no tiene esta sintaxis, pero incluso con Oracle, esta sintaxis genera demoras en comparación con las versiones más simples. –

7

El truco consiste en agregar un DONDE a su instrucción INSERT para que INSERT solo funcione si el elemento no existe, seguido de la instrucción SELECT. Suponiendo que el registro puede ser identificado por una columna de ID que iba a escribir:

INSERT INTO MyTable (ID,Col1,Col2,...) 
SELECT @IDValue,@Col1Value,@Col2Value, ... 
WHERE NOT EXISTS (SELECT ID 
       FROM MyTable 
       WHERE [email protected]) 

SELECT * 
FROM MyTable 
Where [email protected] 

No es necesario poner las declaraciones en una transacción debido a que cada sentencia se ejecuta en su propia transacción implícita. Por lo tanto, no hay forma de que dos INSERTOS tengan éxito al mismo tiempo.

EDIT: La sintaxis INSERT ... SELECT es necesaria porque TSQL no permite una parte VALUES y una parte WHERE en la instrucción INSERT.

+0

Me pregunto si también debería agregar WITH (UPDLOCK, HOLDLOCK) a la instrucción de selección interna? –

+0

¿Para qué? La operación es atómica. HOLDLOCK solo mantendrá los bloqueos exactamente de la misma manera que una transacción, hasta que se cierre la conexión o se complete la transacción abarcadora (si hubo una). De hecho, HOLDLOCK es peor que una transacción explícita porque no puede controlar los bloqueos hasta que se cierre la conexión –

0

Supongo que está utilizando C# para comunicarse con el servidor sql, entonces puede intentar consultar el paralelismo de tareas y la biblioteca de tareas para realizar múltiples subprocesos en los procedimientos almacenados.

+0

Es muy poco probable que cambiar la técnica para acceder a un recurso externo resulte en la solución de un problema de concurrencia. También puede no ser válido si lo fuera, ya que no estamos seguros de qué es el cliente. –

Cuestiones relacionadas