Solución:
Un truco sencillo que puede ayudar con la mayoría de los puntos muertos es ordenar las operaciones en un orden específico.
Obtiene un punto muerto cuando dos transacciones intentan bloquear dos bloqueos en órdenes opuestas, es decir:
- conexión 1: llave de bloqueo (1), llave de bloqueo (2);
- conexión 2: llave de bloqueo (2), llave de bloqueo (1);
Si ambos se ejecutan al mismo tiempo, la conexión 1 bloqueará la llave (1), la conexión 2 bloqueará la llave (2) y cada conexión esperará a que la otra libere la llave -> punto muerto.
Ahora, si cambió sus consultas de modo que las conexiones bloqueen las claves en el mismo orden, es decir:
- conexión 1: llave de bloqueo (1), llave de bloqueo (2);
- conexión 2: llave de cerraduras (1), bloquea la llave (2);
será imposible llegar a un punto muerto.
Entonces esto es lo que sugiero:
-
Asegúrese de no tener otras consultas que bloqueen el acceso a más de una tecla a la vez, excepto la declaración de eliminación. si lo hace (y sospecho que lo hace), ordene su DONDE en (k1, k2, .. kn) en orden ascendente.
-
Corrija su declaración de eliminación para que funcione en orden ascendente:
Cambio
DELETE FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
Para
DELETE FROM onlineusers
WHERE id IN (
SELECT id FROM onlineusers
WHERE datetime <= now() - INTERVAL 900 SECOND
ORDER BY id
) u;
Otra cosa a tener en cuenta es que la documentación de mysql sugiere que, en caso de un punto muerto, el cliente debería volver a intentarlo automáticamente. puede agregar esta lógica a su código de cliente. (Digamos, 3 reintentos en este error en particular antes de rendirse).
El interbloqueo ocurre cuando dos transacciones se esperan una a la otra para adquirir un bloqueo. Ejemplo:
- Tx 1: bloquear A, luego B
- Tx 2: bloquear B, luego A
Existen numerosas preguntas y respuestas sobre los interbloqueos. Cada vez que inserta / actualiza / elimina una fila, se adquiere un bloqueo. Para evitar un punto muerto, debe asegurarse de que las transacciones simultáneas no actualicen la fila en un orden que podría resultar en un punto muerto. Generalmente hablando, tratar de adquirir el candado siempre en el mismo orden incluso en transacciones diferentes (por ejemplo, siempre la tabla A primero, luego la tabla B).
Otra razón para el interbloqueo en la base de datos puede ser índices faltantes. Cuando se inserta / actualiza / elimina una fila, la base de datos necesita verificar las restricciones relacionales, es decir, asegurarse de que las relaciones sean consistentes. Para hacerlo, la base de datos debe verificar las claves externas en las tablas relacionadas. Eso podría como resultado, se adquiere otro bloqueo que no sea la fila que se modifica. Entonces, asegúrese de tener siempre un índice en las claves externas (y, por supuesto, las claves primarias), de lo contrario, podría resultar en un bloqueo de mesa en lugar de un bloqueo de fila. Si se produce un bloqueo de la tabla, la contención de bloqueo es mayor y aumenta la probabilidad de un punto muerto.
Es probable que la declaración de eliminación afecte a una gran fracción del total de filas de la tabla. Eventualmente, esto podría llevar a que se adquiera un bloqueo de tabla al eliminar. Aferrarse a un candado (en este caso, bloqueos de fila o página) y adquirir más bloqueos es siempre un riesgo de interbloqueo. Sin embargo, no puedo explicar por qué la declaración de inserción conduce a una escalada de bloqueo; podría tener que ver con la división / adición de páginas, pero alguien que conozca mejor MySQL tendrá que completarlo allí.
Para empezar, puede valer la pena intentar adquirir explícitamente un bloqueo de tabla de inmediato para la declaración de eliminación. Consulte BLOQUEAR TABLAS y Problemas de bloqueo de tablas.