19 novembre 2008

Oracle e SQL Server a confronto

Avendo avuto a che fare con un test di migrazione da Microsoft SQL Server a Oracle, mi si è aperto un mondo nuovo con l'utilizzo di SQL Developer Migration Workbench.

Nella documentazione HTML è presente un'interessante comparativa tra SQL Server e Oracle, molto istruttiva da qualsiasi punto di vista, anche per riassumere le caratteristiche principali dei due database.
In particolare vengono sintetizzate le differenze per quanto riguarda i concetti di locking e di concorrenza.

6 commenti:

Anonimo ha detto...

Ciao a tutti

Devo fare delle osservazioni non tanto a questo ma soprattutto ai documenti che fanno confronti in generale... premetto che non mi disturba affatto che siano di parte (è pacifico portare l'acqua al proprio mulino), ma le cose vanno dette tutte quante.

Innanzitutto si fa il confronto con MSSQL Server dicendo che non ha il row locking, tranne poi ammettere che c'è dalla 7.0, e quindi il confronto andrebbe fatto eventualmente con questa in primis.

Si fa notare il fatto che i lock "bloccano" le altre transazioni come se fosse la conclusione (negativa) di un argomento, ma il discorso da fare è ben più complesso e sfumato.

Innanzitutto se un'applicazione necessita di avere l'ultima versione dei dati, attendere (=lock) la fine della transazione concorrente è l'UNICA strada (questo fatto va sempre detto per primo, ma curiosamente non lo vedo scritto quasi mai).

Viene detto che "il lock di Oracle è completamente automatico", il che non è vero perché il programmatore si può trovare a dover specificare l'intento di una transazione con clausola "FOR UPDATE" (quindi specificando a mano il tipo di lock), oppure deve gestire i casi tipo "Snapshot too old" (notate che mi sto riferendo ai casi di errori peculiari delle rispettive tecnologie, e non ai casi comuni tipo fine memoria, errore di rete etc).

I lock di MSSQL Server scalano automaticamente al tipo di livello superiore (anche in corso di esecuzione) per le query più impegnative; in questo modo vengono assegnate più risorse se una transazione ne necessita.

In altri documenti si vanta la tecnologia (brevettata, capirai) non-escalating dei row version, ma questo è un limite!
I lock sono strutture mantenute in memoria, ad accesso rapido e a costo ridotto (la struttura X è bloccata, attendere); le row version sono strutture più impegnative (una copia completa dello stato precedente del record), ed è proprio a causa di tale pesantezza che sono soggette a scrittura su disco (tempo perso in più).

Oltre al fatto che fermare temporaneamente le altre transazioni creando una coda è ESATTAMENTE lo scopo dei lock e non un effetto collaterale, bisognerebbe far notare che una coda di n transazioni non necessariamente impiega più tempo dell'esecuzione parallela delle stesse, specialmente tenendo presente che il meccanismo di versionamento ha un suo costo aggiuntivo; il vantaggio si avrebbe solo nel caso di una transazione "bloccata", il che è un caso particolare.

In OraDB il row versioning è trattato comunque al livello di pagina, MSSQL può iniziare dal lock a livello di singola riga per poi scalare a quello più appropriato.

Oltre a questo MSSQL dalla versione 2005 ha la MVCC, per giunta OPZIONALE, per cui c'è il meglio dei due mondi. Una MVCC può mantenere e servire più versioni di una riga, gli undo segment mantengono solo lo stato della prima transazione aperta.

Inoltre per quanto riguarda il discorso fondamentale, ossia che il versionamento aumenterebbe la velocità complessiva delle transazioni, esiste un documento di ricerca (di cui purtroppo ho solo notizie indirette) il quale dimostra che, passato un certo punto di carico sul database, la soluzione con i lock è più performante, perché evita di ingolfare il server.

bye bye

m2w

Rudy ha detto...

Grazie per il contributo, ma penso che siano necessarie precisazioni.

Ho notato intanto che nel documento Oracle indicato c'è un errore: gli undo segments servono per avere le immagini dei blocchi prima che vengano modificati; essi contengono quindi l'immagine dei dati vecchi.
Oracle infatti, in modo ottimistico, modifica direttamente i blocchi interessati da un'update. La commit non fa altro che "invalidare" i blocchi di undo quando nessuna transazione li richiede più per la read-consistency; infatti è molto più pesante il rollback.

Nel documento viene presa in esame la concorrenza a basso livello, praticamente a livello fisico, almeno nel punto che stai considerando tu.

Correttamente viene detto che due transazioni sono fisicamente serializzate in caso di sql server poiché non esiste il meccanismo di copia dei blocchi vecchi nell'undo come in Oracle, dove vengono fisicamente letti dei blocchi distinti dalle due transazioni.
Il versioning è a livello di blocco, non di pagina.

Il lock esclusivo del "select for update" serve quando voglio fare la select/visualizzare dei dati prima di modificarli, ma non c'entra col locking che avviene con la modifica reale successiva.
"Snapshot too old" è più un problema sistemistico e di risorse che altro.

I lock in memoria sono a costo ridotto finché il tempo di query e il numero di record interessati è molto piccolo.
È il principio su cui si basa la velocità (o presunta tale) di mysql/myisam, dove tutti bloccano tutti, ma non ce ne si accorge finché non si chiede quel qualcosa in più al database.

La MVCC c'è in Oracle almeno dalla versione 6, se non erro, ed è un componente fondamentale del motore di database.

Mi interessa molto il documento che citi alla fine. Se lo trovi, posta un commento, per favore.

Bye :-)

Anonimo ha detto...

Il lock esclusivo del "select for update" serve quando voglio fare la select/visualizzare dei dati prima di modificarli, ma non c'entra col locking che avviene con la modifica reale successiva.

In che senso non c'entra, direi il contrario. Se faccio una select for update, il dbms mette (potrei sbagliare i termini, ma il concetto è questo) dei lock esclusivi sulle righe, proprio perché mi "riservo" di modificarli. Se avessi usato una select semplice quei lock non ci sarebbero.
Quello che intendo dire è che in realtà sto specificando quale tipo di lock il dbms deve mettere; (il fatto che non compaia il termine lock ovviamente non rileva... il comando serve a quello scopo).


"Snapshot too old" è più un problema sistemistico e di risorse che altro.

Ho chiaramente sbagliato il nome dell'errore; intendevo l'errore che appare quando a causa di un insufficiente livello di isolazione non riesco ad aggiornare i dati (aiutatemi...).


È il principio su cui si basa la velocità (o presunta tale) di mysql/myisam, dove tutti bloccano tutti, ma non ce ne si accorge finché non si chiede quel qualcosa in più al database.

Questo è l'estremo opposto... mancano lock di granularità intermedia.


Mi interessa molto il documento che citi alla fine. Se lo trovi, posta un commento, per favore.

Eh... lo sto cercando da quando in un post uno ne ha fatto riferimento indiretto... nulla per ora.

bye bye

m2w

Rudy ha detto...

Volevo dire che la "select for update" e la update sono due operazioni distinte.
Il fatto che io faccia la sfu prima è solo per esigenze applicative.
Il "non-bloccaggio" avviene, per ciò che riguarda la nostra analisi sulla concorrenza a livello più fine, durante l'update.

L'errore di cui parli potrebbe essere una lost update, che non è esattamente un errore di per sè, oppure "ORA-08177: cannot serialize access...", ma non mi sembra pertinente. Non capisco a quale ti riferisci.

Cristian ha detto...

Interessante discussione, vorrei aggiungere anche la mia opinione: è chiaro che l'implementazione di un certo isolation level (in oracle e mi sembra anche in MSSQL il default è read committed) influenza i meccanismi di locking. Questo a sua volta influenza l'implementazione delle applicazioni, quindi magari su due sistemi diversi come Oracle e MSSQL si riescono ad ottenere gli stessi risultati con tecniche di programmazione diverse. Quando in Oracle si dice che "il lock è completamente automatico" ci si riferisce all'implementazione dell'isolation level read committed. Il caso del SFU è un caso particolare che non rientra in tale isolation level, però copre un'esigenza che si può avere. Oracle ha anche altri meccanismi di locking esplicito che permetteno a punto di risolvere esigenze particolari.

Per quanto riguarda invece il discorso che prima di MSSQL 2005 gli update bloccassero i lettori è chiaramente un aspetto in qui MSSQL server era inferiore. Si dice che il row versioning ha un costo aggiuntivo, si, ma solo se un'altra transazione deve leggere lo stesso record modificato dalla transazione che lo sta modificando, altrimenti non c'è costo aggiuntivo. Il fatto che MSSQL abbia introdotto MVCC farebbe supporre che si tratti di una tecnica importante e che mancava. Il fatto che sia opzionale, secondo me è dovuto più all'implementazione che da quel poco che ho capito è molto più pesante di quella di Oracle, perchè lavora a livello di dati (copia i regord, come oracle salava i blocchi nell'UNDO) e non a livello di blocchi che al fatto di offrire il "meglio dei due mondi". Anche ammettendo che la tecnica del row versioning (che in Oracle è a livello di blocco perchè in Oracle tutte le operazioni di questo tipo sono a tale livello per questioni di ottimizzazione, anche il paging della ram avviene a livello di pagina per lo stesso motivo) abbia un costo, tale costo può essere sopperito da hardware più potente. Mentre la serializzazione fa comunque attendere tempo. Solitamente ha più valore il tempo che l'hardware e le aziende preferiscono spendere qualcosa in più nell'harware che vedere gli utenti/operatori attendere la risposta di una query.
m2w, tu dici,
In OraDB il row versioning è trattato comunque al livello di pagina, MSSQL può iniziare dal lock a livello di singola riga per poi scalare a quello più appropriato. Il lock in Oracle è sempre a livello di riga, ha costo praticamente zero perchè viene registrato sulla riga stessa.
Oltre a questo MSSQL dalla versione 2005 ha la MVCC, per giunta OPZIONALE, per cui c'è il meglio dei due mondi. Una MVCC può mantenere e servire più versioni di una riga, gli undo segment mantengono solo lo stato della prima transazione aperta. come ha già detto Rudy gli undo mantengono la versione precedente, quindi, per essere chiari, Oracle è in grado di ricostruire tutte le versioni.

Ciao
Cristian

Anonimo ha detto...

Ciao a tutti

Ricapitolando, dato che l'ora tarda non aiuta a riflettere...

@ Rudy

Ho usato impropriamente il termine pagina invece di blocco.

Non credo che la concorrenza con i blocchi di undo, ossia la modifica ottimistica dei dati nuovi, costi meno per solo per il fatto che poi punta a questi se la transazione ha successo; bisogna comunque mantenere le immagini di "prima" e "dopo", ossia due blocchi; forse il momento del commit "costa" poco, ma devo comunque mantenere integre due immagini dei due blocchi (anche se la riga modificata è solo una all'interno di queste); io sostengo che questo costa di più rispetto al lock pessimistico di una riga.
Quello che ho letto dopo non vedo come modifichi questo.

Riguardo ai lock, continuo a credere che dire che sono completamente automatici è esagerato (ma non lo sono neppure in alcuno dei principali dbms...).
Quando si fa la SFU, il dbms mette SUBITO dei row lock DIVERSI da quelli che avrei se avessi fatto una select "semplice"; questo per me è specificare a mano il tipo di lock.
Ho anche trovato in questo documento di Oracle:
http://download.oracle.com/docs/cd/B10501_01/server.920/a96524/c21cnsis.htm
la seguente riga, che per me taglia la testa al toro:
"SELECT ... FOR UPDATE [...] specifically requests a lock for the reader"

(Il lock sarà completamente automatico quando il dbms riuscirà a capire da solo i lock che deve mettere dalla sola analisi di tutte le istruzioni della transazione).

L'errore "cannot serialize" similmente mi impone una gestione manuale della transazione (facendola eventualmente ripartire), ed è peculiare a questa tecnologia.

Ricordiamoci infatti che il versioning è concorrenza ottimistica, i locking sono concorrenza pessimistica; i primi presuppongono che l'assenza di conflitti sia la norma, e impongono la gestione solo in caso (raro) di conflitto; i secondi presuppongono che il conflitto sia la norma, e gestiscono il caso da subito in maniera "difensiva", ma dopo la fase del lock sono tranquillo.

Un effetto collaterale di questo è che il pessimistico costa poco ma sempre, l'ottimistico costa se va male.
Il documento che ho menzionato sopra presumibilmente mi dice che passato un certo punto di carico, il costo del versioning "pesa" più della strategia coi solo lock.

@ Cristian:
D'accordo su parità di risultati, ma la neutralità e/o le esigenze di abilità verso il codice SQL/programmatore è differente (per me il dbms non mi deve dettare come scrivere la mia applicazione).
Il SFU è un caso particolare perché Oracle lo necessita come conseguenza della sua architettura, quindi il distinguo tra le due select è innanzitutto artificiale e comunque indesiderato.
Più che per le mancanze di MSSQL (che ci sono, intendiamoci) la MVCC è stata introdotta per richiesta di mercato in primis.
Per il discorso aziende/hardware/costi, sono d'accordo, ma andare dietro al mondo commerciale è deleterio (vedasi il successo di MySql, che ha un meccanismo di concorrenza terrificante).
Il fatto che sia opzionale è per mantenere la compatibilità col codice precedente, dovuto al fatto che alcune query si comporterebbero diversamente.
Sulla completezza della MVCC avevo un link che spero ritrovi, che riportava alcuni limiti della stessa rispetto alla ricostruzione delle immagini precedenti.

A risentirci qui o sul newsgroup (dove passano pochi messaggi ultimamente...)

bye

m2w