Questa pagina è stata tradotta automaticamente. Leggi l'originale in inglese. English

Libreria IBSurgeon

Ulteriori dettagli sul database Firebird SQL da 1,7 Terabyte

Alexey Kovyazin, 26-mag-2014

Qualche giorno fa abbiamo pubblicato un articolo sui nostri test, dedicato alla relazione tra le prestazioni di Firebird e la crescita dei database, dove abbiamo testato (tra gli altri) il database Firebird SQL da 1,7 Terabyte. Qui puoi trovare maggiori dettagli su un database così grande.

Tabelle

Nella tabella 1 puoi trovare l’elenco delle tabelle con le caratteristiche principali. Questa statistica è stata ottenuta con gstat -a -r e interpretata con il nostro strumento IBAnalyst.

Tabella Record LunghezzaRec, byte Pagine dati Dimensioni tabella, Mb Dimensioni indici, Mb Totale, %
ORDER_LINE 6300024797 60.09 38880344 607505.3 50582.3 34
STOCK 2100000000 298.88 43783806 684121.9 15809.61 39
ORDERS 630004141 29.00 2692324 42067.56 4250.38 2
HISTORY 630000434 48.77 3446455 53850.86 0.00 3
CUSTOMER 630000000 577.52 24226054 378532.0 8675.78 21
NEW_ORDER 188999981 13.00 623764 9746.31 1260.84 1
DISTRICT 210000 103.92 1860 29.06 1.27 ~0
ITEM 100000 82.73 756 11.81 0.52 ~0
WAREHOUSE 21000 97.90 179 2.80 0.11 ~0

Tabella 1. Tabelle e loro parametri principali nel database Firebird SQL da 1,7 Terabyte

Come puoi vedere, la tabella più grande è ORDER_LINE - contiene più di 6 miliardi di record. La sua dimensione è di circa 600Gb e ci sono 50Gb di indici per questa tabella. Questa tabella occupa il 34% del database.

Anche la tabella STOCK è molto grande - contiene circa 2 miliardi di record. Sebbene il numero di record sia inferiore rispetto alla tabella ORDER_LINE, STOCK occupa ~680Gb nel database (39%), perché la lunghezza dei record in STOCK è di 298.88 byte contro solo 60.09 byte in ORDER_LINE. Di conseguenza, la dimensione degli indici associati per STOCK è solo di 15Gb.

La terza tabella più grande, CUSTOMER, ha una lunghezza dei record ancora maggiore - 577.52 byte, e occupa 378Gb (21%) con solo 630 milioni di record.

Indici

Non ci sono molti indici in questo database, poiché è progettato solo per i test - la maggior parte delle tabelle ha solo 1 indice - la chiave primaria. Nelle applicazioni del mondo reale, gli sviluppatori creano molti indici per servire specifiche richieste degli utenti, ma qui gli indici sono concentrati sugli inserimenti e aggiornamenti più rapidi dei dati - di conseguenza, la dimensione degli indici è solo il 5-10% della dimensione delle tabelle, mentre di solito è il 30-50% (se hai indici più grandi del 50% della dimensione della tabella, considera di ridisegnare lo schema del database, o forse di eliminare indici inutili o non efficaci).

Nella tabella 2 puoi trovare l’elenco di tutti gli indici in questo database e le loro caratteristiche principali, prese dalle statistiche di gstat e analizzate da IBAnalyst:

Indice Tabella Profondità Chiavi # Lunghezza chiave, byte # di chiavi uniche Dimensioni, Mb
ORDER_LINE_PK ORDER_LINE 4 6300024797 1.41 6300024797 50582.34
STOCK_PK STOCK 4 2100000000 1.00 2100000000 15809.61
ORDERS_PK ORDERS 3 630004141 1.01 630004141 4250.38
CUSTOMER_LAST CUSTOMER 3 630000000 0.00 1000 4029.64
CUSTOMER_PK CUSTOMER 3 630000000 1.01 188999981 1260.84
NEW_ORDER_PK NEW_ORDER 3 188999981 1.01 188999981 1260.84
DISTRICT_PK DISTRICT 2 210000 1.37 210000 1.27
ITEM_PK ITEM 2 100000 1.00 100000 0.52
WAREHOUSE_PK WAREHOUSE 2 21000 1.00 21000 0.11

Tabella 2. Indici del database Firebird da 1,7Tb

Come puoi vedere, tutti gli indici hanno una dimensione della chiave molto piccola - il massimo è 1.41, il che significa che l’indice è compresso molto efficacemente - ad esempio, la chiave primaria per ORDER_LINE contiene 6 miliardi di chiavi in 50 Gb di spazio. Questo è un ottimo risultato.

Ci sono 2 indici che hanno profondità = 4. Ciò significa che il motore deve eseguire 4 letture delle pagine dell’indice per trovare il valore richiesto. Si raccomanda che la profondità dell’indice non sia superiore a 3, e se è superiore, la dimensione della pagina del database dovrebbe essere aumentata. Tuttavia, abbiamo già la dimensione massima della pagina in questo database (16Kb), quindi dobbiamo conviverci.

Riscaldamento

Prima di continuare con le query, eseguiamo il “riscaldamento”. Quando il database è grande, anche i dati di sistema associati alle tabelle sono grandi, e Firebird impiega del tempo per memorizzare nella cache le pagine di sistema necessarie. Ad esempio, la tabella ORDER_LINE ha 10110 pagine puntatore. Il riscaldamento è necessario per qualsiasi database grande.

Senza riscaldamento, la prima esecuzione delle query richiederà significativamente più tempo rispetto a con riscaldamento.

Per riscaldare la cache del database, eseguiamo una serie di query semplici per caricare i dati di sistema nella cache:

select first 1 * from ORDER_LINE; select first 1 * from STOCK; select first 1 * from customer; select first 1 * from ORDERS; select first 1 * from ITEM;

Questo è sufficiente per uno schema dati così semplice. Per database grandi e complessi con centinaia di tabelle, lo schema di riscaldamento potrebbe essere molto più complesso e dovrebbe essere sviluppato con attenzione.

Di conseguenza, il consumo di memoria del processo Firebird (Firebird SuperServer 2.5.2 64 bit) cresce:

riscaldamento cache firebird

Figura 1. Riscaldamento della cache del database

Query

Dopo il riscaldamento possiamo eseguire diverse query tipiche per stimare come gli utenti lavoreranno con un database Firebird così grande: quali saranno i tempi di risposta per le query tipiche e le operazioni di lettura.

Query Piano e statistiche Descrizione
select first 10 w_id, w_name, c_id, c_last<br> from WAREHOUSE, customer<br> where c_w_id = w_id and c_w_id = 10000 PLAN JOIN (WAREHOUSE NATURAL,<br> CUSTOMER INDEX (CUSTOMER_PK))<br> STATISTICS<br> Current memory = 166919744<br> Delta memory = 5616<br> Max memory = 166977488<br> Elapsed time= 0.08 sec<br> Buffers = 10000<br> Reads = 1<br> Writes 0<br> Fetches = 51 Join della tabella WAREHOUSE (21000 record) con CUSTOMER (630 milioni di record), con condizione per un magazzino specifico
select count(*)<br> from WAREHOUSE, customer<br> where c_w_id = w_id and c_w_id = 10000 PLAN JOIN (WAREHOUSE INDEX (WAREHOUSE_PK),<br> CUSTOMER INDEX (CUSTOMER_PK))<br> COUNT<br> ============<br> 30000<br> <br> Current memory = 166918560<br> Delta memory = -1184<br> Max memory = 166977488<br> Elapsed time= 0.16 sec<br> Buffers = 10000<br> Reads = 1175<br> Writes 0<br> Fetches = 60040 Conteggio dei record per la query precedente.
SELECT first 10 *<br> FROM ORDER_LINE<br> WHERE OL_W_ID = 10050 PLAN (ORDER_LINE INDEX (ORDER_LINE_PK))<br> STATISTICS<br> Current memory = 167013640<br> Delta memory = 95080<br> Max memory = 167055968<br> Elapsed time= 0.80 sec<br> Buffers = 10000<br> Reads = 10285<br> Writes 0<br> Fetches = 10424 Query alla tabella più grande ORDER_LINE (~6 miliardi di record) con condizione per selezionare i record order_line per un id di magazzino specifico. Poiché la chiave primaria ORDER_LINE_PK è composta e contiene l’ID del magazzino (CONSTRAINT ORDER_LINE_PK: Primary key (OL_W_ID, OL_D_ID, OL_O_ID, OL_NUMBER), viene utilizzata efficacemente in questa query.
SELECT count(*)<br> FROM ORDER_LINE<br> WHERE OL_W_ID = 10050; PLAN (ORDER_LINE INDEX (ORDER_LINE_PK))<br> <br> COUNT<br> ============<br> 299509<br> Current memory = 167125464<br> Delta memory = -7160<br> Max memory = 167267976<br> Elapsed time= 3.41 sec<br> Buffers = 10000<br> Reads = 1994<br> Writes 0<br> Fetches = 599170 Conteggio per la query precedente. La seconda query con gli stessi parametri sarà molto più veloce, ma query con parametri diversi mostrano risultati simili.
Select first 10 w_id, w_name, c_id, c_last<br> rom WAREHOUSE, CUSTOMER<br> where c_w_id = w_id and (c_w_id > 8000)<br> and (c_w_id < 10000) PLAN JOIN (WAREHOUSE INDEX (WAREHOUSE_PK),<br> CUSTOMER INDEX (CUSTOMER_PK))<br> <br> STATISTICS<br> Current memory = 167687664<br> Delta memory = -651888<br> Max memory = 168931400<br> Elapsed time= 0.19 sec<br> Buffers = 10000<br> Reads = 31<br> Writes 0<br> Fetches = 63 Query con join e 2 condizioni.
select first 10 c1.C_ID, c1.C_FIRST, c1.C_LAST, o1.O_ID,<br> o1.O_OL_CNT, i1.I_NAME, i1.I_PRICE, ol1.OL_AMOUNT<br> from customer c1<br> join orders o1 on (c1.c_w_id = o1.O_w_ID<br> and c1.C_D_ID = o1.O_D_ID and c1.C_ID = o1.O_C_ID)<br> join ORDER_LINE ol1 on (ol1.ol_w_id = c1.C_W_ID<br> and ol1.OL_D_ID = c1.C_D_ID<br> and ol1.OL_O_ID = o1.O_ID)<br> join Item i1 on (ol1.OL_I_ID = i1.i_id)<br> where o1.o_d_id = 1 and o1.o_w_id = 1 and c1.c_id = 10; PLAN JOIN (C1 INDEX (CUSTOMER_PK),<br> O1 INDEX (ORDERS_PK),<br> OL1 INDEX (ORDER_LINE_PK),<br> I1 INDEX (ITEM_PK))<br> <br> STATISTICS<br> Current memory = 166807128<br> Delta memory = 6016<br> Max memory = 166879200<br> Elapsed time= 0.20 sec<br> Buffers = 9999<br> Reads = 1<br> Writes 0<br> Fetches = 4960 Query per mostrare i dettagli di un cliente specifico in un magazzino e distretto specifici. Dimostra le prestazioni di join più complessi.

Riepilogo

Come puoi vedere, il database Firebird da 1,7 Terabyte mostra prestazioni abbastanza buone per le query di lettura comuni. Se la query è ben progettata e utilizza buoni indici, le prestazioni sono buone anche su database molto grandi. Naturalmente, ci sono punti critici, come i tempi di recupero lunghi per set di dati molto grandi e i tempi lunghi per le operazioni di conteggio (poiché Firebird visita tutte le pagine per ottenere un numero esatto di record per la query specifica nella transazione specifica), ma questi punti critici sono ben noti agli sviluppatori Firebird esperti, e ci sono metodi per progettare il database in modo da aggirarli.

Ma che dire del degrado delle prestazioni per inserimenti e aggiornamenti? È possibile compensarlo con una messa a punto intelligente della configurazione di Firebird?

Nel prossimo articolo prenderemo in considerazione le opzioni di messa a punto per il database Firebird da 1,7 terabyte - daremo una spinta al nostro uccello e lo faremo volare.