Alguma maneira de selecionar sem causar bloqueio no MySQL?

126

Inquerir:

SELECT COUNT(online.account_id) cnt from online;

Mas a tabela on-line também é modificada por um evento, com tanta frequência que consigo ver o bloqueio executando show processlist.

Existe alguma gramática no MySQL que possa fazer com que a instrução select não cause bloqueios?

E eu esqueci de mencionar acima que está em um banco de dados escravo do MySQL.

Depois que eu adicionei no my.cnf:transaction-isolation = READ-UNCOMMITTED escravo vai encontrar com erro:

Erro 'O registro binário não é possível. Mensagem: O nível de transação 'READ-UNCOMMITTED' no InnoDB não é seguro para o modo de log de bin 'STATEMENT' 'na consulta

Então, existe uma maneira compatível de fazer isso?

AMD
fonte
3
Para outras pessoas que encontram essa pergunta e estão tendo dificuldades com os bloqueios em suas tabelas: Como o mySQL usa bloqueios internamente depende do mecanismo de armazenamento. Leia a resposta de @zombat abaixo.
Simon Forsberg

Respostas:

169

Encontrou um artigo intitulado "MYSQL WITH NOLOCK"

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

No MS SQL Server, você faria o seguinte:

SELECT * FROM TABLE_NAME WITH (nolock)

e o equivalente MYSQL é

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

EDITAR

Michael Mior sugeriu o seguinte (dos comentários)

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED ;
SELECT * FROM TABLE_NAME ;
COMMIT ;
Jon Erickson
fonte
54
Apenas uma observação para futuros leitores que você deseja eliminar SESSIONe, portanto, o nível de transação se aplica apenas à próxima transação. Em seguida, basta substituir a terceira declaração acima por COMMIT. Nesse caso, será um noop, mas terá o efeito colateral de encerrar a transação e redefinir para o nível de isolamento padrão.
Michael Mior 04/04
3
Apenas uma nota, esse link está morto ... :(
longda
5
Desculpe, mas eu tenho que rebaixar esta resposta por não mencionar as diferenças muito importantes entre o InnoDB e o MyISAM aqui. Conforme afirmado por @omg acima, isso funcionará para o InnoDB, mas não para as tabelas MyISAM.
Simon Forsberg
4
@ Craig Certamente é imprecisa que MyISAM não está emitindo fechaduras lido durante consultas SELECT - não são fechaduras e opondo-se a InnoDB esses bloqueios são bloqueios de tabela, bloqueando todos os bloqueios de ESCRITA solicitados e todas as consultas subsequentes durante a execução. A pergunta original parece ser sobre o InnoDB e os níveis de isolamento também não existem para o MyISAM - os documentos para oSET TRANSACTION estado da instrução : "Esta declaração define o nível de isolamento da transação, usado para operações nas tabelas do InnoDB".
Syneticon-dj
1
Ponto concedido. :-) Eu estava realmente tentando me referir ao comportamento de bloqueio do MyISAM vs InnoDB. Essas soluções baseadas em nível de isolamento não se aplicam ao MyISAM, que não é transacional, por isso usa um bloqueio de tabela simples. O MyISAM UPDATE e DELETE precisa aguardar a limpeza do bloqueio da tabela, para que qualquer fila SELECT subsequente fique atrás da solicitação de gravação, bloqueada até que a gravação termine. O MyISAM não possui "leituras sujas" e nenhuma maneira de permitir que a maioria das gravações ocorra simultaneamente com as leituras, portanto, não adianta se incomodar com comentários aqui "que falhem em resolver o MyISAM". Eu acho que era nisso que eu estava chegando. :-)
Craig
24

Se a tabela for InnoDB, consulte http://dev.mysql.com/doc/refman/5.1/en/innodb-consistent-read.html - ela usa leitura consistente (modo sem bloqueio) para SELECTs "que fazem não especifique FOR UPDATE ou LOCK IN SHARE MODE se a opção innodb_locks_unsafe_for_binlog estiver configurada e o nível de isolamento da transação não estiver configurado como SERIALIZABLE. Portanto, nenhum bloqueio será definido nas linhas lidas da tabela selecionada ".

Alex Martelli
fonte
16

Usar

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED.

Os documentos da versão 5.0 estão aqui .

Os documentos da versão 5.1 estão aqui .

Eu não
fonte
2
Obrigado, acho que está próximo, mas quanto tempo essa declaração terá efeito? Vou usar esta instrução em um programa PHP e é melhor redefinir o TRANSACTION ISOLATION LEVEL automaticamente quando a consulta for concluída
omg
14

Você pode querer ler esta página do manual do MySQL. Como uma tabela é bloqueada depende de que tipo de tabela é.

O MyISAM usa bloqueios de tabela para obter uma velocidade de leitura muito alta, mas se você tiver uma instrução UPDATE aguardando, os SELECTS futuros ficarão na fila atrás do UPDATE.

As tabelas do InnoDB usam o bloqueio no nível da linha, e você não terá a tabela inteira bloqueada atrás de um UPDATE. Existem outros tipos de problemas de bloqueio associados ao InnoDB, mas você pode achar que ele se adapta às suas necessidades.

zombat
fonte
1
"SET TRANSACTION ISOLATION LEAD UNCOMMITTED" funcionará para tabelas MyISAM?
Omg
5
As tabelas MyISAM não suportam transações de nenhuma forma. Uma consulta transacional será executada em uma tabela MyISAM, portanto, a consulta mencionada acima será executada, mas não terá efeito.
Zombat 27/05/09
1
Então, o que posso fazer para evitar SELECTS na fila no caso do MyISAM?
Omg
6
o que posso fazer para evitar SELECTS na fila no caso do MyISAM? Mude para innodb. O MyISAM usa bloqueios no nível da tabela para todas as consultas. Essa é a sua principal falha.
Frank Farmer
2

Dependendo do tipo da sua mesa, o bloqueio terá um desempenho diferente, mas o mesmo ocorrerá com uma contagem SELECT. Para tabelas MyISAM, uma simples tabela SELECT count (*) FROM não deve bloquear a tabela, pois acessa metadados para obter a contagem de registros. O Innodb levará mais tempo, pois precisa pegar a tabela em um instantâneo para contar os registros, mas não deve causar o bloqueio.

Você deve pelo menos ter o concurrent_insert definido como 1 (padrão). Então, se não houver "lacunas" no arquivo de dados para a tabela preencher, as inserções serão anexadas ao arquivo e SELECT e INSERTs podem ocorrer simultaneamente com as tabelas MyISAM. Observe que a exclusão de um registro coloca uma "lacuna" no arquivo de dados que tentará ser preenchida com inserções e atualizações futuras.

Se você raramente excluir registros, poderá definir concurrent_insert como 2 e as inserções sempre serão adicionadas ao final do arquivo de dados. Em seguida, as seleções e inserções podem ocorrer simultaneamente, mas seu arquivo de dados nunca ficará menor, não importa quantos registros você exclua (exceto todos os registros).

A linha inferior, se você tem muitas atualizações, insere e seleciona em uma tabela, deve fazê-lo no InnoDB. Você pode combinar livremente tipos de tabela em um sistema.

Brent Baisley
fonte
1

Outra maneira de habilitar a leitura suja no mysql é adicionar uma dica: LOCK NO SHARE MODE

SELECT * FROM TABLE_NAME LOCK IN SHARE MODE; 
PuGong
fonte
"Se a confirmação automática estiver definida como 1, as cláusulas LOCK IN SHARE MODE e FOR UPDATE não terão efeito." ... e a confirmação automática = 1 é o padrão
Martin Zvarík
0

A partir desta referência:

Se você adquirir um bloqueio de tabela explicitamente com LOCK TABLES, poderá solicitar um bloqueio READ LOCAL em vez de um bloqueio READ para permitir que outras sessões realizem inserções simultâneas enquanto a tabela estiver bloqueada.

Paul Sonier
fonte
0

Os SELECTs normalmente não fazem nenhum bloqueio de seu interesse nas tabelas do InnoDB. O nível de isolamento da transação padrão significa que as seleções não bloqueiam coisas.

Claro que a disputa ainda acontece.

MarkR
fonte
1
Eu sei que este post é antigo, mas essa resposta é muito geral e às vezes é verdadeira. Consulte dev.mysql.com/doc/refman/5.0/en/innodb-locks-set.html . Certamente as fechaduras são adquiridas para leituras, dependendo do nível de isolamento. Especificamente, nesse caso, o pôster está lidando com bancos de dados replicados e afirmou explicitamente que ele pode usar show processlistpara realmente ver os bloqueios. Portanto, é seguro supor que, de fato, existem bloqueios sendo feitos.
Craig
1
A resposta é sempre verdadeira. Obviamente, existem alguns bloqueios - alguns mutexes internos dentro do innodb que são usados ​​(mutex do buffer pool do innodb, por exemplo). A maioria dos usuários não se preocupa ou percebe esses bloqueios, e eles normalmente contendem apenas durante operações DDL (como se você tiver um buffer pool 16G e "soltar tabela" em outro encadeamento). Mas não é necessário nenhum bloqueio de linha por padrão. Foi isso que eu quis dizer. A resposta foi bastante vaga.
MarkR
1
Sempre sempre? E se o nível de isolamento da transação estiver definido como serializável ou a instrução select usar LOCK IN SHARE MODE e a confirmação automática estiver desativada? Sei que muitos servidores de banco de dados (a maioria / todos?) Agora usam isolamento de instantâneo por padrão, em vez de serialização verdadeira, mas ainda não existem justificativas ocasionais para forçar leituras serializáveis? Mas parece que você estava dizendo que, em todos os casos remotamente normais, as condições padrão no MySQL não causam bloqueios de leitura que afetam outros threads; portanto, não se preocupe com um problema que você não tem? Tentei desfazer meu voto negativo, BTW. Desculpe ...
Craig
3
Eu disse "normalmente não". Eu quis dizer se você fizer uma seleção normal (sem FOR UPDATE ou LOCK IN SHARE MODE) e usar o nível de isolamento de transação padrão. Existem alguns casos válidos para alterar o nível de isolamento, mas eu o faria apenas por sessão, nunca o padrão.
precisa saber é