Pergunta

Temos um aplicativo que foi originalmente escrito como um aplicativo de desktop, e há muitos anos. Ele inicia uma transação sempre que você abre uma tela de edição e se compromete se você clicar em OK ou revirá se clicar em Cancelar. Isso funcionou bem para um aplicativo de desktop, mas agora estamos tentando mudar para o ADO.NET e o SQL Server, e as transações de longa duração são problemáticas.

Descobri que teremos um problema quando vários usuários estão tentando editar (diferentes subconjuntos de) a mesma tabela ao mesmo tempo. Em nosso banco de dados antigo, a transação de cada usuário adquiriria bloqueios de nível recorde para todos os registros que modificaram durante sua transação; Como usuários diferentes estavam editando diferentes registros, todos recebem suas próprias bloqueios e tudo funciona. Mas no SQL Server, assim que um usuário edita um registro dentro de uma transação, o SQL Server parece obter um bloqueio no Tabela inteira. Quando um segundo usuário tenta editar um registro diferente na mesma tabela, o aplicativo do segundo usuário simplesmente bloqueia, porque o SQLConnection bloqueia até que o primeiro usuário cometa ou volte.

Estou ciente de que as transações de longa data são ruins, e eu sei que o melhor A solução seria alterar essas telas para que não mantenham mais as transações abertas por um longo tempo. Mas como isso significaria algumas mudanças invasivas e arriscadas, também quero pesquisar se há uma maneira de colocar esse código em funcionamento como está, apenas para saber quais são minhas opções.

Como posso obter duas transações de usuários diferentes no SQL Server para bloquear registros individuais em vez de toda a tabela?

Aqui está um aplicativo de console rápido e sujo que ilustra o problema. Eu criei um banco de dados chamado "Test1", com uma tabela chamada "valores" que apenas possui colunas ID (int) e Value (Nvarchar). Se você executar o aplicativo, ele solicita um ID para modificar, inicia uma transação, modifica esse registro e deixa a transação aberta até pressionar Enter. Eu quero ser capaz de

  1. Inicie o programa e diga para atualizar o ID 1;
  2. Deixe -o obter sua transação e modificar o registro;
  3. Inicie uma segunda cópia do programa e peça para atualizar o ID 2;
  4. Consiga atualizar (e comprometer) enquanto a transação do primeiro aplicativo ainda está aberta.

Atualmente, ele congela na etapa 4, até eu voltar à primeira cópia do aplicativo e fechar ou pressionar Enter, para que ele se comprometa. A chamada para command.ExecutenonQuery bloqueia até que a primeira conexão seja fechada.

public static void Main()
{
    Console.Write("ID to update: ");
    var id = int.Parse(Console.ReadLine());
    Console.WriteLine("Starting transaction");
    using (var scope = new TransactionScope())
    using (var connection = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test1;Integrated Security=True"))
    {
        connection.Open();
        var command = connection.CreateCommand();
        command.CommandText = "UPDATE [Values] SET Value = 'Value' WHERE ID = " + id;
        Console.WriteLine("Updating record");
        command.ExecuteNonQuery();
        Console.Write("Press ENTER to end transaction: ");
        Console.ReadLine();
        scope.Complete();
    }
}

Aqui estão algumas coisas que eu já tentei, sem mudança de comportamento:

  • Alterar o nível de isolamento da transação para "ler não comprometido"
  • Especificando um "com (Rowlock)" na declaração de atualização
Foi útil?

Solução

Apenas verificando, mas você tem uma chave primária ou índice exclusivo na coluna ID?

Outras dicas

Observe o bloqueio otimista versus pessimista.

Edit: Artigo anterior vinculado ao clássico Ado ... desculpe.

http://msdn.microsoft.com/en-us/library/cs6hb8k4(vs.71).aspx

Provavelmente, o índice foi criado com bloqueios de linha definidos como "OFF".
"Com (Rowlock)" em uma consulta não teria efeito nesse caso.

Você pode ligá -los novamente com Índice de Alter, por exemplo:

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top