2012-03-18 10 views
6

NHibernate Profiler mostra un sacco di messaggi di errore sul piano di query:formati differenti parametri risultano nella cache piano di query inefficienti

formati differenti parametri determinano l'utilizzo della cache inefficiente piano di query

Esso conduce anche ad un spiegazione in http://nhprof.com/Learn/Alerts/UncachedQueryPlan e ti avverte sull'uso del parametro durante la creazione della sessione. Lo faccio in modo fluente:

.ExposeConfiguration(configuration => configuration 
    .SetProperty("current_session_context_class", "thread_static") 
    .SetProperty("prepare_sql", "true") 
    .SetProperty("generate_statistics", "true") 
    ) 

Ma sembra che non funzioni perché i messaggi di errore sono ancora lì. È una limitazione su OracleClientConfiguration o sto sbagliando?

Modifica Per fornire qualche informazione in più su questo ...

Nel mio repository faccio questo

session.Query<TEntity>.Where(predicate).ToList(); 

e questa è la chiamata

var value = ParameterRepository.First(p => (p.Pipeline.Id == pipelineId && p.Name == name)); 

Ad esempio quelli sono due SQL generati da questa chiamata e quel profiler Nhibernate mostra come "Le dimensioni dei parametri DIFFERENTI portano a un piano di query inefficiente età"

select GUID1_12_, 
     PARAMETER2_12_, 
     PARAMETER3_12_, 
     GUID4_12_ 
from (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_, 
       pipelineex0_.PARAMETER_NAME    as PARAMETER2_12_, 
       pipelineex0_.PARAMETER_VALUE    as PARAMETER3_12_, 
       pipelineex0_.GUID_PIPELINE_TRACKING  as GUID4_12_ 
     from FCT_PIPELINE_EXEC_PARAMETER pipelineex0_ 
     where pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */ 
       and pipelineex0_.PARAMETER_NAME = 'lid' /* :p1 */) 
where rownum <= 1 /* :p2 */ 

e la seconda

select GUID1_12_, 
     PARAMETER2_12_, 
     PARAMETER3_12_, 
     GUID4_12_ 
from (select pipelineex0_.GUID_PIPELINE_EXEC_PARAMETER as GUID1_12_, 
       pipelineex0_.PARAMETER_NAME    as PARAMETER2_12_, 
       pipelineex0_.PARAMETER_VALUE    as PARAMETER3_12_, 
       pipelineex0_.GUID_PIPELINE_TRACKING  as GUID4_12_ 
     from FCT_PIPELINE_EXEC_PARAMETER pipelineex0_ 
     where pipelineex0_.GUID_PIPELINE_TRACKING = 'A5916E73CF1E406DA26F65C24BFBF694' /* :p0 */ 
       and pipelineex0_.PARAMETER_NAME = 'period' /* :p1 */) 
where rownum <= 1 /* :p2 */ 

IMHO è questo PARAMETER_NAME con 'coperchio' e 'periodo' che sta generando diversi piani di query.

grazie in anticipo

+0

quindi quali sono i diversi piani di esecuzione di Oracle? – steve

+0

Ora non rughetto come è il piano di query di Oracle, ma lo scenario è abbastanza simile a [quello evidenziato nella descrizione di ayende del problema] (http://nhprof.com/Learn/Alerts/UncachedQueryPlan). In breve si dice che il nibbato deve essere confidato per eseguire querie in un piano di ricerca amichevole _ e, da quello che vedo io non funziona. – guillem

+0

beh, mi sento di raccomandare un approccio più analitico, altrimenti si tratta di tentativi ed errori. Quali sono gli errori? – steve

risposta

0

Per generare lo stesso piano ogni volta che il parametro deve essere impostata alla stessa lunghezza indipendentemente dal valore del parametro.

È possibile personalizzare l'implementazione del driver per impostare la lunghezza del parametro di query sulla lunghezza del campo specificata nella mappatura.

public class CustomOracleClientDriver : OracleClientDriver 
{ 
    protected override void InitializeParameter(IDbDataParameter dbParam, string name, SqlType sqlType) 
    { 
     base.InitializeParameter(dbParam, name, sqlType); 

     if (sqlType.LengthDefined) 
      dbParam.Size = sqlType.Length; 
    } 
} 

(NOTA: Eredita da OracleDataClientDriver se si utilizza ODP.Net)

Se si sta utilizzando Fluent NHibernate si registra l'implementazione pilota come questo:

Fluently.Configure() 
       .Database(
        OracleDataClientConfiguration.Oracle10 
         .ConnectionString(c => c.FromAppSetting("ConnectionString")) 
         .Driver<CustomOracleClientDriver>()) 
0

ho provato questo con un OracleClientDriver sovrascritto (che utilizza il vecchio driver Microsoft Oracle, non ODP.NET), simile al codice nella risposta di Mattk, e non ho visto alcuna differenza nell'esecuzione di Oracle, sebbene i parametri di stringa ora avessero una dimensione comune.

Here's il mio post su StackExchange DBA.

Oracle Enterprise Manager non ha mostrato query duplicate per il mio SQL generato da NHibernate, e in entrambe le versioni, ogni chiamata ha causato un parsing (fino a circa 1000 per lunghi test), ma molto pochi pareri rigidi, senza differenze tra variabile e fisso lunghezza del parametro.

In effetti, Oracle ha creato piani di query duplicati solo per le query senza parametri di bind, ma con valori concatenati nella stringa SQL (qualcosa da evitare in SQL codificato). Quindi ora mi sembra che la dimensione dei parametri non sia importante per Oracle, quando si tratta di riutilizzare i piani di query (o, in termini Oracle, condivisione del cursore).

Oracle probabilmente confronta solo la stringa SQL per la corrispondenza del piano, mentre SQL Server controlla anche le definizioni dei parametri. È anche possibile vedere una differenza quando guardando lo SQL dinamico comandi EXECUTE IMMEDIATE (Oracle) e sp_executesql (SQL Server): sp_executesql ottiene anche una stringacon le definizioni dei parametri (in una stringa, non come parametri per la chiamata allo sp_executesql!). So che NHibernate/ADO.NET utilizza sp_executesql quando si inviano query con parametri a SQL Server, quindi è probabile che abbia una diversa gestione in SQL Server. Inoltre, quando ci si connette a SQL Server tramite NHibernate, tutti i parametri delle stringhe hanno dimensioni univoche (dalla mappatura NHibernate o dalla lunghezza massima predefinita), quindi è probabile che il problema sia stato risolto laddove rilevante. Correggimi se sbaglio!

L'utilizzo di Prepara/prepare_sql in ADO.NET/NHibernate presenta alcuni svantaggi: a seconda dell'implementazione, prima di eseguire qualsiasi SQL, è necessario inviare una richiesta di preparazione al database, l'applicazione deve mantenere un handle per l'istruzione preparata e può essere utilizzato solo per una connessione. Significato: i nuovi manici devono essere creati spesso. Quando ho provato con Oracle e ODP.NET, era un po 'più lento rispetto alla versione non preparata, sebbene l'interrogazione da parte dell'handle stesso sia (poco) più performante rispetto a SQL parametrizzato, abbinato al database per parità di stringhe. Probabilmente, la preparazione è buona se l'applicazione utilizza molte volte la stessa query all'interno della stessa connessione DB o della stessa sessione di NHibernate.

Problemi correlati