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.

John Riehl
fonte
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)

Martin Smith
fonte
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

Suraj
fonte
Não é relevante para esta questão, pois as instruções INSERT e UPDATE estão escrevendo uma única linha
Martin Smith