Risolvere gli errori di connessione temporanei nel database SQL e in Istanza gestita di SQL

Si applica a: Database SQL di Azure Istanza gestita di SQL di Azure Azure Synapse Analytics

Questo articolo descrive come evitare, risolvere, diagnosticare e attenuare gli errori di connessione e gli errori temporanei riscontrati dall'applicazione client quando interagisce con Azure SQL Database, Istanza gestita di SQL di Azure e Azure Synapse Analytics. Informazioni su come configurare la logica di ripetizione dei tentativi, compilare la stringa di connessione e modificare altre impostazioni di connessione.

Errori temporanei

Un errore temporaneo è un errore la cui causa sottostante si risolverà automaticamente in modo rapido. Una causa occasionale di errori temporanei è costituita dal cambio rapido di risorse hardware da parte del sistema Azure per ottenere un bilanciamento migliore dei diversi carichi di lavoro. La maggior parte di questi eventi di riconfigurazione viene completata in meno di 60 secondi. Durante questo intervallo di tempo di riconfigurazione, potrebbero verificarsi problemi di connessione al database in database SQL. Le applicazioni che si connettono al database devono essere sviluppate in modo da prevedere tali errori temporanei. Per gestirli, implementare la logica di ripetizione nel codice anziché mostrarli agli utenti come errori dell'applicazione.

Se il programma client usa ADO.NET, l'errore temporaneo verrà segnalato al programma tramite la generazione di un'eccezione SqlException.

Connessione e comando

Ripetere l'database SQL e Istanza gestita di SQL connessione o stabilire di nuovo la connessione, a seconda delle condizioni seguenti:

  • Si verifica un errore temporaneo durante un tentativo di connessione

Attendere alcuni secondi e quindi tentare nuovamente la connessione.

  • Si verifica un errore temporaneo durante un comando di query di database SQL e di Istanza gestita di SQL

Non ripetere immediatamente il comando. È invece consigliabile stabilire una nuova connessione dopo un breve intervallo di tempo. Quindi eseguire di nuovo il comando.

Logica di ripetizione dei tentativi per errori temporanei

I programmi client in cui occasionalmente si verifica un errore temporaneo sono più affidabili se contengono una logica di ripetizione dei tentativi. Quando il programma comunica con il database in database SQL tramite middleware di terze parti, chiedere al fornitore se il middleware contiene la logica di ripetizione dei tentativi per gli errori temporanei.

Principi per la ripetizione dei tentativi

  • Se l'errore è temporaneo, provare a ristabilire la connessione.
  • Non ripetere direttamente un'istruzione database SQL o Istanza gestita di SQL SELECT non riuscita con un errore temporaneo. Stabilire invece una nuova connessione e quindi provare a eseguire di nuovo l'istruzione SELECT.
  • Quando un'istruzione database SQL o Istanza gestita di SQL UPDATE ha esito negativo con un errore temporaneo, stabilire una nuova connessione prima di ripetere l'operazione UPDATE. La logica di ripetizione dei tentativi deve assicurare il completamento dell'intera transazione di database o il rollback dell'intera transazione.

Altre considerazioni per la ripetizione dei tentativi

  • Un programma batch avviato automaticamente dopo l'orario di lavoro e con completamento previsto prima del mattino può permettersi di attendere a lungo tra i diversi tentativi.
  • Un programma di interfaccia utente deve tenere conto della tendenza degli utenti a desistere dopo un'attesa troppo lunga. La soluzione non deve prevedere nuovi tentativi con intervalli di pochi secondi, perché un criterio simile può inondare il sistema con un numero eccessivo di richieste.

Incremento dell'intervallo tra i tentativi

È consigliabile attendere 5 secondi prima di riprovare. Al primo tentativo con un ritardo inferiore a 5 secondi, si rischia di sovraccaricare il servizio cloud. Per ogni tentativo successivo, aumentare in modo esponenziale il ritardo, fino a un massimo di 60 secondi.

Per una descrizione del periodo di blocco per i client che usano ADO.NET, vedere Pool di connessioni (ADO.NET).

È anche possibile che si voglia impostare un numero massimo di nuovi tentativi prima dell'autoterminazione del programma.

Esempi di codice con logica di ripetizione dei tentativi

Esempi di codice di logica di ripetizione dei tentativi sono disponibili in:

Eseguire test sulla logica di ripetizione tentativi

Per testare la logica di ripetizione dei tentativi, è necessario simulare o provocare un errore che può essere corretto mentre il programma è ancora in esecuzione.

Eseguire il test mediante la disconnessione dalla rete

Uno dei modi per testare la logica di ripetizione dei tentativi consiste nel disconnettere il computer client dalla rete mentre il programma è in esecuzione. L'errore è:

  • SqlException.Number = 11001
  • Messaggio: "Host sconosciuto"

Come parte del primo tentativo di ripetizione, è possibile riconnettere il computer client alla rete e quindi tentare di connettersi.

Per semplificare il test, disconnettere il computer dalla rete prima di avviare il programma. Il programma riconoscerà quindi un parametro di runtime che ha le conseguenze seguenti sul programma:

  • Aggiunta temporanea di 11001 al rispettivo elenco di errori da considerare temporanei.
  • Tentativo della prima connessione come di consueto.
  • Dopo il rilevamento dell'errore, rimozione di 11001 dall'elenco.
  • Visualizzazione di un messaggio che richiede all'utente di connettere il computer alla rete.
  • Sospensione delle ulteriori esecuzioni con il metodo Console.ReadLine o una finestra di dialogo con un pulsante OK. L'utente preme il tasto INVIO dopo la connessione del computer alla rete.
  • Nuovo tentativo di connessione, con esito positivo previsto.

Eseguire il test con errori di ortografia del nome utente durante la connessione

Il programma può intenzionalmente digitare in modo errato il nome utente prima del primo tentativo di connessione. L'errore è:

  • SqlException.Number = 18456
  • Messaggio: "Accesso non riuscito per l'utente 'WRONG_MyUserName'."

Come parte del primo tentativo, il programma può correggere l'errore di digitazione e quindi provare a connettersi.

Per semplificare il test, il programma riconosce un parametro di runtime che ha le conseguenze seguenti sul programma:

  • Aggiunta temporanea di 18456 al rispettivo elenco di errori da considerare temporanei.
  • Aggiunta intenzionale di 'WRONG_' al nome utente.
  • Dopo il rilevamento dell'errore, rimozione di 18456 dall'elenco.
  • Rimozione di 'WRONG_' dal nome utente.
  • Nuovo tentativo di connessione, con esito positivo previsto.

Parametri di SqlConnection di .NET per nuovi tentativi di connessione

Se il programma client si connette al database in database SQL usando la classe .NET Framework System.Data.SqlClient.SqlConnection, usare .NET 4.6.1 o versione successiva (o .NET Core) in modo da poter usare la funzionalità di ripetizione dei tentativi di connessione. Per altre informazioni sulla funzionalità, vedere Proprietà SqlConnection.ConnectionString.

Quando si crea la stringa di connessione per l'oggetto SqlConnection, coordinare i valori tra i parametri seguenti:

  • ConnectRetryCount: il valore predefinito è 1. L'intervallo consentito è tra 0 e 255.
  • ConnectRetryInterval: il valore predefinito è 10 secondi. L'intervallo consentito è tra 1 e 60.
  • Timeout connessione: il valore predefinito è 15 secondi. L'intervallo consentito è tra 0 e 2147483647.

In particolare, i valori scelti devono rendere vera l'uguaglianza seguente: Timeout di connessione = ConnectRetryCount * ConnectionRetryInterval

Ad esempio, se il conteggio è uguale a 3 e l'intervallo è uguale a 10 secondi, un timeout di soli 29 secondi non concede al sistema tempo sufficiente per il terzo tentativo finale di connessione: 29 < 3 * 10.

Connessione e comando

I parametri ConnectRetryCount e ConnectRetryInterval consentono all'oggetto SqlConnection di ripetere l'operazione di connessione senza interferire con il programma, ad esempio per restituire il controllo al programma. I tentativi possono verificarsi nelle situazioni seguenti:

  • Chiamata al metodo SqlConnection.Open
  • Chiamata al metodo SqlConnection.Execute

È importante sottolineare che, se si verifica un errore temporaneo durante l'esecuzione della query, l'oggetto SqlConnection non ripete l'operazione di connessione e certamente non ritenta l'esecuzione della query. Prima di inviare la query per l'esecuzione, tuttavia, SqlConnection controlla rapidamente la connessione e, se viene rilevato un problema, SqlConnection ritenta l'operazione di connessione. Se il tentativo ha esito positivo, la query viene inviata per l'esecuzione.

Opportunità di combinare ConnectRetryCount con la logica di ripetizione dei tentativi nell'applicazione

Si supponga che l'applicazione disponga di una logica di ripetizione dei tentativi particolarmente avanzata, in cui l'operazione di connessione può essere ritentata fino a quattro volte. Se si aggiunge ConnectRetryInterval e ConnectRetryCount = 3 alla stringa di connessione, il numero dei tentativi aumenterà a 4 * 3 = 12 tentativi. Un numero così elevato di tentativi potrebbe non essere consigliabile.

Connessioni al database in database SQL

Connessione: stringa di connessione

La stringa di connessione necessaria per connettersi al database è leggermente diversa dalla stringa usata per connettersi a SQL Server. È possibile copiare la stringa di connessione per il database dal portale di Azure.

Ottenere la stringa di connessione dal portale di Azure

Usare il portale di Azure per ottenere la stringa di connessione necessaria al programma client per interagire con il database SQL di Azure.

  1. Selezionare Tutti i servizi>database SQL.

  2. Immettere il nome del database nella casella di testo di filtro vicino alla parte superiore sinistra del pannello Database SQL.

  3. Selezionare la riga per il database.

  4. Quando viene visualizzato il pannello del database, per una visualizzazione più chiara selezionare i pulsanti Riduci a icona per comprimere i pannelli usati per la ricerca e il filtro del database.

  5. Nel pannello del database selezionare Mostra stringhe di connessione del database.

  6. Copiare la stringa di connessione appropriata. Se ad esempio si prevede di usare la libreria di connessione ADO.NET, copiare la stringa appropriata dalla scheda ADO.NET.

    Copiare la stringa di connessione ADO per il database

  7. Modificare la stringa di connessione in base alle esigenze. Inserire la password nella stringa di connessione o rimuovere "@<servername>" dal nome utente se il nome utente o il nome server è troppo lungo.

  8. In un formato o un altro, incollare le informazioni sulla stringa di connessione nel codice del programma client.

Per altre informazioni, vedere Stringhe di connessione e file di configurazione.

Connessione: indirizzo IP

È necessario configurare database SQL per accettare la comunicazione dall'indirizzo IP del computer che ospita il programma client. Per impostare questa configurazione, modificare le impostazioni del firewall tramite il portale di Azure.

Se si dimentica di configurare l'indirizzo IP, il programma restituirà un messaggio di errore che indica la necessità dell'indirizzo IP.

  1. Accedere al portale di Azure.

  2. Nell'elenco a sinistra selezionare Tutti i servizi.

  3. Scorrere e selezionare SQL Server.

    Trovare il server di Database SQL di Azure nel portale di

  4. Nella casella di testo di filtro, iniziare a digitare il nome del server. Viene visualizzata la riga.

  5. Selezionare la riga per il server. Viene visualizzato un pannello per il server.

  6. Nel pannello del server, selezionare Impostazioni.

  7. Selezionare Firewall.

    Selezionare Firewall impostazioni >

  8. Selezionare Aggiungi IP client. Digitare un nome per la nuova regola nella prima casella di testo.

  9. Digitare i valori di indirizzo IP minimo e massimo per l'intervallo che si desidera abilitare.

    • Può essere utile impostare come fine del valore minimo .0 e come fine del valore massimo su .255.
  10. Selezionare Salva.

Per altre informazioni, vedere Configurare le impostazioni del firewall in database SQL.

Connessione: porte

In genere è sufficiente assicurarsi che soltanto la porta 1433 sia aperta per le comunicazioni in uscita sul computer che ospita il programma client.

Ad esempio, se il programma client è ospitato in un computer Windows, è possibile usare Windows Firewall nell'host per aprire la porta 1433.

  1. Aprire il Pannello di controllo.
  2. Selezionare Tutte le Pannello di controllo elementi>impostazioni avanzate di>Windows Firewall>Azioni regole>> in uscitaNuova regola.

Se il programma client si trova su una macchina virtuale (VM) di Azure, leggere Porte superiori a 1433 per ADO.NET 4.5 e il database SQL.

Per informazioni di base sulla configurazione delle porte e degli indirizzi IP nel database, vedere Azure SQL Firewall del database.

Connessione: ADO.NET 4.6.2 o versioni successive

Se il programma usa classi ADO.NET come System.Data.SqlClient.SqlConnection per la connessione al database SQL, è consigliabile usare .NET Framework 4.6.2 o versioni successive.

A partire da ADO.NET 4.6.2

  • Il tentativo di apertura della connessione viene ritentato immediatamente per Azure SQL, migliorando così le prestazioni delle app abilitate per il cloud.

A partire da ADO.NET 4.6.1

  • Per il database SQL, è possibile migliorare l'affidabilità aprendo una connessione con il metodo SqlConnection.Open. Il metodo Open incorpora ora meccanismi di ripetizione dei tentativi di tipo "massimo sforzo" in risposta agli errori temporanei, per alcuni errori entro l'intervallo di durata della connessione.
  • È supportato il pool di connessioni, inclusa una verifica efficiente del corretto funzionamento dell'oggetto connessione fornito al programma.

Quando si usa un oggetto connessione da un pool di connessioni, è consigliabile che il programma chiuda temporaneamente la connessione se questa non deve essere usata immediatamente. Riaprire una connessione non ha alcun costo, a differenza della creazione di una nuova connessione.

Se si usa ADO.NET 4.0 o versioni precedenti, è consigliabile eseguire l'aggiornamento alla versione più recente di ADO.NET. A partire da agosto 2018, è possibile scaricare ADO.NET 4.6.2.

Diagnostica

Diagnostica: verificare se le utilità si possono connettere

Se il programma non riesce a connettersi al database in database SQL, un'opzione di diagnostica consiste nel provare a connettersi con un programma di utilità. Idealmente l'utilità si connette mediante la stessa libreria usata dal programma.

In qualsiasi computer Windows è possibile provare queste utilità:

  • SQL Server Management Studio (ssms.exe), che si connette tramite ADO.NET
  • sqlcmd.exe, che si connette tramite ODBC

Dopo la connessione del programma, verificare il funzionamento di una breve query SQL SELECT.

Diagnostica: verificare le porte aperte

Se si ritiene che i tentativi di connessione abbiano esito negativo a causa di problemi di porta, è possibile eseguire sul computer un'utilità che segnala le configurazioni delle porte.

In Linux possono risultare utili le utilità seguenti:

  • netstat -nap
  • nmap -sS -O 127.0.0.1: modificare il valore di esempio con il proprio indirizzo IP.

In Windows è possibile usare l'utilità PortQry.exe. Di seguito è riportato un esempio di esecuzione che ha eseguito una query sulla situazione della porta in un database in database SQL e che è stato eseguito in un computer portatile:

[C:\Users\johndoe\]
>> portqry.exe -n johndoesvr9.database.windows.net -p tcp -e 1433

Querying target system called: johndoesvr9.database.windows.net

Attempting to resolve name to IP address...
Name resolved to 23.100.117.95

querying...
TCP port 1433 (ms-sql-s service): LISTENING

[C:\Users\johndoe\]
>>

Diagnostica: registrare gli errori

La diagnosi di un problema intermittente è spesso agevolata dal rilevamento di uno schema generale nel corso di giorni o settimane.

Il client può supportare l'analisi tramite la registrazione di tutti gli errori rilevati. È possibile che si riesca a correlare le voci del log con i dati di errore registrati internamente dal database SQL.

Enterprise Library 6 (EntLib60) offre classi .NET gestite per semplificare la registrazione. Per altre informazioni, vedere 5 - Più facile che mai: uso del blocco applicazione di registrazione.

Diagnostica: cercare errori nei log di sistema

Ecco alcune istruzioni Transact-SQL SELECT che eseguono query nei log degli errori e alla ricerca di altre informazioni.

Query di un log Descrizione
SELECT e.*
FROM sys.event_log AS e
WHERE e.database_name = 'myDbName'
AND e.event_category = 'connectivity'
AND 2 >= DateDiff
  (hour, e.end_time, GetUtcDate())
ORDER BY e.event_category,
  e.event_type, e.end_time;
La visualizzazione sys.event_log offre informazioni sui singoli eventi, inclusi quelli che possono causare errori temporanei o di connettività.

In teoria, è possibile correlare i valori start_time o end_time con le informazioni relative al momento in cui si sono verificati problemi nel programma client.

È necessario connettersi al database master per eseguire questa query.
SELECT c.*
FROM sys.database_connection_stats AS c
WHERE c.database_name = 'myDbName'
AND 24 >= DateDiff
  (hour, c.end_time, GetUtcDate())
ORDER BY c.end_time;
La visualizzazione sys.database_connection_stats offre conteggi aggregati dei tipi di evento per la diagnostica aggiuntiva.

È necessario connettersi al database master per eseguire questa query.

Diagnostica: cercare eventi relativi a problemi nel log del database SQL

È possibile cercare voci relative agli eventi problematici nel log del database SQL. Provare a eseguire l'istruzione Transact-SQL SELECT seguente nel database master :

SELECT
   object_name
  ,CAST(f.event_data as XML).value
      ('(/event/@timestamp)[1]', 'datetime2')                      AS [timestamp]
  ,CAST(f.event_data as XML).value
      ('(/event/data[@name="error"]/value)[1]', 'int')             AS [error]
  ,CAST(f.event_data as XML).value
      ('(/event/data[@name="state"]/value)[1]', 'int')             AS [state]
  ,CAST(f.event_data as XML).value
      ('(/event/data[@name="is_success"]/value)[1]', 'bit')        AS [is_success]
  ,CAST(f.event_data as XML).value
      ('(/event/data[@name="database_name"]/value)[1]', 'sysname') AS [database_name]
FROM
  sys.fn_xe_telemetry_blob_target_read_file('el', null, null, null) AS f
WHERE
  object_name != 'login_event'  -- Login events are numerous.
  and
  '2015-06-21' < CAST(f.event_data as XML).value
        ('(/event/@timestamp)[1]', 'datetime2')
ORDER BY
  [timestamp] DESC
;

Alcune righe restituite da sys.fn_xe_telemetry_blob_target_read_file

Nell'esempio seguente viene illustrato il possibile aspetto di una riga restituita. I valori Null mostrati sono spesso non Null in altre righe.

object_name                   timestamp                    error  state  is_success  database_name

database_xml_deadlock_report  2015-10-16 20:28:01.0090000  NULL   NULL   NULL        AdventureWorks

Enterprise Library 6

Enterprise Library 6 (EntLib60) è un framework di classi .NET che consentono di implementare client affidabili di servizi cloud, uno dei quali è database SQL. Gli argomenti dedicati a ogni area per cui EntLib60 può risultare utile sono disponibili in Enterprise Library 6 - Aprile 2013.

Logica di ripetizione dei tentativi per la gestione degli errori temporanei è un'area in cui EntLib60 può essere utile. Per altre informazioni, vedere 4 - Perseveranza, il segreto di tutti i successi: uso del Blocco di applicazioni per la gestione degli errori temporanei.

Nota

Il codice sorgente per EntLib60 è disponibile per il download pubblico nell'Area download. Microsoft non prevede di fornire altre funzionalità o aggiornamenti di manutenzione per EntLib.

Classi di EntLib60 per errori temporanei e ripetizione dei tentativi

Le classi seguenti di EntLib60 sono particolarmente utili per la logica di ripetizione dei tentativi. Tutte queste classi sono disponibili nello spazio dei nomi Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling o nei livelli sottostanti.

Nello spazio dei nomi Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling:

  • RetryPolicy
    • ExecuteAction
  • Classe ExponentialBackoff
  • SqlDatabaseTransientErrorDetectionStrategy
  • ReliableSqlConnection
    • ExecuteCommand

Nello spazio dei nomi Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.TestSupport:

  • AlwaysTransientErrorDetectionStrategy
  • NeverTransientErrorDetectionStrategy

Ecco alcuni collegamenti alle informazioni relative a EntLib60:

EntLib60: il blocco di registrazione

  • Il blocco di registrazione è una soluzione a flessibilità e configurabilità elevata che consente di:
    • Creare e archiviare messaggi di log in diverse posizioni.
    • Classificare e filtrare i messaggi.
    • Raccogliere informazioni contestuali utili per il debug e la traccia, oltre che per i requisiti di controllo e di registrazione generale.
  • Il blocco di registrazione astrae la funzionalità di registrazione dalla destinazione di registrazione, in modo che il codice applicazione sia coerente, indipendentemente dalla posizione e dal tipo di archivio di registrazione di destinazione.

Per altre informazioni, vedere 5 - Più facile che mai: uso del blocco applicazione di registrazione.

Codice sorgente del metodo IsTransient di EntLib60

La classe SqlDatabaseTransientErrorDetectionStrategy include anche il codice sorgente C# per il metodo IsTransient. Il codice sorgente chiarisce gli errori considerati temporanei e idonei alla ripetizione dei tentativi, a partire da aprile 2013.

public bool IsTransient(Exception ex)
{
  if (ex != null)
  {
    SqlException sqlException;
    if ((sqlException = ex as SqlException) != null)
    {
      // Enumerate through all errors found in the exception.
      foreach (SqlError err in sqlException.Errors)
      {
        switch (err.Number)
        {
            // SQL Error Code: 40501
            // The service is currently busy. Retry the request after 10 seconds.
            // Code: (reason code to be decoded).
          case ThrottlingCondition.ThrottlingErrorNumber:
            // Decode the reason code from the error message to
            // determine the grounds for throttling.
            var condition = ThrottlingCondition.FromError(err);

            // Attach the decoded values as additional attributes to
            // the original SQL exception.
            sqlException.Data[condition.ThrottlingMode.GetType().Name] =
              condition.ThrottlingMode.ToString();
            sqlException.Data[condition.GetType().Name] = condition;

            return true;

          case 10928:
          case 10929:
          case 10053:
          case 10054:
          case 10060:
          case 40197:
          case 40540:
          case 40613:
          case 40143:
          case 233:
          case 64:
            // DBNETLIB Error Code: 20
            // The instance of SQL Server you attempted to connect to
            // does not support encryption.
          case (int)ProcessNetLibErrorCode.EncryptionNotSupported:
            return true;
        }
      }
    }
    else if (ex is TimeoutException)
    {
      return true;
    }
    else
    {
      EntityException entityException;
      if ((entityException = ex as EntityException) != null)
      {
        return this.IsTransient(entityException.InnerException);
      }
    }
  }

  return false;
}

Passaggi successivi

Vedere anche