sistemas - mysql insertar condición de carrera
race condition vulnerability (7)
lo que quieres es TABLAS DE BLOQUEO
o si eso parece excesivo, ¿qué tal INSERTAR IGNORAR con un control de que la fila realmente se insertó?
Si usa la palabra clave IGNORE, los errores que se producen al ejecutar la instrucción INSERT se tratan como advertencias.
¿Cómo se detienen las condiciones de carrera en MySQL? el problema en cuestión es causado por un algoritmo simple:
- seleccionar una fila de la mesa
- si no existe, insértela
y luego obtienes una fila duplicada, o si la evitas mediante claves únicas / principales, un error.
Ahora, normalmente, creo que las transacciones ayudan aquí, pero dado que la fila no existe, la transacción en realidad no ayuda (¿o me falta algo?).
LOCK TABLE suena como una exageración, especialmente si la tabla se actualiza varias veces por segundo.
La única otra solución que puedo pensar es GET_LOCK () para cada ID diferente, pero ¿no hay una mejor manera? ¿No hay problemas de escalabilidad aquí también? Y también, hacerlo para cada mesa suena un poco antinatural, ya que para mí es un problema muy común en las bases de datos de alta concurrencia.
A nivel técnico, una transacción ayudará aquí porque otros hilos no verán la nueva fila hasta que usted realice la transacción.
Pero en la práctica eso no resuelve el problema, solo lo mueve . Su aplicación ahora necesita verificar si falla la confirmación y decidir qué hacer. Normalmente me gustaría deshacer lo que hizo, y reiniciar la transacción porque ahora la fila será visible. Así es como se supone que debe funcionar el programador basado en transacciones.
Me parece que debe tener un índice único en su columna de identificación, por lo que una inserción repetida generaría un error en lugar de ser nuevamente aceptada deslumbrantemente.
Eso se puede hacer definiendo el ID como clave principal o usando un índice único por sí mismo.
Creo que la primera pregunta que debe hacerse es ¿por qué tiene muchos hilos haciendo exactamente el MISMO trabajo? ¿Por qué tendrían que insertar la misma fila exacta?
Luego de que me respondan, creo que ignorar los errores será la solución más efectiva, pero mida ambos enfoques (GET_LOCK v / s ignore los errores) y compruébelo usted mismo.
No hay otra manera que yo sepa. ¿Por qué quieres evitar errores? Aún debe codificar el caso cuando se produce otro tipo de error.
Como dice staticsan, las transacciones ayudan, pero, como normalmente están implícitas, si dos inserciones se ejecutan por hilos diferentes, ambas estarán dentro de una transacción implícita y verán vistas consistentes de la base de datos.
Me encontré con el mismo problema y busqué en la red por un momento :)
Finalmente se me ocurrió una solución similar al método para crear objetos del sistema de archivos en directorios compartidos (temporales) para abrir de manera segura los archivos temporales:
$exists = $success = false;
do{
$exists = check();// select a row in the table
if (!$exists)
$success = create_record();
if ($success){
$exists = true;
}else if ($success != ERROR_DUP_ROW){
log_error("failed to create row not ''coz DUP_ROW!");
break;
}else{
//probably other process has already created the record,
//so try check again if exists
}
}while(!$exists)
No tengas miedo de busy-loop : normalmente se ejecutará una o dos veces.
Bloquear la mesa entera es de hecho excesivo. Para obtener el efecto que desea, necesita algo que la literatura denomina "bloqueos de predicados". Nadie ha visto ninguno excepto impreso en el papel en el que se publican los estudios académicos. Lo mejor es bloquear las "rutas de acceso" a los datos (en algunos DBMS: "bloqueos de página").
Algunos sistemas no SQL le permiten hacer ambos (1) y (2) en una sola declaración, más o menos significa que las posibles condiciones de carrera que surgen de su sistema operativo suspendiendo su hilo de ejecución entre (1) y (2), son completamente eliminado
Sin embargo, en ausencia de bloqueos de predicados, dichos sistemas necesitarán recurrir a algún tipo de esquema de bloqueo, y cuanto más fina sea la "granularidad" (/ "alcance") de los bloqueos, mejor será para la concurrencia.
(Y para concluir: algunos SGBD, especialmente los que no tiene que pagar, en realidad no ofrecen una granularidad de bloqueo más fina que "toda la tabla").
Evita las filas duplicadas simplemente colocando índices únicos en sus tablas. Eso no tiene nada que ver con LOCKS o TRANSACTIONS.
¿Te importa si falla un inserto porque es un duplicado? ¿Necesita ser notificado si falla? ¿O todo lo que importa es que se haya insertado la fila, y no importa quién o cuántos duplicados insertaron?
Si no te importa, todo lo que necesitas es INSERT IGNORE
. No hay necesidad de pensar en transacciones o bloqueos de tabla en absoluto.
InnoDB tiene bloqueo de nivel de fila automáticamente, pero eso se aplica solo a actualizaciones y eliminaciones. Tiene razón en que no se aplica a las inserciones. ¡No puedes bloquear lo que aún no existe!
Puede LOCK
explícitamente toda la tabla. Pero si su propósito es evitar duplicados, entonces lo está haciendo mal. Nuevamente, use un índice único.
Si hay un conjunto de cambios que realizar y desea un resultado de todo o nada (o incluso un conjunto de resultados de todo o nada dentro de un resultado de todo o nada más grande), utilice transacciones y puntos de rescate. A continuación, utilice ROLLBACK
o ROLLBACK TO SAVEPOINT *savepoint_name*
para deshacer los cambios, incluidas las eliminaciones, las actualizaciones y las inserciones.
LOCK
tablas LOCK
no reemplazan las transacciones, pero es su única opción con las tablas MyISAM, que no son compatibles con las transacciones. También puede usarlo con tablas InnoDB si el nivel de nivel de fila no es suficiente. Consulte esta página para obtener más información sobre el uso de transacciones con instrucciones de bloqueo de tabla.
Tengo un problema similar. Tengo una tabla que en la mayoría de los casos debería tener un único valor ticket_id, pero hay algunos casos en los que tendré duplicados; no es el mejor diseño, pero es lo que es.
- El usuario A comprueba si el ticket está reservado, no es
- El usuario B comprueba si el ticket está reservado, no es
- El usuario B inserta un registro ''reservado'' en la tabla para ese boleto
- El usuario A inserta un registro ''reservado'' en la tabla para ese boleto
- El usuario B verifica el duplicado? Sí, ¿es mi registro más nuevo? Sí, déjalo
- Usuario Un cheque para duplicar? Sí, ¿es mi registro más nuevo? No, elimínelo
El usuario B ha reservado el ticket, el usuario A informa que el ticket ha sido tomado por otra persona.
La clave en mi caso es que necesita un desempate, en mi caso es la identificación de incremento automático en la fila.