¿Alguna forma de seleccionar sin causar bloqueo en MySQL?

126

Consulta:

SELECT COUNT(online.account_id) cnt from online;

Pero la tabla en línea también se modifica por un evento, por lo que con frecuencia puedo ver el bloqueo ejecutando show processlist.

¿Hay alguna gramática en MySQL que pueda hacer que la instrucción select no cause bloqueos?

Y he olvidado mencionar anteriormente que está en una base de datos esclava MySQL.

Después de agregar en my.cnf:transaction-isolation = READ-UNCOMMITTED el esclavo se encontrará con un error:

Error 'El registro binario no es posible. Mensaje: El nivel de transacción 'READ-UNCOMMITTED' en InnoDB no es seguro para el modo binlog 'STATEMENT' 'en la consulta

Entonces, ¿hay una manera compatible de hacer esto?

Dios mio
fuente
3
Para otros que encuentran esta pregunta y están teniendo dificultades con los bloqueos en sus tablas: cómo mySQL usa los bloqueos internamente depende del motor de almacenamiento. Lea la respuesta de @zombat a continuación.
Simon Forsberg

Respuestas:

169

Encontré un artículo titulado "MYSQL WITH NOLOCK"

https://web.archive.org/web/20100814144042/http://sqldba.org/articles/22-mysql-with-nolock.aspx

en MS SQL Server haría lo siguiente:

SELECT * FROM TABLE_NAME WITH (nolock)

y el equivalente MYSQL es

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;

EDITAR

Michael Mior sugirió lo siguiente (de los comentarios)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;
Jon Erickson
fuente
54
Solo una nota para futuros lectores que tal vez desee eliminar SESSIONy, por lo tanto, que el nivel de transacción se aplique solo a la próxima transacción. Luego, simplemente reemplace la tercera declaración anterior con COMMIT. Esto será un noop en este caso, pero tendrá el efecto secundario de finalizar la transacción y restablecer el nivel de aislamiento predeterminado.
Michael Mior
3
Solo una nota, ese enlace está muerto ... :(
longda
55
Lo siento, pero tengo que rechazar esta respuesta por no mencionar las diferencias muy importantes entre InnoDB y MyISAM aquí. Como se indicó anteriormente en @omg, esto funcionará para InnoDB pero no para las tablas MyISAM.
Simon Forsberg
44
@Craig Sin duda, es inexacto que MyISAM no está emitiendo bloqueos READ durante las consultas SELECT - no son cerraduras y oponiéndose a InnoDB esos bloqueos son bloqueos de tabla, el bloqueo de todos los bloqueos ESCRIBIR solicitados y todas las consultas posteriores durante la ejecución. La pregunta original parece ser de InnoDB y aunque los niveles de aislamiento son inexistentes para MyISAM también - la documentación para la SET TRANSACTIONdeclaración de estado: "Esta declaración establece el nivel de aislamiento, que se utiliza para las operaciones en las tablas InnoDB".
syneticon-dj
1
Punto concedido. :-) Realmente estaba tratando de referirme al comportamiento de bloqueo de MyISAM vs InnoDB. Estas soluciones basadas en el nivel de aislamiento no se aplican a MyISAM, que no es transaccional, por lo que utiliza un bloqueo de tabla simple. MyISAM UPDATE y DELETE tienen que esperar a que se borre el bloqueo de la tabla, por lo que cualquier cola de SELECT subsiguiente detrás de la solicitud de escritura, bloqueada hasta que finalice la escritura. MyISAM no tiene "lecturas sucias" y no hay forma de permitir que la mayoría de las escrituras sucedan simultáneamente con las lecturas, por lo tanto, no tiene sentido quejarse de ningún comentario aquí "que no aborde MyISAM". Creo que a eso me refería. :-)
Craig
24

Si la tabla es InnoDB, consulte http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html : utiliza la lectura consistente (modo sin bloqueo) para los SELECT "que hacen no especifique FOR UPDATE o LOCK IN SHARE MODE si la opción innodb_locks_unsafe_for_binlog está establecida y el nivel de aislamiento de la transacción no está establecido en SERIALIZABLE. Por lo tanto, no se establecen bloqueos en las filas leídas de la tabla seleccionada ".

Alex Martelli
fuente
16

Utilizar

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Versión 5.0 Los documentos están aquí .

Versión 5.1 Los documentos están aquí .

Yo no
fuente
2
Gracias, creo que está cerca, pero ¿cuánto tiempo tendrá efecto esta declaración? Voy a utilizar esta declaración en un programa PHP, y debería ser mejor nivel de transacción AISLAMIENTO reajuste automáticamente una vez terminada la consulta
OMG
14

Es posible que desee leer esta página del manual de MySQL. La forma en que se bloquea una tabla depende del tipo de tabla que sea.

MyISAM usa bloqueos de tabla para lograr una velocidad de lectura muy alta, pero si tiene una instrucción UPDATE esperando, los futuros SELECTS se pondrán en cola detrás de UPDATE.

Las tablas de InnoDB usan bloqueo de nivel de fila, y no tendrá toda la tabla bloqueada detrás de una ACTUALIZACIÓN. Hay otro tipo de problemas de bloqueo asociados con InnoDB, pero es posible que se ajuste a sus necesidades.

zombat
fuente
1
¿"SET TRANSACTION ISOLATION LEAD READ UNCOMMITTED" funcionará para las tablas MyISAM?
omg
55
Las tablas MyISAM no admiten transacciones de ninguna forma. Se ejecutará una consulta transaccional en una tabla MyISAM, por lo que se ejecutará la consulta que mencione anteriormente, pero no tiene ningún efecto.
zombat
1
Entonces, ¿qué puedo hacer para evitar que SELECTS haga cola en el caso de MyISAM?
omg
66
¿Qué puedo hacer para evitar que SELECTS haga cola en el caso de MyISAM? Cambia a innodb. MyISAM utiliza bloqueos de nivel de tabla para cada consulta. Ese es un gran defecto.
Frank Farmer
2

Según el tipo de tabla, el bloqueo se realizará de manera diferente, pero también lo hará un recuento SELECT. Para las tablas MyISAM, una simple cuenta SELECT (*) FROM no debe bloquear la tabla ya que accede a los metadatos para extraer la cuenta del registro. Innodb tomará más tiempo ya que tiene que tomar la tabla en una instantánea para contar los registros, pero no debería causar el bloqueo.

Al menos debe tener concurrent_insert establecido en 1 (predeterminado). Luego, si no hay "huecos" en el archivo de datos para que se llene la tabla, se agregarán inserciones al archivo y SELECT e INSERT pueden ocurrir simultáneamente con las tablas MyISAM. Tenga en cuenta que la eliminación de un registro pone un "vacío" en el archivo de datos que intentará rellenarse con futuras inserciones y actualizaciones.

Si rara vez elimina registros, puede establecer concurrent_insert igual a 2, y las inserciones siempre se agregarán al final del archivo de datos. Luego, las selecciones e inserciones pueden suceder simultáneamente, pero su archivo de datos nunca será más pequeño, sin importar cuántos registros elimine (excepto todos los registros).

En pocas palabras, si tiene muchas actualizaciones, inserciones y selecciones en una tabla, debe convertirla en InnoDB. Sin embargo, puede mezclar libremente tipos de tablas en un sistema.

Brent Baisley
fuente
1

Otra forma de habilitar la lectura sucia en mysql es agregar una pista: BLOQUEO EN MODO COMPARTIR

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 
PuGong
fuente
"Si la confirmación automática se establece en 1, las cláusulas LOCK IN SHARE MODE y FOR UPDATE no tienen efecto". ... y autocommit = 1 es el predeterminado
Martin Zvarík
0

De esta referencia:

Si adquiere un bloqueo de tabla explícitamente con LOCK TABLES, puede solicitar un bloqueo READ LOCAL en lugar de un bloqueo READ para permitir que otras sesiones realicen inserciones concurrentes mientras tiene la tabla bloqueada.

Paul Sonier
fuente
0

Los SELECT normalmente no realizan ningún bloqueo que le interese en las tablas de InnoDB. El nivel de aislamiento de transacción predeterminado significa que las selecciones no bloquean cosas.

Por supuesto, la contienda todavía sucede.

MarkR
fuente
1
Sé que esta publicación es antigua, pero esta respuesta es demasiado general y solo a veces es cierta. Ver dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html . Los bloqueos ciertamente se adquieren para las lecturas, según el nivel de aislamiento. Específicamente, en este caso, el póster está tratando con bases de datos replicadas y declaró explícitamente que puede usar show processlistpara ver realmente las cerraduras. Por lo tanto, es seguro asumir que, de hecho, se están bloqueando.
Craig
1
La respuesta siempre es cierta. Por supuesto, hay algunos bloqueos, algunas mutex internas dentro de innodb que se utilizan (mutex de agrupación de almacenamiento intermedio innodb, por ejemplo). La mayoría de los usuarios no se preocupan o notan estos bloqueos y normalmente solo compiten durante las operaciones DDL (por ejemplo, si tiene un grupo de búferes de 16G y hace "caída de la tabla" en otro hilo). Pero no requiere ningún bloqueo de fila por defecto. A eso me refería. Sin embargo, la respuesta fue bastante vaga.
MarkR
1
Siempre siempre? ¿Qué sucede si el nivel de aislamiento de la transacción se establece en serializable o si la instrucción select usa el MODO BLOQUEO COMPARTIDO y la confirmación automática está desactivada? Sé que muchos servidores de bases de datos (¿la mayoría / todos?) Ahora usan el aislamiento de instantáneas de forma predeterminada en lugar de la verdadera serialización, pero ¿aún no hay justificaciones ocasionales para forzar las lecturas serializables? Pero parece que estabas diciendo que en todos los casos remotos normales, las condiciones predeterminadas en MySQL no causan bloqueos de lectura que afecten a otros hilos, así que no te preocupes por un problema que no tienes. Traté de deshacer mi voto negativo, por cierto. Lo siento ...
Craig
3
Dije "normalmente no". Me refiero a que si haces una selección normal (sin FOR UPDATE o LOCK IN SHARE MODE) y usas el nivel de aislamiento de transacción predeterminado. Hay algunos casos válidos para cambiar el nivel de aislamiento, pero solo lo haría por sesión, nunca por defecto.
MarkR