Ogni istanza ha una propria buffer cache che si gestisce autonomamente, in più esiste una caratteristica di RAC chiamata cache fusion, che sarebbe un insieme di processi che si parlano tra loro da un nodo del cluster all'altro, garantendo l'integrità dei dati attraverso le istanze.
Dato che una query, eseguita su un nodo qualunque del cluster, deve ottenere risultati consistenti, le istanze devono poter parlare tra loro tramite una connessione molto veloce, che in genere viene ottenuta tramite una rete ethernet, soprattutto per la relativa economicità della soluzione.
Le istanze trasferiscono tra loro direttamente i blocchi della SGA, che vengono quindi impacchettati secondo gli usuali protocolli di rete per essere spediti alle altre istanze del cluster. Oracle usa UDP per il trasferimento, penso soprattutto per evitare l'overhead del TCP.
La dimensione dei pacchetti trasferiti sull'interconnect è data al parametro della scheda di rete MTU, che è la dimensione in byte del più grande pacchetto o frame che può essere inviato attraverso la rete. In generale il default per tutte le interfacce di rete è 1500 byte.
Ciò implica che, trasferendo un blocco dalla SGA di un nodo a quella di un altro nodo, il blocco viene spezzettato in più frame sufficienti a contenere le sue dimensioni (es. 8K standard). Questa operazione è relativamente lenta, e poiché le prestazioni dell'interconnect possono avere un forte impatto sulle prestazioni globali del cluster, è preferibile limitare l'uso dell'interconnect, facendo in modo ad esempio che qualsiasi nodo del cluster lavori prevalentemente o unicamente su un sottoinsieme dei dati disgiunto da quello su cui lavora ciascun altro nodo.
C'è anche un modo per aumentare le prestazioni dell'interconnect: l'uso dei jumbo frame.
I jumbo frame sono dei frame (molto) più grandi di quelli standard di 1500 bytes; generalmente il limite delle loro dimensioni è 9000 bytes; questo limite è interessante perché molto vicino alle dimensioni "standard" del blocco Oracle (8K), e comunque superiore alle dimensioni dei blocchi usati in OLTP (2K, 4K, 8K).
L'idea che mi è venuta è di verificare che i blocchi transitino "intatti" sulla interconnect, in modo da massimizzare le prestazioni, al variare della MTU. Non tutte le apparecchiature di rete, infatti, supportano i jumbo frame o comunque dimensioni troppo grandi dei frame.
Il modo più diretto e anche più affidabile è usare un tool di monitoring del traffico. Io propendo per Wireshark, per la facilità e l'estrema potenza.
Il test che ho pensato è il seguente: creo una tabella 3 o 4 blocchi in un tablespace con blocksize di 8K, inserisco un po' di dati ad-hoc, "carico" il suo contenuto nella SGA dell'istanza su cui sto lavorando (una semplice select va bene, anche se l'insert ordinaria dovrebbe bastare), e poi cerco di accedere agli stessi dati da una seconda istanza del cluster (un'altra select è sufficiente).
Ipotizzo che Oracle trasferisca i blocchi in questione sull'interconnect invece che leggerli da disco, poiché si presume che il trasferimento via interconnect sia molto più veloce.
Per riconoscere i blocchi nel traffico di rete utilizzo dei "marker" sui dati: creo una tabella contenente stringhe ben visibili. In questo caso utilizzo stringhe tipo "AAAA...", "BBBB...." e così via, per 20 righe di circa 2000 caratteri ciascuna, in modo che in ogni blocco da 8192 byte ci stiano 3/4 record. "Forzo" la massima occupazione dello spazio nei blocchi utilizzando l'opzione pctfree 0.
SQL> create table aob (rn integer, rp char(2000)) pctfree 0;
Table created.
SQL> insert into aob select rownum, rpad(chr(64+rownum), 2000, chr(64+rownum))
from all_objects where rownum <= 20;
20 rows created.
SQL> commit;
Commit complete.
SQL> select rn, substr(rp, 1, 1) letter, length(rp) len, rowid
from aob order by rn;
RN LETT LEN ROWID
---------- ---- ---------- ------------------
1 A 2000 AAAM8OAAFAAABCXAAA
2 B 2000 AAAM8OAAFAAABCXAAB
3 C 2000 AAAM8OAAFAAABCXAAC
4 D 2000 AAAM8OAAFAAABCXAAD
5 E 2000 AAAM8OAAFAAABCYAAA
6 F 2000 AAAM8OAAFAAABCYAAB
7 G 2000 AAAM8OAAFAAABCYAAC
8 H 2000 AAAM8OAAFAAABCYAAD
9 I 2000 AAAM8OAAFAAABCUAAA
10 J 2000 AAAM8OAAFAAABCUAAB
11 K 2000 AAAM8OAAFAAABCUAAC
12 L 2000 AAAM8OAAFAAABCUAAD
13 M 2000 AAAM8OAAFAAABCVAAA
14 N 2000 AAAM8OAAFAAABCVAAB
15 O 2000 AAAM8OAAFAAABCVAAC
16 P 2000 AAAM8OAAFAAABCVAAD
17 Q 2000 AAAM8OAAFAAABCWAAA
18 R 2000 AAAM8OAAFAAABCWAAB
19 S 2000 AAAM8OAAFAAABCWAAC
20 T 2000 AAAM8OAAFAAABCWAAD
20 rows selected.
Nell'ultima select per brevità è presente solo una lettera per ogni riga (rp è una stringa char di 2000 caratteri tutti uguali); è evidente come i record stiano nello stesso blocco a gruppi di 4 dal rowid: la riga all'interno del blocco è indetificata dagli ultimi tre caratteri del rowid, mentre il blocco dai 5 caratteri precedenti.
Ho quindi un modo per identificare visualmente i blocchi in transito sull'interconnect: il primo sarà pieno di A, B, C e D; il secondo di E, F, G, H, e così via.
Dopo aver eseguito le istruzioni precedenti su un nodo del cluster, passo al successivo facendo semplicemente
SQL> select * from aob;
Nel frattempo ho attivato Wireshark in modo da loggare tutto il traffico di passaggio sull'interfaccia di rete dell'interconnect su uno dei due nodi del cluster.
Ecco che cosa si vede da Wireshark con la MTU a 1500 byte (il blocco visualizzato è evidenziato in blu nell'elenco dei frame):
I frame di dati sono lunghi 882 byte, e vengono riassemblati in blocchi da 8248 byte (vedere in basso a sinistra nell'immagine). Nell'immagine si vede uno dei frame contenenti la "P"; servono perfino più di due frame per poter trasmettere anche una sola riga, lunga almeno 2002 byte.
Il frame di 1500 byte è quindi addirittura di gran lunga sottoutilizzato in questo caso.
Ora vediamo in dettaglio il traffico con la MTU a 9000 byte.
Ecco il blocco che comincia con la riga delle "I":
Più in basso si passa dalla "K" alla "L":
Ecco il blocco della "A" (notare che non sono in ordine, giustamente):
Ecco la fine del blocco della "A", "D":
Nei dati del blocco è presente la dimensione del frame, 8282 byte:
Ciò significa che, con l'MTU impostata a 9000 byte, i blocchi transitano integri sull'interconnect, garantendone il massimo delle prestazioni.
Probabilmente è possibile arrivare ad un valore ottimale per l'MTU, di poco superiore o di poco inferiore, guardando anche gli altri tipi di comunicazione sull'interconnect, ma per ora ci fermiamo qui.
1 commento:
decisamente interessante questo post, tanto più che qualche anno fa, per la mia tesi di laurea mi ero occupato delle performance del protocollo raw ethernet in combinata con i jumbo frames (saltando i protocolli tcp e udp per massimizzare le prestazioni). L'MTU impostato a 9000 bytes permetteva, in effetti di consumare meno cpu per ricomporre dati più grandi di 1500 bytes e con un transfer rate decisamente elevato (molto prossimo al limite del Gbit/s) come indicato anche in questo documento (un pò vecchiotto ma rende l'idea) http://sd.wareonearth.com/~phil/jumbo.html
Inoltre, il fatto di viaggiare su UDP, nel caso dei pacchetti che transitano sulla rete privata tra i nodi di un rac non dovrebbe creare problemi di perdita di pacchetti visto che in generesi tratta di connessioni punto-punto, se non ricordo male.
Posta un commento