it-swarm.it

Problemi di prestazioni SQL con query remote su server collegati

Questo sproc

create proc dbo.Get_Accounts as
begin
  declare @current_date datetime
  set @current_date = dbo.fn_currdate()

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = @current_date
end

fallisce continuamente dopo 10 minuti con il seguente messaggio di errore:

Server: messaggio 7399, livello 16, stato 1, riga 1 OLE il provider DB "SQLOLEDB" ha segnalato un errore. L'esecuzione è stata terminata dal provider perché è stato raggiunto un limite di risorse. [Provider OLE/DB restituito messaggio: Timeout scaduto] OLE Traccia errore DB [OLE/DB provider 'SQLOLEDB' ICommandText :: Execute restituito 0x80040e31: esecuzione terminata dal provider perché è stato raggiunto un limite di risorse.].

Tuttavia, quando eseguo la stessa query dallo stesso database (non su quello remoto) in una finestra di query interattiva con la data hardcoded:

  select [fields]
  into dbo.current_accounts
  from linkedserver.database.dbo.accounts
  where date = '1/20/2012'

Ritorna in 30 secondi.

Il server locale è SQLSERVER 2008, il telecomando è SQLSERVER 2000.

Abbiamo fatto quanto segue inutilmente:

  • Ricreato il proc memorizzato.
  • sp_recompile sul proc memorizzato
  • aggiorna le statistiche su dbo.accounts
  • rilasciato e ricreato gli indici su dbo.accounts
  • rilasciato l'indice su dbo.accounts e provare
  • DBCC FREEPROCCACHE & DBCC DROPCLEANBUFFERS su server sia locali che remoti
  • Riavviato il server remoto (non un'opzione facile su quello locale)

Domande

  • Qualcuno può spiegare questo comportamento bizzarro?
  • Qualche suggerimento su altre opzioni per correggerlo?
8
Bob Probst

Puoi attivare trace flag 73 which might ti darà un messaggio di errore più dettagliato

Quante righe restituisce una query rappresentativa? Quanto è veloce/affidabile la connessione di rete tra i due server?

È possibile che un set di dati di grandi dimensioni impieghi troppo tempo per il trasferimento (oltre al tempo di query effettivo). È possibile aumentare il valore di timeout.

Puoi provare a riconfigurare le impostazioni di timeout come segue:

Impostare il timeout di accesso remoto su 300 secondi:

sp_configure 'remote login timeout', 300
go 
reconfigure with override 
go 

Impostare il timeout della query remota su 0 (attesa infinita):

sp_configure 'remote query timeout', 0 
go 
reconfigure with override 
go 

Aggiornamento : SQL Server 2012 SP1 in poi : utenti con SELECT l'autorizzazione sarà in grado di accedere a DBCC SHOW_STATISTICS che migliorerà le prestazioni di sola lettura sui server collegati. Rif: https://msdn.Microsoft.com/en-us/library/ms174384 (v = sql.110) .aspx

Aggiornamento : hai ragione nel dire che non è la dimensione dei dati o la velocità della connessione. Suonò un campanello nella mia memoria nebbiosa e mi ricordai dove l'avevo visto: Lento nell'applicazione, Veloce in SSMS? (Un problema con i server collegati). Non è lo sniffing dei parametri, sono le stesse statistiche che mancano (a causa delle autorizzazioni), causando l'utilizzo di un piano di query errato:

Puoi vedere che le stime sono diverse. Quando ho funzionato come amministratore di sistema, la stima era 1 riga, che è un numero corretto, poiché non vi sono ordini in Northwind in cui l'ID ordine supera 20000. Ma quando ho eseguito come utente normale, la stima era 249 righe. Riconosciamo questo numero particolare come il 30% di 830 ordini o la stima per un'operazione di disuguaglianza quando l'ottimizzatore non ha informazioni. In precedenza, ciò era dovuto a un valore variabile sconosciuto, ma in questo caso non esiste alcuna variabile che può essere sconosciuta. No, mancano le statistiche stesse.

Finché una query accede solo alle tabelle nel server locale, l'ottimizzatore può sempre accedere alle statistiche per tutte le tabelle nella query; non ci sono controlli di autorizzazione extra. Ma questo è diverso con le tabelle su un server collegato. Quando SQL Server accede a un server collegato, non esiste un protocollo segreto utilizzato solo per le comunicazioni tra server. No, invece SQL Server utilizza l'interfaccia standard OLE DB per server collegati, siano altre istanze di SQL Server, Oracle, file di testo o l'origine dati prodotta in casa e si connette esattamente come qualsiasi altro utente. Il modo esatto in cui vengono recuperate le statistiche dipende dall'origine dati e dal OLE provider DB in questione. In questo caso, il provider è SQL Server Native Client che recupera le statistiche in due passaggi. (Puoi vedere eseguendo Profiler sul server remoto). Innanzitutto il provider esegue la procedura sp_table_statistics2_rowset che restituisce informazioni su quali statistiche di colonna sono presenti, nonché la loro cardinalità e le loro informazioni sulla densità. Nel secondo passaggio, il provider esegue DBCC SHOW_STATISTICS, un comando che restituisce le statistiche di distribuzione complete. (Vedremo più da vicino questo comando più avanti in questo articolo.) Ecco il trucco: per eseguire DBCC SHOW_STATISTICS, è necessario essere membri del ruolo server sysadmin o qualsiasi ruolo del database db_owner o db_ddladm in.

Ed è per questo che ho ottenuto risultati diversi. Durante l'esecuzione come amministratore di sistema ho ottenuto le statistiche di distribuzione complete che indicavano che non vi sono righe con ID ordine> 20000 e che la stima era di una riga. (Ricorda che l'ottimizzatore non assume mai zero righe dalle statistiche.) Ma quando è in esecuzione come utente normale, DBCC SHOW_STATISTICS non è riuscito con un errore di autorizzazione. Questo errore non è stato propagato, ma l'ottimizzatore ha accettato che non vi fossero statistiche e ha utilizzato ipotesi predefinite. Dato che ha ottenuto informazioni sulla cardinalità, ha appreso che la tabella remota ha 830 righe, da cui la stima di 249 righe.

Ogni volta che si riscontra un problema di prestazioni in cui una query che include l'accesso a un server collegato è lenta nell'applicazione, ma viene eseguita rapidamente quando lo si verifica da SSMS, è necessario verificare sempre se autorizzazioni insufficienti sul database remoto potrebbero essere la causa. (Tenere presente che l'accesso al server collegato potrebbe non essere palese nella query, ma potrebbe essere nascosto in una vista.) Se si determina che le autorizzazioni sul database remoto sono il problema, quali azioni è possibile intraprendere?

  • È possibile aggiungere gli utenti al ruolo db_ddladmin, ma poiché ciò dà loro il diritto di aggiungere e eliminare tabelle, ciò non è consigliabile.

  • Per impostazione predefinita, quando un utente si connette a un server remoto si connettono come se stessi, ma è possibile impostare un mapping di accesso con sp_addlinkedsrvlogin, in modo che gli utenti eseguano il mapping a un account proxy che ha l'appartenenza a db_ddladmin. Si noti che questo account proxy deve essere un accesso SQL, quindi questa non è un'opzione se il server remoto non ha l'autenticazione SQL abilitata. Anche questa soluzione è alquanto dubbia dal punto di vista della sicurezza, sebbene sia meglio la proposta precedente.

  • In alcuni casi è possibile riscrivere la query con OPENQUERY per forzare la valutazione sul server remoto. Ciò può essere particolarmente utile se la query include diverse tabelle remote. (Ma può anche ritorcersi contro, perché l'ottimizzatore ora ottiene ancora meno informazioni statistiche dal server remoto.)

  • Ovviamente, puoi utilizzare la batteria completa di suggerimenti e guide di piano per ottenere il piano che desideri.

  • Infine, dovresti chiederti se è necessario l'accesso al server collegato. Forse i database potrebbero trovarsi sullo stesso server? I dati potrebbero essere replicati? Qualche altra soluzione?

11
Mitch Wheat

Cosa succede quando provi questo (cioè indica esplicitamente cosa dovrebbe essere eseguito sul server remoto) ?:

select [fields]
into dbo.current_accounts
from OPENQUERY(linkedserver, 'SELECT [fields] FROM database.dbo.accounts where date = ''1/20/2012''');

Sospetto che nel tuo caso sopra SQL Server stia semplicemente estraendo l'intera tabella dal server remoto, quindi eseguendo la query localmente (l'ho visto accadere molte volte in passato). Preferisco essere esplicito (utilizzando OPENQUERY o creando un SP sul server remoto), quindi non c'è alcuna possibilità di confusione.

2
Gareth

Poiché si tratta di un problema di risorsa, il pool di memoria esterno al server SQL utilizzato per caricare driver esterni e il CLR potrebbe essere vicino al limite. L'impostazione predefinita è 256 MB. Per ovviare a questo, ti suggerisco di andare al gestore della configurazione del server SQL, scheda avanzata e aggiungere l'opzione -g alla fine dei parametri di avvio.i.e; -g1024 quindi riavviare il servizio SQL Server. Di solito lo faccio perché utilizziamo un numero elevato di server collegati. http://msdn.Microsoft.com/en-us/library/ms190737.aspx

1
nopol

Ho due idee che potrebbero aiutare. Ho anche intenzione di dirti che ho avuto un po 'di sfortuna con prestazioni che eseguivano query su server collegati. Quindi la mia prima raccomandazione è di evitarlo se puoi.

La mia prima idea è quella di installare la procedura memorizzata nella casella SQL Server 2000, facendola riferimento al server locale. È quindi possibile eseguire la procedura memorizzata in remoto.

exec linkedserver.database.dbo.Get_Accounts

Se riesci a seguire questa strada, dovrebbe migliorare notevolmente le prestazioni.

La mia seconda idea è quella di ottenere il piano di query stimato durante l'esecuzione della procedura memorizzata. Ti sta mostrando ciò che richiede così tanto tempo? Un potenziale problema è che l'account che si sta utilizzando sul server collegato potrebbe non disporre dell'autorizzazione sufficiente per ottenere le statistiche della tabella (è necessaria più autorità per il server collegato rispetto a quella per il server locale). E questo può rendere le query incredibilmente lente. Puoi leggere di più su quel particolare problema qui .

1
Jeff Siver