2010-10-03 15 views
6

Sólo para darle un ejemplo:¿Cómo gestiona MySQL múltiples consultas de múltiples usuarios simultáneamente?

Tengo un script PHP que administra los votos de los usuarios.

Cuando un usuario vota, la secuencia de comandos realiza una consulta para verificar si alguien ya votó por la misma ID/producto. Si nadie ha votado, realiza otra consulta e inserta el ID en una tabla de ID de ID general y otra para insertar los datos en una tabla de votos de ID de usuario. Y este tipo de comportamiento se repite en otro tipo de secuencias de comandos.

La pregunta es si dos usuarios diferentes votan simultáneamente, es posible que las dos instancias del código intenten insertar una nueva ID (o algún tipo similar de consulta) que dará un error.

En caso afirmativo, ¿cómo evito que esto suceda?

Gracias?

Nota importante: ¡Estoy usando MyISAM! Mi alojamiento web no permite InnoDB.

+0

Puede ser útil mostrar la estructura de su tabla (nombres de tabla/columna) – rojoca

+0

Solo estoy dando un ejemplo pero para ayudarlo: la tabla product_votes tiene un productID, un votes_count y una columna total_votes_value. La tabla user_product_votes tiene un ID de usuario, un IdProducto y una columna usuario_voto. – Jonathan

+1

La única razón por la que pregunto es que en este ejemplo parece que sería mejor reorganizar la lógica y la estructura de la tabla para que las inserciones simultáneas no importen en lugar de tratando de prevenirlos. ¿Hace una pregunta general o necesita resolver el problema específico en su ejemplo? – rojoca

Respuesta

6

La pregunta es, si dos usuarios diferentes votaciones simultáneamente su posible que las dos instancias del código intenta insertar un nuevo ID (o algún tipo similar de consulta) que le dará un erro

Sí, es posible que termine con dos consultas al insertar. Según las restricciones de la tabla, una de ellas generará un error o terminarás con dos filas en tu base de datos.

Puede resolver esto, creo, con la aplicación de un bloqueo; p.Si es necesario agregar un voto para el producto con theProductId id: (pseudo código)

START TRANSACTION; 
//lock on the row for our product id (assumes the product really exists) 
select 1 from products where id=theProductId for update; 
//assume the vote exist, and increment the no.of votes 
update votes set numberOfVotes = numberOfVotes + 1 where productId=theProductId ; 
//if the last update didn't affect any rows, the row didn't exist 
if(rowsAffected == 0) 
    insert into votes(numberOfVotes,productId) values(1,theProductId) 
//insert the new vote in the per user votes 
insert into user_votes(productId,userId) values(theProductId,theUserId); 
COMMIT; 

Algunos más información here

MySQL ofrece otra solución, así, que podría ser aplicable en este caso, insert on duplicate

por ejemplo usted podría ser capaz de simplemente hacer:

insert into votes(numberOfVotes,productId) values(1,theProductId) on duplicate key 
    update numberOfVotes = numberOfVotes + 1; 

Si los votos de la tabla tienen una clave única en la columna de la identificación del producto, lo anterior se hacer una inserción si no existe la theProductId en particular, de lo contrario se hará una update, donde incrementa la columna numberOfVotes por 1

Probablemente pueda evitar mucho de esto si crea una fila en la tabla de votos al mismo tiempo que agrega el producto a la base de datos. De esta forma, puede estar seguro de que siempre hay una fila para su producto, y simplemente emita una ACTUALIZACIÓN en esa fila.

+0

Las transacciones existen exactamente para resolver este problema. Al usar las transacciones de esta manera, usted garantiza que ninguna consulta puede ver un estado intermedio en la base de datos. – SingleNegationElimination

+0

+1 para 'en clave duplicada'. Funcionamiento atómico> bloqueo manual. – egrunin

-4

Esto cuando el patrón singleton es útil. Asegura que un código se ejecuta solo por un proceso en un instante.

http://en.wikipedia.org/wiki/Singleton_pattern

usted tiene que hacer una clase singleton para el acceso a la base de datos de este evitará que el tipo de error que describir.

Saludos.

+0

Mala idea, y no resolverá el problema del acceso concurrente a DB en PHP. – nos

+1

Una clase singleton (incluso en un entorno Java típico) no resolverá el problema. Simplemente significa que solo puede haber una instancia de esto, pero a esta instancia se puede acceder mediante varios hilos en paralelo. –

+0

Gracias por la explicación :) – enokd

1

La pregunta es, si dos diferentes usuarios califican al mismo tiempo su posible que las dos instancias del código intenta insertar un nuevo ID (o algún tipo similar de consulta) que dará un error? ?

Sí, en general esto es posible. Este es un ejemplo de un problema muy común en sistemas concurrentes, llamado race condition.

Evitarlo puede ser un tanto complicado, pero en general debe asegurarse de que las operaciones no se pueden intercalar en la forma que describe, p. Ej. bloqueando la base de datos por un tiempo.

Existen varias soluciones prácticas para esto, todas con sus propias ventajas y riesgos (por ejemplo, bloqueos muertos). Vea el artículo de Wikipedia para una discusión y otros indicadores de información.

0

La forma más sencilla:

LOCK TABLES table1 WRITE, table2 WRITE, table3 WRITE 
-- check for record, insert if not exists, etc... 
UNLOCK TABLES 

Si la votación no se produce muchas veces por segundo, a continuación de lo anterior debería ser suficiente.

Las tablas InnoDB ofrecen transacciones, que también pueden ser útiles aquí. Otros ya lo han comentado, así que no entraré en detalles.

Alternativamente, podría resolverlo en el nivel de código mediante el uso de algún tipo de mutex de memoria compartida que deshabilita la ejecución simultánea de esa sección de código PHP.

Cuestiones relacionadas