3 pagine in totale: <<Indietro 1 2 [3]
Cosa c'è dietro...
Il principio di esecuzione dei MARS non include comunque parallelismo, bensì alternanza: uno ed un solo default resultset può essere attivo in ogni singolo momento e la sua attivazione comporta la sospensione di tutti gli altri. Il comportamento risultante è simile a quello presente a livello di sistema operativo di un context switch tra i vari thread attivi.
L'alternanza di esecuzione non è però consentita per tutte le tipologie di statement eseguibili verso SQL Server, ma esclusivamente per un loro sottoinsieme, tipicamente per comandi di popolamento asincrono di cursori, operazioni di BULK INSERT, RECEIVE, FETCH, READTEXT e SELECT.
L'esecuzione di un comando non MARS-enabled comporta la serializzazione di tutti i comandi successivamente sottoposti all'esecuzione anche se questi fossero MARS-enabled.
Tipicamente, una pesante operazione di DELETE FROM... seguita da qualsivoglia operazione anche MARS-enabled fa si che tutte le operazioni successive siano serializzate sino al completamento del task DML; diversamente, un'operazione di SELECT che sia MARS-enabled può essere sospesa per attivare l'operazione di DELETE, ma in questo caso lo switch non sarà rientrante visto che, come detto, l'istruzione DELETE non è sospendibile.
Come già indicato, l'utilizzo dei MARS non è limitato alle sole operazioni di selezione, bensì possono essere utilizzati con successo anche come alternativa ai cursori server side. Nel modello di esecuzione dei MARS è consentito utilizzare corposi comandi inclusi in batch, magari anche richiamanti stored procedures esistenti come anche SQL dinamico.
Grazie all'utilizzo del cursore interno di tipo firehose le prestazioni dei default resultset prodotti sono sicuramente superiori ai cursori server-side Transact-SQL. Le imposizioni fisiche delle risorse allocate dalle operazioni MARS-enabled possono però vanificare tali miglioramenti prestazionali specialmente nel caso di consumo non pro-attivo dei resultset, cosa non necessaria nei cursori Transact-SQL, dove il consumo può anche essere più ritardato, anche se ciò non è comunque raccomandabile.
Breve panoramica sui cursori Transact-SQL
SQL Server gestisce i cursori in modo diverso dalle query standard, anche se entrambe le metodologie di accesso ai dati possono (o meno, come spesso può avenire nel caso di semplici query standard) richiedere accesso a tempdb per la costruzione di strutture e schemi di allocamento nel caricamento del risultato. I cursori peraltro eccedono sempre in tal senso, richiedendo spazi non convenzionali e non riciclabili dal server per manipolazioni successive, sia in termini di memoria e caching di piani e risultati che di performance.
I cursori richiedono infatti la chiamata esplicita di DEALLOCATE CURSOR che aiuta l'engine ad un clean-up intelligente, altrimenti esclusivamente gestito dallo scope di dichiarazione. Il tutto viene appesantito dalla necessità, da parte dell'engine stesso, di mantenere la relativa gestione di lock e dall'utilizzo di cursori interni di una posizione (simili a quelli prodotti per le operazioni MARS-enabled, tipicamente quelli utilizzati per l'individuazione delle righe all'intero del risultato restituito). La posizione è un concetto per certi versi esterno alla natura dei DBMS, visto che il risultato viene sempre (eventualmente ordinato) restituito in stream continui di dati (T(abular) D(ata) S(tream) nel nostro caso) e mai mantenuto.
Per certi versi una certa anomalia è sempre stata rappresentata dai cursori pessimistici server side di ADO che, con meccanismi interni al provider OLE DB, mantenevano una posizione conosciuta sulla riga consumata, basandosi sulla tecnologia disponibile dei cursori SQL Server. Diversamente, sempre all'elaborazione del client (per esempio, cursori client side messi a disposizione dal Client Cursor Engine di ADO o i recordset tipicamenti disconnessi di ADO.NET) è stato demandato l'onere del posizionamento che meglio si addice ad un'architettura client/server, dove lo scopo fondamentale del server è quello di esaudire nel più breve tempo possibile le richieste di numerosi client.
In questo senso non va confuso anche lo standard di output di SQL Server, il cosidetto cursore firehose, che al di là della categorizzazione, non costituisce un cursore a tutti gli effetti, ma uno stream unidirezionale e continuo di TDS.
SQL Server conosce 4 tipi di cursori, FORWARD_ONLY, DYNAMIC, STATIC e KEYSET, che si differenziano in particolare dalla capacità di conoscere eventuali modifiche intervenute nei dati originali durante la navigazione.
I cursori FORWARD_ONLY sono quello che più si avvicinano al risultato standard di SQL Server, il firehose cursor, e non richiedono spazio in tempdb.
I cursori DYNAMIC condividono con i forward only cursors molte proprietà, nel senso che le modifiche ai dati sottostanti sono visibili nel momento del posizionamento sulla riga modificata, ma chiaramente sono scrollabili.
I cursori STATIC cambiano decisamente politica, in quanto non riflettono mai le modifiche intervenute ai dati sottostanti, mantenendo una completa scrollabilità all'interno del risultato, dovuto ad una copia completa dello stesso in tempdb.
I cursori KEYSET per certi versi includono un pò delle caratteristiche degli uni e degli altri: i dati sottostanti sono sensibili alle modifiche con l'esclusione delle chiavi univoche che identificano le righe, ma non sono in grado di includere nel risultato i nuovi inserimenti. In questa tipologia solo le chiavi vengono archiviate in tempdb, e nel caso di assenza di chiavi primarie o univoche, l'intero set di chiavi candidate viene inserito nell'immagine in tempdb.
Le tipologie di cursori si differenziano anche per il diverso utilizzo dei lock. Nei cursori STATIC i lock vengono impostati nel momento della costruzione del risultato e lo stesso discorso vale per i KEYSET, ma limitatamente alle chiavi utilizzate. I cursori DYNAMIC pospongono tale necessità al momento del fetch della riga, mentre i cursori FORWARD_ONLY sono indirizzati dall'optimizer: nel caso di utilizzo di work-table ausiliarie i lock vengono imposti nel momento dell'apertura del cursore, diversamente nel momento del recupero della singola riga. Non va dimenticato il fatto che gli scroll-lock necessari alla navigazione interna vengono mantenuti fino al caricamento della riga successiva.
Queste considerazioni fanno sì che non sempre sia possibile sostituire cursori Transact-SQL con batch MARS-enabled.
Riguardo all'utilizzo dei MARS occorre fare alcune considerazioni:
- è necessario attivarne la funzionalità (per ogni singola connessione che vuole farne uso) al momento dell'apertura della connessione da associare al DataReader tramite l'apposita specifica in stringa di connessione, tipicamente oggetto.ConnectionString = "Integrated Security=true; Data Source=...; Initial Catalog=...; MultipleActiveResultSets=true;"
- è utilizzabile esclusivamente verso fonti di dati SQL Server 2005 (qualsiasi edizione);
- non c'è parallelizzazione nello scorrimento di più Reader eseguiti come MARS, in quanto ogni attivazione di un singolo Reader comporta la disattivazione dei Reader concorrenti; siamo quindi in presenza di alternanza di esecuzione e non di esecuzione parallela; DataReader legati a diverse connessioni possono invece sfruttare un certo grado di parallelizzazione;
- è chiaramente utilizzabile nei soli casi nei quali non si richieda una navigabilità all'interno dei risultati restituiti, ma esclusivamente un consumo dei dati restituiti sia da un client visuale (che sia WindowsForm o WebForm) che non visuale;
- può essere poco indicato nei casi nei quali si richieda una alta capacità interattiva di aggiornamento verso la base dati, visto che necessariamente si tratta di un modello di consumo ancora più disconnesso del modello comunque disconnesso presente nel framework di ADO.NET;
- non è necessario modificare il proprio codice funzionante che non fa utilizzo dei MARS relativamente alla stringa di connessione, anche se un leggero overhead viene aggiunto (anche nel traffico di rete) nel caso in cui la funzionalità sia abilitata;
- lo stack OLE DB di versioni precedenti di SQL Server ha spesso tentato di ovviare la limitazione di un singolo default resultset per connessione, aprendo dietro le quinte ulteriori connessioni secondo necessità; questo poteva avere ripercussioni notevoli nel caso di transazioni esplicite, visto che le diverse connessioni non condividono la stessa transazione;
- resta comunque buona prassi quella corrente per il normale consumo dei DataReader tradizionali: l'utilizzazione più veloce possibile dell'oggetto con conseguente rilascio delle risorse;
- è veramente importante consumare il resultset in modo rapido dato che, in caso contrario, i buffer del server tendono a riempirsi e i lock (nella migliore delle ipotesi, almeno gli stability lock) non vengono rilasciati, bloccando il worker thread;
- l'uso dei MARS richiede driver specifici:
- driver ODBC inclusi nel SQL Native Client,
- driver OLEDB inclusi nel SQL Native Client,
- il provider SqlClient nativo incluso nel Microsoft .NET Framework, Versione 2.0.
Conclusioni
I MARS aggiungono un ulteriore strumento nelle mani dei consumatori di basi di dati, un'arma che chiaramente vanta sia pregi che difetti. Uno dei pregi più importanti riguarda l'avvicinamento del tradizionale mondo dei database relazionali basato su set al mondo degli sviluppatori notoriamente più vicino ad un modello basato su cursori.
I MARS rappresentano un'arma potente che può consentire miglioramenti prestazionali eccellenti, specialmente nelle situazioni di alta disconnessione dalla base dati, con bassi foot-print in termini di risorse rispetto ai modelli dove il livello di coupling è molto alto, come nel caso dei tradizionali RecordSet ADO e degli oggetti DataSet e DataTable di ADO.NET.
3 pagine in totale: <<Indietro 1 2 [3]
Attenzione: Questo articolo contiene un allegato
Contenuti dell'articolo
Per inserire un commento, devi registrarti alla nostra community.







Difficoltà
Utilità
Stampa
Download


