Domanda

Aggiorna : sembra che la query non generi alcun timeout. La connessione sta scadendo.

Questo è un codice di esempio per l'esecuzione di una query. A volte, durante l'esecuzione di query che richiedono tempo, genera un'eccezione di timeout.

Non non posso utilizzare nessuna di queste tecniche: 1) Aumenta il timeout. 2) Eseguirlo in modo asincrono con un callback. Questo deve essere eseguito in modo sincrono.

suggerisci altre tecniche per mantenere viva la connessione mentre esegui una query che richiede tempo?

private static void CreateCommand(string queryString,
    string connectionString)
{
    using (SqlConnection connection = new SqlConnection(
               connectionString))
    {
        SqlCommand command = new SqlCommand(queryString, connection);
        command.Connection.Open();
        command.ExecuteNonQuery();
    }
}
È stato utile?

Soluzione

Poiché si utilizza ExecuteNonQuery che non restituisce alcuna riga, è possibile provare questo approccio basato sul polling. Esegue la query in modo asyc (senza callback) ma l'applicazione attenderà (all'interno di un ciclo while) fino al completamento della query. Da MSDN . Questo dovrebbe risolvere il problema del timeout. Per favore, provalo.

Ma, sono d'accordo con gli altri sul fatto che dovresti pensare di più sull'ottimizzazione della query per eseguire meno di 30 secondi.

        IAsyncResult result = command.BeginExecuteNonQuery();

        int count = 0;
        while (!result.IsCompleted)
        {
            Console.WriteLine("Waiting ({0})", count++);
            System.Threading.Thread.Sleep(1000);
        }
        Console.WriteLine("Command complete. Affected {0} rows.",
        command.EndExecuteNonQuery(result));

Altri suggerimenti

Dovresti prima controllare la tua query per vedere se è ottimizzata e non è in qualche modo in esecuzione su indici mancanti. 30 secondi è assegnato per la maggior parte delle query , anche su database di grandi dimensioni se sono correttamente ottimizzati. Se disponi di solide prove utilizzando il piano di query che la query non può essere eseguita più velocemente di quella, allora dovresti aumentare il timeout, non c'è altro modo per mantenere la connessione, questo è lo scopo del timeout per terminare la connessione se il la query non viene completata in tale intervallo di tempo.

Sono d'accordo con Terrapin.

Hai alcune opzioni su come ridurre il tempo. Innanzitutto, se la tua azienda impiega DBA, ti consiglio di chiedere loro suggerimenti.

Se questa non è un'opzione o se vuoi prima provare altre cose, ecco le tre opzioni principali:

  1. Suddividi la query in componenti eseguiti in timeout. Questo è probabilmente il più semplice.
  2. Modifica la query per ottimizzare il percorso di accesso attraverso il database (in genere: colpire un indice il più vicino possibile)
  3. Modifica o aggiungi indici per influire sul percorso di accesso della query.

Se sei costretto a utilizzare il processo predefinito di modifica del valore di timeout, molto probabilmente dovrai fare molto più lavoro. Vengono in mente le seguenti opzioni

  1. Convalida con il tuo DBA e un'altra revisione del codice che hai veramente ottimizzato la query nel miglior modo possibile
  2. Lavora sulla struttura del DB sottostante per vedere se c'è qualche guadagno che puoi ottenere sul lato DB, creando / modificando uno o più idex.
  3. Dividilo in più parti, anche se ciò significa eseguire procedure con più parametri di ritorno che chiamano semplicemente un altro parametro. (Questa opzione non è elegante e, onestamente, se il tuo codice REALMENTE impiegherà così tanto tempo, mi occuperei della gestione e discuterò del timeout di 30 secondi)

Di recente abbiamo riscontrato un problema simile in un database di SQL Server 2000.

Durante la tua query, esegui questa query sul tuo database principale sul server db e vedi se ci sono blocchi che dovresti risolvere:

select 
  spid,
  db_name(sp.dbid) as DBname,
  blocked as BlockedBy,
  waittime as WaitInMs,
  lastwaittype,
  waitresource,
  cpu,
  physical_io,
  memusage,
  loginame,
  login_time,
  last_batch,
  hostname,
  sql_handle
from sysprocesses sp
where (waittype > 0 and spid > 49) or spid in (select blocked from sysprocesses where blocked > 0)

SQL Server Management Studio 2008 contiene anche un monitoraggio delle attività molto interessante che consente di visualizzare lo stato del database durante la query.

Nel nostro caso, è stato un blocco networkio a tenere occupato il database. Era un codice VB legacy che non disconnetteva il suo set di risultati abbastanza velocemente.

Se è vietato utilizzare le funzionalità dell'API di accesso ai dati per consentire a una query di durare più di 30 secondi, è necessario visualizzare l'SQL.

I guadagni in termini di prestazioni da ottenere ottimizzando l'uso di ADO.NET sono lievi rispetto ai vantaggi derivanti dall'ottimizzazione di SQL.

E stai già utilizzando il metodo più efficiente per eseguire SQL. Altre tecniche sarebbero numericamente più lente (anche se, se hai fatto un rapido recupero delle tue righe e qualche elaborazione sul lato client molto lenta usando DataSet, potresti essere in grado di portare il recupero iniziale a meno di 30 secondi, ma ne dubito. )

Se sapessimo che stavi facendo degli inserti, allora forse dovresti usare l'inserimento di massa. Ma non conosciamo il contenuto del tuo sql.

Questo è un brutto hack, ma potrebbe aiutarti a risolvere temporaneamente il tuo problema fino a quando non puoi risolvere il problema reale

    private static void CreateCommand(string queryString,string connectionString)
    {
        int maxRetries = 3;
        int retries = 0;
        while(true)
        {
            try
            {
                using (SqlConnection connection = new SqlConnection(connectionString))
                {
                    SqlCommand command = new SqlCommand(queryString, connection);
                    command.Connection.Open();
                    command.ExecuteNonQuery();
                }
                break;
            }
            catch (SqlException se)
            {
                if (se.Message.IndexOf("Timeout", StringComparison.InvariantCultureIgnoreCase) == -1)
                    throw; //not a timeout

                if (retries >= maxRetries)
                    throw new Exception( String.Format("Timedout {0} Times", retries),se);

                //or break to throw no error

                retries++;
            }
        }
    }
command.CommandTimeout *= 2;

Questo raddoppierà il timeout predefinito, che è di 30 secondi.

In alternativa, inserisci il valore per CommandTimeout in un file di configurazione, in modo da poterlo regolare secondo necessità senza ricompilare.

È necessario suddividere la query in più blocchi che ciascuno esegue nel periodo di timeout.

Se non è assolutamente possibile aumentare il timeout, l'unica opzione è ridurre il tempo di esecuzione della query entro il timeout predefinito di 30 secondi.

A me non piace aumentare il timeout di connessione / comando poiché nella mia mente sarebbe una questione di prendersi cura del sintomo, non del problema

hai pensato di suddividere la query in più blocchi più piccoli?

Inoltre, hai eseguito la tua query contro l'ottimizzatore del motore di database in:

Management Studio > Strumenti > Advisor di ottimizzazione del motore di database

Infine, possiamo dare un'occhiata alla query stessa?

applausi

Hai provato a racchiudere il tuo sql all'interno di una procedura memorizzata, sembrano avere una migliore gestione della memoria. Ho già visto timeout come questo in precedenza nell'istruzione sql del piano con query interne che utilizzano ADO classico. vale a dire selezionare * da (selezionare ....) t inner join somthingTable. Dove la query interna stava restituendo un numero molto elevato di risultati.

Altri suggerimenti 1. Eseguire letture con il suggerimento di esecuzione with (nolock), è sporco e non lo consiglio, ma tenderà ad essere più veloce. 2. Guarda anche il piano di esecuzione di sql che stai tentando di eseguire e riduci la scansione delle righe, nell'ordine in cui ti unisci alle tabelle. 3. cerca di aggiungere alcuni indici alle tue tabelle per letture più veloci. 4. Ho anche scoperto che eliminare le righe è molto costoso, potresti provare a limitare il numero di righe per chiamata. 5. Anche le variabili Swap @table con le tabelle #temporary hanno funzionato per me in passato. 6. Potresti anche aver salvato un piano di esecuzione errata (sentito, mai visto).

Spero che questo aiuti

  

Aggiornamento: sembra che la query non lo faccia   gettare qualsiasi timeout. La connessione è   timeout.

I.o.w., anche se non si esegue una query, la connessione scade? perché ci sono due timeout: connessione e query. Tutti sembrano concentrarsi sulla query, ma se si verificano timeout della connessione, si tratta di un problema di rete e non ha nulla a che fare con la query: la connessione deve essere stabilita prima di poter eseguire una query, ovviamente.

Potrebbe valere la pena provare a eseguire il paging dei risultati.

basta impostare la proprietà CommandTimeout di sqlcommand su 0, questo farà attendere il comando fino al termine della query ...     ad esempio:

SqlCommand cmd = new SqlCommand(spName,conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 0;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top