php - optimize - Evitar el acceso simultáneo a la fila de la tabla db
mysql how to use explain (4)
Creo que el bloqueo a nivel de fila de la base de datos no cumple la situación que mencionaste en el primer método. Pero tampoco creo que la creación de archivos sea más rápida que acceder al sistema de bases de datos. La creación de archivos obviamente es más pesada que CRUD en la base de datos.
Entonces, sugiero un enfoque similar con la tabla de registro.
- Cada tabla tiene su propia clave principal (como
pid
) - Registre el nombre de la tabla y el pid en la tabla de registro con la marca de tiempo cuando alguien intente manipular una fila.
- Verifique la tabla de registro antes de ejecutar una consulta.
A veces sucede que dos administradores de nuestro equipo de soporte intentan hacer la misma operación confidencial en la fila de la tabla db (digamos, modificar el valor en la fila). Necesitamos prevenir eso. (El bloqueo de filas no es posible porque las tablas son "myisam")
He pensado en varias soluciones:
estableciendo el valor anterior en el formulario y comparándolo con el actual en enviar
<input name="money"><input type="hidden" name="old_money" value="10">
y luego antes de actualizar:
$currentmoney=value_from_query("select money from mytable","money");
if($currentmoney!=$_REQUEST["old_money"]){
return "value changed to $currentmoney while you were editing it, are you sure you still want to change it?!??!?!?!?";
}
else{
mysql_query("update everyonesmoney set money=''".intval($_REQUEST["money"])."'' where user=''$user_id''");
return true;
}
pero puede haber una situación siguiente:
el usuario necesita un valor monetario que se cambiará de 9 $ a 10 $
admin1 cambia su dinero a 10 $
¡el usuario gasta inteligentemente 1 $, por lo que su dinero actual se vuelve 9 $ otra vez!
admin2 cambia su dinero a 10 $ sin previo aviso.
crear la marca de fecha y hora (updated_at column) en la fila
Y haciendo lo mismo que en la solución 1. Esto tiene la ventaja de que está diciendo algo más que la simple comparación de datos. Podemos decir con certeza si los datos se cambiaron mientras estábamos jugando con el formulario o no. desventaja: no podemos rastrear qué columna se modificó exactamente, a menos que la combinemos con la solución 1
<input type="hidden" name="formtimestamp" value="<? echo time();?>">
y luego al actualizar:
$query_add = ($overriden ? "" : " and updated_at>''".securevalue($_REQUEST["formtimestamp"])."''");
if(mysql_affected_rows(mysql_query("update everyonesmoney set money=''".intval($_REQUEST["money"])."'', updated_at=NOW() where user=''$user_id'' ".$query_add))==0){
return "some values were changed by someone else while you were editing it, are you sure you still want to change it?!??!?!?!?";
}
else{
return true;
}
crear el archivo temporal de 0 longitudes con nombre específico de objeto / acción
Creación / bloqueo durante la actualización y comprobación de su existencia / fecha antes de la actualización.
Antes de la actualización:
$myfname="/tmp/user{$user_id}EDITMONEY.tmp";
$timedifference=((time()-filectime($myfname)); //in seconds
if(file_exists($myfname) and ($timedifference<60) and (!$overriden)){ // a minute difference
$currentmoney=value_from_query("select money from mytable","money");
return "money were edited by someone else $timedifference seconds ago and set to {$currentmoney}, are you sure you still want to change it?!??!?!?!?";
}else{
$fp = fopen("/tmp/user".intval($_REQUEST["user_id"])."EDITMONEY.tmp", "r+");
if (flock($fp, LOCK_EX)) { // do an exclusive lock
mysql_query("update everyonesmoney set money=''".intval($_REQUEST["money"])."'' where user=''$user_id''")
flock($fp, LOCK_UN); // release the lock
return true;
} else {
return "Couldn''t get the lock, it''s possible that someone tried to execute query simultaneously!";
}
fclose($fp);
}
Por ahora, la creación de archivos es mi enfoque preferido porque:
Creo que es más rápido crear un archivo local que acceder a la base de datos.
No necesito agregar una columna más (marca de tiempo) a la tabla
Puedo modificar fácilmente el nombre del archivo para verificar la modificación específica de la columna, es decir, crear el archivo "money_user {$ userid} _modified" cuando se completa mysqlupdate.
¿Es correcto o hay algo que malinterpreto?
Eche un vistazo a InnoDB y las transacciones. Son más adecuados para cambios sensibles (es decir, equilibrio).
Las bases de datos generalmente son mejores ya que son una solución centralizada. Si tiene que escalar debido al tráfico o, en general, a la carga de trabajo, no es fácil sincronizar esos archivos. A menos que no espere ninguna necesidad de escalado y las tasas de E / S sean buenas, está bien.
Permítanme mencionar dos soluciones posibles, que también podrían haber mencionado anteriormente.
Puede agregar un "assigned_id" con la identificación de su cuenta de administrador combinada con una marca de tiempo para que su aplicación muestre una advertencia si alguien más la está editando.
Otra posible solución es verificar si se han realizado cambios mientras llenaba sus formularios. Una marca de tiempo last_edited podría utilizarse aquí.
En tu caso, supongo que bloquear es el mejor enfoque. Puede usar bloqueos MySQL: GET_LOCK, RELEASE_LOCK, IS_FREE_LOCK . Las transacciones, en mi opinión, no garantizan que la fila no cambie mientras otro proceso realiza su tarea en los datos obtenidos.
Aunque, su caso particular no tiene nada que ver con el bloqueo en el sentido tradicional. En mi humilde opinión, debe registrar sus transacciones con las credenciales y descripciones correspondientes, para que sus administradores puedan leerlas y no doblar la misma modificación de saldo. Lock puede proteger contra la modificación simultánea de filas, pero no de cambios intencionales en caso de doblaje.
Puede especificar el valor antiguo en la cláusula WHERE
la operación UPDATE
y luego ver el número de filas afectadas:
Dado
id name amount
--- ------------- ---------
1 Joe User 10
El hilo 1 se ejecuta
UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 1 row(s) affected
El hilo 2 se ejecuta
UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 0 row(s) affected
Aparte de eso, probablemente implementaría la exclusión un poco antes, al asignar tareas a administradores individuales primero, con el fin de reducir la cantidad de tiempo desperdiciado.