Como obter inserção e / ou atualização do SQL para não bloquear a tabela inteira no MS SQL Server


13

Muito novato no trabalho do DB, então aprecie sua paciência com uma pergunta básica. Estou executando o SQL Server 2014 em minha máquina local e tenho uma tabela pequena e um aplicativo cliente básico para testar diferentes abordagens. Estou recebendo o que parece ser um bloqueio de tabela durante as instruções INSERT INTOe UPDATE. O cliente é um aplicativo ASP.NET com o seguinte código:

OleDbConnection cn = new OleDbConnection("Provider=SQLNCLI11; server=localhost\\SQLEXPRESS; Database=<my db>; user id=<my uid>; password=<my pwd>");
cn.Open();
OleDbTransaction tn = cn.BeginTransaction();
OleDbCommand cmd = new OleDbCommand("INSERT INTO LAYOUTSv2 (LAYOUTS_name_t, LAYOUTS_enabled_b, LAYOUTS_data_m) VALUES ('name', '-1', 'data')", cn, tn);
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT SCOPE_IDENTITY()";
int newkey = Decimal.ToInt32((decimal)cmd.ExecuteScalar());
Console.WriteLine("Created index " + newkey);
Thread.Sleep(15000);
tn.Commit();
tn = cn.BeginTransaction();
cmd.CommandText = "UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key='" + newkey + "'";
cmd.Transaction = tn;
cmd.ExecuteNonQuery();
Console.WriteLine("updated row");
Thread.Sleep(15000);
tn.Rollback();
cn.Close();

Eu corro esse código e, a partir do estúdio de gerenciamento, corro SELECT * FROM LAYOUTSv2. Nos dois casos em que o encadeamento do cliente é pausado (ou seja, antes da confirmação / reversão), a consulta SELECT trava até que a confirmação / reversão ocorra.

A tabela possui o campo LAYOUTS_key atribuído como a chave primária. Na janela de propriedades, mostra que é exclusivo e agrupado, com bloqueios de página e bloqueios de linha permitidos. A configuração de escalação de bloqueios para a tabela é Desativar ... Tentei as outras configurações disponíveis de Tabela e AUTO sem alterações. Eu tentei SELECT ... WITH (NOLOCK)e isso retorna um resultado imediatamente, mas como é bem recomendado aqui e em outros lugares, não é o que devo fazer. Eu tentei colocar a ROWLOCKdica nas declarações INSERTe UPDATE, mas nada mudou.

O comportamento que estou procurando é o seguinte: antes da confirmação de um INSERT, as consultas de outros threads lêem todas as linhas, exceto a que está sendo INSERTeditada. Antes de confirmar uma UPDATEconsulta de outros threads, leia a versão inicial da linha que está sendo UPDATEeditada. Existe alguma maneira de fazer isso? Se precisar fornecer outras informações para esclarecer meu caso de uso, entre em contato. Obrigado.


3
A propósito, WHERE LAYOUTS_key='" + newkey + "'é um completo não-não por vários motivos, incluindo injeção SQL, você deve usar consultas parametrizadas.
Martin Smith

1
@MartinSmith Obrigado pelo aviso sobre isso ... nunca ouvi falar de consultas parametrizadas ou ataques de injeção de SQL.
John Riehl

@JohnRiehl, re: ataques de injeção, imagine se seu usuário definir newkey" something';DELETE FROM LAYOUTSv2 --". Sua atualização seria concluída com êxito e depois esvaziaria a tabela porque o usuário manipulou a consulta inserindo um apóstrofo. Normalmente, uma consulta parametrizada se parece com a seguinte UDPATE LAYOUTSv2 SET LAYOUTS_enabled_b='-3' WHERE LAYOUTS_key=?, após a qual você atribui valor (es) separadamente ao ?(o parâmetro) no seu código.
Daniel Hutmacher

Respostas:


10

As chances são de que ele não esteja bloqueando a "tabela inteira".

Ele está bloqueando uma linha na tabela, mas suas SELECT * FROM LAYOUTSv2tentativas de ler a tabela inteira são necessariamente bloqueadas por esse bloqueio.

Para o caso de inserção, você pode apenas especificar a READPASTdica para ignorar a linha bloqueada - no entanto, isso não fornecerá o resultado desejado para o UPDATEcaso (ele ignorará a linha novamente e não lerá a versão inicial da linha).

Se você configurar o banco de dados para isolamento de captura instantânea confirmada por leitura, isso proporcionará o efeito desejado para os dois casos (às custas de um maior uso de tempdb)


Alterei "É lido o snapshot confirmado" para True e agora ele funciona perfeitamente sem a necessidade de dicas. Obrigado! Um acompanhamento ... Deixei "Permitir isolamento de instantâneo" definido como Falso ... está tudo bem? Obrigado.
John Riehl

@ JohnRiehl - Sim, se você não estiver usando explicitamente o SNAPSHOTmelhor para deixá-lo desativado e ativá-lo se você decidir posteriormente que isso seria útil para você.
Martin Smith

7

As instruções de inserção e atualização devem criar bloqueios no nível de linha. No entanto, quando o número de bloqueios em qualquer transação é de 5.000 ou mais, ocorre uma escalação de bloqueios e cria um bloqueio no nível da tabela. Por favor veja abaixo.

https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx


Não é relevante para esta questão, pois as instruções INSERT e UPDATE estão escrevendo uma única linha
Martin Smith
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.