2015-08-04 19 views
7

Sono in remoto selezionando i risultati da un database di produzione personalizzato con un criterio di circa tre minuti da un'applicazione C#.Riduzione del carico della CPU su una selezione remota da un database

Ogni volta che viene eseguito il comando di selezione, il PC server che sto utilizzando CPU sale fino a circa il 50%. Ma sicuramente, il carico dovrebbe essere sul database a cui mi sto connettendo?

Perché l'applicazione C# razzo al 50% finché i dati non vengono recuperati per la lettura?

Alcuni retroscena

  • ho lavorato fuori dalla debug che l'istruzione Select sul database remoto dura circa 30-40 secondi, tenendo in mente che sto selezionando con un criterio che utilizza la colonna indicizzata .
  • Allo stesso tempo della selezione dei dati dal DB remoto, ho monitorato il TaskManager e la CPU si è posizionata al 50% fino a quando la selezione è completa .. questo può durare circa 30-40 secondi per ogni ciclo.
  • Se si seleziona nel motore sql nativo per il DB remoto, non vi è alcun ritardo nella selezione, i dati (se presenti) vengono restituiti immediatamente.
  • So che non è l'analisi del set di risultati che sta prendendo il carico della CPU in quanto alcune selezioni non restituiranno nulla.

Ecco un codice che sto usando.

OdbcConnection remoteConn = new OdbcConnection(ConfigurationManager.ConnectionStrings["remoteConnectionString"].ToString()); 

      remoteConn.Open(); 

      OdbcCommand remoteCommand = new OdbcCommand(); 
      remoteCommand.Connection = remoteConn; 

      using (remoteConn) 
      { 
       string localSql = ""; 
       string remoteSql = "select * from tracking where last_update > 212316247440000000"; // Julian No = 2015-07-12 11:24:00 

       remoteCommand.CommandText = remoteSql; 

       OdbcDataReader remoteReader; 

       remoteReader = remoteCommand.ExecuteReader(); 

       while (remoteReader.Read()) 
       { 


        for (int i = 0; i < 68; i++) 
        { 
         localSql += ",'" + remoteReader[i].ToString() + "'"; 
        } 

       } 

      } 

Ho eseguito un test di prestazioni e diagnostica sull'applicazione e ha prodotto questo risultato.

enter image description here

Come, se del caso, posso ridurre questo carico della CPU o addirittura eliminare completamente. È completamente fuori dall'ordinario e non ho idea di come procedere.

Grazie

+0

Alcune domande prima di suggerire una soluzione ... vedo che si sta utilizzando l'ODBC driver, ma che tipo di database ti stai collegando? Quante righe vengono restituite dalla query? Hai bisogno di tutte le 68 colonne? Infine, puoi fornire qualche contesto su dove questo codice è in esecuzione? Si sta eseguendo in un'applicazione GUI? Se è così, è in esecuzione sul thread principale? – Randy

+0

@Randy - 1. È un DB SQL. 2. A volte circa dieci righe .. a volte nessuna. 3. Sì, tutte le colonne sono necessarie. 4. Viene eseguito in un'applicazione Windows Form. L'ho provato sul thread principale e sul background worker, nessuna differenza. EDIT: e ho anche provato in un'applicazione console, nessuna differenza di nuovo - Spero che questo aiuti. – SK2017

risposta

5

Grazie per l'informazione, Dan. Ecco i miei pensieri ...

Credo che il motivo principale per cui la tua app stia consumando tanta CPU è a causa dei driver che stai utilizzando.

Dato che ci si sta collegando a un database SQL Server, è necessario utilizzare i driver SQL Server che sanno come ottimizzare il trasporto dei dati tra client e server.

Per utilizzare i driver appropriati, assicurarsi di utilizzare il SqlConnection, SqlCommand, ecc

Questo permetterà a SQL Server di flusso i risultati al vostro cliente mentre si esegue una query il lettore di dati.

In secondo luogo, non utilizzare il metodo ExecuteReader() sull'oggetto DbCommand.Una delle tante meravigliose funzionalità esclusive dei driver SQL Server è il metodo ExecuteReaderAsync().

Poiché questo comando è un'operazione legata all'IO (non legata al calcolo), non è necessario bloccare il thread chiamante. Quando il risultato ritorna arriveranno su un thread di completamento IO.

Ecco un esempio di codice di come potrebbe essere il tuo codice dopo la modifica.

using (var remoteConn = new SqlConnection(ConfigurationManager.ConnectionStrings["remoteConnectionString"].ToString())) 
{ 
    remoteConn.Open(); 
    using (var remoteCommand = new SqlCommand()) 
    { 
     remoteCommand.Connection = remoteConn; 
     string localSql = ""; 
     string remoteSql = "select * from tracking where last_update > 212316247440000000"; // Julian No = 2015-07-12 11:24:00 

     remoteCommand.CommandText = remoteSql; 
     var remoteReader = await remoteCommand.ExecuteReaderAsync(); 
     while (remoteReader.Read()) 
     { 
      for (int i = 0; i < 68; i++) 
      { 
       localSql += ",'" + remoteReader[i].ToString() + "'"; 
      } 
     } 
    } 
} 
+0

Ho aggiornato la mia risposta per fornire un esempio di come potrebbe essere la soluzione. – Randy

+0

Hai menzionato "ottimizzare il trasporto dei dati", ma a volte non c'è ritorno nei dati. È solo una selezione sul DB che non restituisce nulla, questo sarebbe ancora un problema di driver? – SK2017

+0

Per essere onesti, non so davvero cosa stiano facendo i driver ODBC ma posso dire che sono molto generici e non sfruttano le tecnologie che Microsoft ha messo a punto nei driver di SQL Server per ottimizzare l'esecuzione delle query contro un database SQL Server. Hai notato un miglioramento delle prestazioni quando sei passato ai driver di SQL Server? – Randy

0

Potrebbe essere l'analisi del risultato? Stai usando una sorta di datalayer che analizza automaticamente i set di risultati in oggetti di dominio? Potrebbe essere molto intenso per la CPU ..

+0

Non penso che l'applicazione abbia loop ogni trenta secondi, 7 volte su dieci loop di solito non ci sono nuovi dati, ma la CPU continua a oscillare al 50%. – SK2017

1

Io parto dal presupposto che il comando di selezione viene eseguito da un computer separato da quello che ospita il DB SQL.

Francamente è un po 'sconcertante. L'unica cosa che mi viene in mente è che potrebbe passare del tempo e elaborare i metadati. Hai provato a cambiare la tua query per tornare a dire solo un pezzo di dati?

Ad esempio:

select top 1 last_update from tracking where last_update > 212316247440000000 
select count (*) from tracking where last_update > 212316247440000000 

Questo chiarirà se i dati + metadati è la causa del problema.

0

Il modo migliore per le vostre operazioni IO Bound sta usando il Task, siccome non ha bisogno di cambiare il codice esistente:

var task = Task.Factory.StartNew(() => { 
    //your code or @Randy code with little change here: 
    using (var remoteConn = new SqlConnection(ConfigurationManager.ConnectionStrings["remoteConnectionString"].ToString())) 
    { 
     remoteConn.Open(); 
     using (var remoteCommand = new SqlCommand()) 
     { 
      remoteCommand.Connection = remoteConn; 
      string localSql = ""; 
      string remoteSql = "select * from tracking where last_update > 212316247440000000"; // Julian No = 2015-07-12 11:24:00 

      remoteCommand.CommandText = remoteSql; 
      var remoteReader = remoteCommand.ExecuteReader(); 
      while (remoteReader.Read()) 
      { 
       for (int i = 0; i < 68; i++) 
       { 
        localSql += ",'" + remoteReader[i].ToString() + "'"; 
       } 
      } 
     } 
    } 
}); 
//use can use 'task.wait();' to waiting for complete the task. 
+0

In realtà questo è leggermente errato in quanto il thread su cui viene eseguita l'attività sarebbe ancora bloccato dall'operazione legata all'IO ExecuteReader(). – Randy