Jak uzyskać efektywną obsługę SQL Server w C# z ADO?

Mam klasę "Database", która działa jako wrapper dla ADO.net na przykład, gdy muszę wykonać procedurę, dzwonię do bazy danych.ExecuteProcedure (nazwa procedury, parametryi wartości).

W SQL Server 2000 występują poważne problemy z blokadą. Część naszego zespołu pracuje nad kodem sql i transakcjami, aby zminimalizować te zdarzenia, ale myślę o uczynieniu tej klasy bazy danych odporną na sytuacje impasowe.

Chcemy impasu ofiara spróbować ponownie może po pewnym czasie opóźnienia, ale nie wiem, czy jest to możliwe. Oto kod metody, której używamy:

public int ExecuteQuery(string query)
{
    int rows = 0;

    try
    {
        Command.Connection = Connection;
        Command.CommandType = CommandType.Text;

        if(DatabaseType != enumDatabaseType.ORACLE)
          Command.CommandText = query;
        else
          Command.CommandText ="BEGIN " +  query + " END;";



        if (DatabaseType != enumDatabaseType.SQLCOMPACT)
            Command.CommandTimeout = Connection.ConnectionTimeout;

        if (Connection.State == ConnectionState.Closed)
            Connection.Open();

        rows = Command.ExecuteNonQuery();
    }
    catch (Exception exp)
    {
        //Could I add here any code to handle it?
        throw new Exception(exp.Message);
    }
    finally
    {
        if (Command.Transaction == null)
        {
            Connection.Close();
            _connection.Dispose();
            _connection = null;
            Command.Dispose();
            Command = null;
        }
    }
    return rows;
}

Czy Mogę to zrobić wewnątrz bloku?

Author: redcalx, 2008-11-26

4 answers

Po Pierwsze, chciałbym przejrzeć mój kod SQL 2000 i dostać się do dołu, dlaczego ten impas się dzieje. Naprawienie tego może być ukrywanie większego problemu (np. brakujący indeks lub złe zapytanie).

Po drugie chciałbym przejrzeć moją architekturę, aby potwierdzić, że stwierdzenie deadlocking naprawdę musi być wywoływane tak często (czy select count(*) from bob musi być wywoływane 100 razy na sekundę?).

Jeśli jednak naprawdę potrzebujesz wsparcia i nie masz błędów w SQL lub architekturze, spróbuj czegoś wzdłuż kolejne linie. (Uwaga: musiałem użyć tej techniki dla systemu obsługującego tysiące zapytań na sekundę i dość rzadko trafiał w impas)

int retryCount = 3;
bool success = false;  
while (retryCount > 0 && !success) 
{
  try
  {
     // your sql here
     success = true; 
  } 
  catch (SqlException exception)
  {
     if (exception.Number != 1205)
     {
       // a sql exception that is not a deadlock 
       throw; 
     }
     // Add delay here if you wish. 
     retryCount--; 
     if (retryCount == 0) throw;
  }
}
 32
Author: Sam Saffron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-14 11:08:48

Bazując na odpowiedzi @ Sam, przedstawiam metodę retry wrapper ogólnego przeznaczenia:

private static T Retry<T>(Func<T> func)
{
    int count = 3;
    TimeSpan delay = TimeSpan.FromSeconds(5);
    while (true)
    {
        try
        {
            return func();
        }
        catch(SqlException e)
        {
            --count;
            if (count <= 0) throw;

            if (e.Number == 1205)
                _log.Debug("Deadlock, retrying", e);
            else if (e.Number == -2)
                _log.Debug("Timeout, retrying", e);
            else
                throw;

            Thread.Sleep(delay);
        }
    }
}

private static void Retry(Action action)
{
    Retry(() => { action(); return true; });
}

// Example usage
protected static void Execute(string connectionString, string commandString)
{
    _log.DebugFormat("SQL Execute \"{0}\" on {1}", commandString, connectionString);

    Retry(() => {
        using (SqlConnection connection = new SqlConnection(connectionString))
        using (SqlCommand command = new SqlCommand(commandString, connection))
            command.ExecuteNonQuery();
    });
}

protected static T GetValue<T>(string connectionString, string commandString)
{
    _log.DebugFormat("SQL Scalar Query \"{0}\" on {1}", commandString, connectionString);

    return Retry(() => { 
        using (SqlConnection connection = new SqlConnection(connectionString))
        using (SqlCommand command = new SqlCommand(commandString, connection))
        {
            object value = command.ExecuteScalar();
            if (value is DBNull) return default(T);
            return (T) value;
        }
    });
}
 20
Author: Neil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-14 11:13:50

Jeśli impas można rozwiązać na warstwie danych, to zdecydowanie jest to droga do zrobienia. Blokowanie podpowiedzi, przeprojektowanie sposobu działania modułu i tak dalej. NoLock nie jest jednak panaceum-czasami nie jest możliwe użycie ze względu na integralność transakcji i miałem przypadki prostych (choć złożonych) odczytów danych ze wszystkimi odpowiednimi tabelami NoLock ' D, które nadal powodowały bloki na innych zapytaniach.

W każdym razie-jeśli nie możesz tego rozwiązać w warstwie danych z jakiegokolwiek powodu, jak o

bool OK = false;
Random Rnd = new Random();

while(!OK)
{
    try
    {
        rows = Command.ExecuteNonQuery();
        OK = true;
    }
    catch(Exception exDead)
    {
        if(exDead.Message.ToLower().Contains("deadlock"))
            System.Threading.Thread.Sleep(Rnd.Next(1000, 5000));
        else
            throw exDead;
    }
}
 5
Author: Rahul Hendawe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 06:23:47

Jeśli masz problemy z blokadami, lepiej przyjrzeć się, co robi kod SQL. Na przykład blokady eskalacji blokad są bardzo łatwe do utworzenia, jeśli masz serializowalny poziom izolacji (lub jakikolwiek odpowiednik w rdbms) - i mogą być złagodzone na kilka sposobów, takich jak ponowne zamawianie zapytań lub (przynajmniej w SQL Server) za pomocą (UPDLOCK) do wcześniejszego usunięcia blokady zapisu (więc nie otrzymasz konkurencyjnej blokady odczytu).

Ponowne próby będą mieszane... na na przykład, jeśli znajdujesz się w TransactionScope, może on już zostać przerwany. Ale tylko na poziomie purystycznym-jeśli będę miał problemy z rozmową z db, chcę, aby mój kod wpadł w panikę i spanikował wcześniej... re-try wydaje się nieco chwiejny w tym konkretnym scenariuszu.

 2
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-26 13:15:20