Benchmarking Relational DBMS

La verifica delle prestazioni di un database relazione fa parte dei compiti principali di un DBA (DataBase Administrator). Questo documento cerca, con un taglio volutamente semplice e pratico, di fornire indicazioni utili per effettuare benchmark significativi su applicazioni reali orientate ai dati.

La prima parte del documento riporta i principali benchmark standard per la valutazione delle prestazioni degli RDBMS (Relational DataBase Management System).
Quindi vengono riportati diversi esempi di codice per l'implementazione di benchmark. Gli esempi sono basati su un benchmark standard ma possono essere facilmente adattati a situazioni reali. Gli esempi sono, in alcuni casi, programmi completi e funzionanti, in altri casi snippet di codice.

Nel capitolo seguente vengono riportati alcuni tool Open Source e commerciali per misurare le prestazioni.

La parte finale del documento riporta la terminologia, alcuni importanti concetti di base, ... insomma una serie di indicazioni che, forse, dovrebbero essere riportate all'inizio ma che, essendo un poco piu' noiose, ho deciso di tenere alla fine!

TPC

Il Transaction Processing Performance Council (TPC) e' un organismo internazionale che disegna benchmark standard e ne omologa i risultati. I benchmark del TPC sono ritenuti i piu' importanti e significativi per la valutazione delle prestazioni dei sistemi che ospitano DBMS. Quindi li riportiamo per primi! I principali benchmark definiti dal TPC sono:

Ogni benchmark viene definito con un documento di specifiche molto ampio e completo che ne definisce ogni aspetto (eg. scala o dimensione della base dati, proprieta' ACID, durata minima dei run di test, popolamento base dati, ...) Per essere pubblicati i risultati dei benchmark debbono essere validati da un Advisor e vanno accompagnati da un documento che riporta tutti i dettagli della configurazione e delle prove svolte. L'impegno per effettuare un benchmark ufficiale e' molto oneroso e viene sostenuto solo dai principali vendor HW e SW a fronte del rilascio di nuovi e significativi prodotti.
I benchmark TPC sono molto utili poiche' forniscono una metrica significativa e di facile raffronto (eg. tpmC: transazioni TPC-C al minuto; Price/tpmC: costo per transazione) e possono essere implementati anche senza soddisfare tutti i requisiti per l'omologazione fornendo comunque risultati significativi.
Nel seguito utilizzeremo il benchmark TPC-B quale riferimento per la realizzazione di programmi in diversi ambienti e con differenti RDBMS.
Alcune implementazioni Open Source ispirate ai test TPC si trovano in questo link oppure in questo link; i workload sono differenti e quindi i risultati non sono teoricamente confrontabili con i risultati dei benchmark TPC standard... ma sono comunque significativi! Oracle ha implementato un paio di questi test nel kit OLT.

Altri benchmark

L'AS3AP (Ansi Standard Sql Scalable And Portable) e' un benchmark che prevede una serie di test da eseguire, tutti correttemente, su una base dati in 24 ore di tempo. La metrica utilizzata e' la dimensione della base dati (la dimensione massima di una base dati che consenta di eseguire l'intero test nelle 12 ore previste). Il test AS3AP e' stato ripresentato in diverse varianti e versioni...
Una benchmark suite disponibile free in rete e' l'OSDB (Open Source Database Benchmark) all'indirizzo http://sourceforge.net/projects/osdb (ref. http://osdb.sourceforge.net). Si tratta di un bench derivato dall'AS3AP ma con alcune significative differenze (eg. non e' previsto il test di recovery, non ci sono limitazioni nella pubblicazione dei risultati) che lo rendono piu' facilmente realizzabile.

Con i sorgenti MySQL viene distribuita la MySQL benchmark suite (e l'utility "crash-me"). Sebbene possano essere utilizzate per la misurazione delle prestazioni, l'obiettivo della suite e' quello di verificare la presenza e la corretta implementazione delle funzionalita' in SQL. L'implementazione e' tale da consentire un raffronto (di tipo funzionale) anche con altri RDBMS. E' naturalmente disponibile come sorgente...

Con l'evolvere delle funzionalita' delle basi dati nascono e si diffondono nuovi benchmark. Sulle basi dati chiave-valore cosi' diffusi sulle piattaforme in Cloud e' possibile utilizzare YCSB (Yahoo! Cloud Serving Benchmark)...

Il test EMP7 non e' standard, ma e' terribilmente semplice da eseguire. Con l'utente SCOTT/TIGER (demo di Oracle) e' sufficiente eseguire, impostando set timing on, una select count(*) from emp, emp, ... 7 volte! La tabella EMP ha 14 righe, ripetendo sette volte il nome della tabella il numero di righe selezionate e' pari a 147= 105.413.504 quindi cento milioni di righe circa: perfetto per un test! Il test e' CPU bound, va eseguito a sistema scarico ed interessa il valore minimo ottenuto. Semplicissimo... ma per i piu' pigri c'e' lo script EMP7.sql gia' pronto.
Con altri RDBMS e' utilizzabile lo stesso test avendo l'avvertenza di creare una tabella EMP con 14 righe. La sintassi puo' variare leggermente poiche' alcuni RDBMS non accettano self-join... Inoltre alcuni ottimizzatori riescono ad effettuare il prodotto cartesiano senza accedere fisicamente a tutte le righe. La query di partenza, valida anche per Oracle in modo da poterlo confrontare ad "armi pari", e' approssimativamente questa:

SELECT COUNT(*)
FROM emp emp1, emp emp2, emp emp3, emp emp4, emp emp5, emp emp6, emp emp7
WHERE emp7.deptno=10;
Un elenco di risultati ottenuti di recente con il benchmark EMP7 su differenti piattaforme e database e' riportato in questo Report. Il benchmark puo' essere eseguito utilizzando script gia' predisposti per differenti ambienti: Oracle, MySQL, Informix, PostgreSQL, SQLite, DB2, MS SQLServer, Grinder/Jython via JDBC, Hadoop Pig (uses this data file), MongoDB, ... Maggiori dettagli sull'EMP7 si trovano su questo link.

Naturalmente i risultati di diversi benchmark dipendono dalle prestazioni dei processori, dischi, software di base, ... utilizzati dai sistemi in esame.
Anche se non e' l'obiettivo di questo documento, e' importante riportare qualche indicazione per una valutazione di massima.
Per conoscere le prestazioni dei processori presenti sulle varie macchine e' possibile riferirsi benchmark specifici. Tra i piu' validi ed utilizzati quelli pubblicati dallo SPEC tra cui quelli relativi alle CPU: CINT2000 e CFP2000. I benchmark pubblicati dallo SPEC sono molteplici ed oltre a quelli sulle prestazioni dei processori sono molto utilizzati quelli relativi alla misurazione di applicazione J2EE.
Non conoscendo nulla sui sistema sotto test e' possibile eseguire qualche semplice bench per la misurazione della velocita' del processore. Un programma C compilato con l'ottimizzazione abilitata che effettua 100.000.000 di cicli ed assegnazioni a reali (double) puo' essere facilmente utilizzato per confrontare le prestazioni di processori differenti: Esempio ciclo su double

Controlli sulle prestazioni dello storage possono essere realizzati in modo semplice con il comando dd mentre per la rete il comando ping, con le diverse opzioni, permette di effettuare tutti i test necessari sul network.
Ma per chi non si accontenta... Oracle ha sviluppato un test per i dischi che simula il carico dell'RDBMS: ORION (ovviamente Oracle non lo dice ma il tool funziona benissimo per valutare le prestazione dello storage anche se poi si utilizza un altro RDBMS ;-). Mentre Netperf e' il piu' noto tool Open Source per misurare le prestazioni di una rete.

Se quanto riportato fino ad ora non e' sufficiente... un'ampia raccolta di benchmark e' pubblicata nella Linux Benchmark Suite Homepage... buona lettura!

TPC-B

Il TPC-B e' un benchmark tipico per la misurazione delle prestazioni di una base dati a fronte di un carico transazionale.

La transazione TPC-B viene eseguita su una banca ipotetica che ha una o piu' filiali. Su ciascuna filiale piu' cassieri e conti correnti. La transazione prevede un movimento di cassa su un conto aggiornando le tabelle relative al conto, al cassiere ed alla filiale.
Lo schema e' costituito da tabelle con la seguente cardinalita':

Il numero di filiali (BRANCHES) e' il fattore di scala del test che deve crescere per ogni transazione nominale. Con scale=10 il numero di ACCOUNTS e' quindi pari a 1.000.000. La tabella HISTORY cresce durante le sessioni di test e deve poter contenere dati a sufficenza (lo standard prevede un dimensionamento sufficiente per 60 giorni). Effettuati gli opportuni controlli di consistenza dei dati la tabella viene generalmente troncata.

La transazione effettua il seguente ciclo di operazioni:

Lo standard prevede tempi di pensamento pari a 0 e la misurazione del TPS massimo.
Ma naturalmente e' possibile utilizzare il benchmark anche per valutare l'interazione dell'utente (inserendo un think time tra le esecuzioni delle transazioni), oppure scalare i test con un numero via via crescente di utenti, o utilizzare un carico "misto" di transazioni batch ed interattivi, per utilizzare il TPC-B per caricare il sistema e quindi effettuare prove su transazioni applicative, ...

TPC-C

Il TPC-C e' un benchmark per la misurazione delle prestazioni di una base dati a fronte di un carico transazionale. La sua definizione e' piu' completa del benchmark TPC-B poiche' prevede la presenza di un job-mix di transazioni differenti. La base dati di riferimento e' quella di un magazzino.

La transazione misurata e' quella dell'Order Entry, ma e' presente un job-mix di altre transazioni eseguite in contemporanea sulla stessa base dati.

Progammi

I programmi di test riportati nel seguito, che realizzano la transazione TPC-B standard, consentono l'effettuazione di una serie di prove parametrizzabili per simulare differenti situazioni. Poiche' sono riportati i sorgenti di tutti i programmi e gli script e' possibile utilizzarli sia per eseguire la transazione standard TPC-B che modificarli per inserire il proprio codice applicativo realizzando cosi' un benchmark reale.

I programmi ricevono come parametri gli elementi necessari all'elaborazione su un RDBMS (eg. Username/password), ed altri parametri di configurazione del test. Il numero di esecuzioni in ciclo, le utenze attive, la durata del tempo di pensamento ed il fattore di scala sono configurabili.
E' cosi' possibile generare attivita' differenti sulla base dati. Con tempi di pensamento uguali a 0 si simulano attivita' batch. Impostando ad un valore reale il tempo di pensamento dell'utente (eg. 10 sec), possono essere effettuati test di stress.
Innalzando il numero di cicli il test risulta meno influenzato da variazioni statistiche (ma naturalmente richiede piu' tempo per l'esecuzione). Il fattore di scala consente di utilizzare lo stesso programma con generazione di codici random piu' ampi (numero di branch) che agiscono su basi dati via via crescenti.

Innanzi tutto va preparata la base dati creando lo schema (eg. schema su Oracle schema su MySQL ), popolandolo con i dati richiesti (eg. caricamento dati su Oracle caricamento dati su MySQL ) e creando le eventuali Stored Procedure (eg. Stored Procedure in Oracle Stored Procedure in MySQL ).
Possono quindi essere utilizzati i seguenti programmi, realizzati su alcuni tra i piu' diffusi RDBMS:

I programmi implementano la transazione standard TPC-B ma possono essere semplicemente modificati per realizzare qualsiasi transazione applicativa.

I test effettuati con tempo di pensamento uguale a 0 simulano una attivita' di tipo batch ed offrono un elemento di paragone sulle prestazioni ottenibili sulle varie macchine. Vengono in genere effettuate diverse serie di test con numeri di utenze parallele crescenti. Lo standard TPC-B prevede test con tempo di pensamento pari a 0.
I test effettuati con un tempo di pensamento uguale a 10 (simile al tempo di un utente reale) non pesano eccessivamente sulla macchina ospite: l'elapsed totale e' dovuto in gran parte ai tempi utente e la variazione dei tempi dovuta ad un numero maggiore di utenti operanti sulla stessa macchina e' limitato. E' naturalmente possibile in questo modo simulare un numero di utenti e di connessioni maggiori generando un carico complesso e verificare cosi' eventuali limiti del sistema e delle configurazioni adottate (eg. numero di sessioni utente).

Su un sistema Unix lanciare decine o centinaia di programmi in parallelo e' banale. Lanciando i programmi descritti sopra o variazioni degli stessi che sia piu' vicine alle applicazioni effettivamente presenti, e' possibile simulare il carico voluto.
Meno banale e' invece il caricamento iniziale delle basi dati, la creazione dei programmi di test (se si utilizzano test differenti da quelli standard) e fare in modo che i test siano semplicemente ripetibili...

Un programma che realizza il bench emp7 e' lo script Jython EMP7.PY richiamabile con Grinder su qualsiasi RDBMS raggiungibile via JDBC. La versione sotto forma di script SQL e' personalizzata per RDBMS: Oracle, MySQL, Informix, PostgreSQL, SQLite, DB2, Hadoop Pig (uses this data file), ...

Altro benchmark interessante e' quello relativo all'esecuzione di cicli nel linguaggio procedurale utilizzato per la realizzazione di Trigger e Stored Procedure: Procedural Language - Loop. Disponibile per basi dati diverse: Oracle, MySQL, PostgreSQL, ... Non e' mio ma e' molto indicativo anche lo script SQL JLOCI

Misurare le prestazioni

Una volta pronti i programmi, la misurazione delle prestazioni non e' complessa.

Per raccogliere dati significativi e' necessario avere qualche accortezza nel preparare i test: escludere i ramp-up ed i ramp-down, effettuare i test a sistema dedicato, caricare il sistema con il "rumore" corretto. In caso contrario i dati raccolti sono semplicemente non significativi.
La matematica e la statistica sarebbero un obbligo per una corretta raccolta dei dati ma e' possibile procedere anche in modo empirico effettuando alcuni run di test controllando che i risultati siano stabili. Prima di effettuare le registrazioni dei tempi vengono quindi eseguiti alcuni cicli: sia per prova, che per far andare a regime il sistema. I tempi sono raccolti raccogliendo la media dei tempi elapsed totali. Un numero di sessioni maggiore si ottiene semplicemente lanciando piu' script in parallelo.
Teoricamente la preparazione di uno scenario di test con i corretti ramp-up, il randez-vous per la partenza sincronizzata, la misurazione di ogni singolo run e la completa analisi statistica di tutti i dati e' l'unica modalita' utilizzabile. Un trucco? Anziche' raccogliere i dati di tutti i programmi lanciati... e' sufficiente costruire un buon rumore di fondo con n-1 processi e, con sistema stabile, eseguire misurazioni precise di un singolo processo. In poche ore potrete ottenere risultati affidabili che altrimenti avrebbero richiesto settimane o strumenti appositi.

Raccolti i dati su uno spreadsheet e' semplice ottenere le statistiche sui benchmark effettuati. Nella figura che segue e' riportata una situazione tipica. Al crescere del numero di utenti il Throughtput sale fino ad un massimo per rimanere quasi costante per un certo periodo e quindi diminuire per il crescere dell'overhead generato dal grande numero di sessioni parallele.
In un sistema stabile le prestazioni decadono lentamente. In molti casi pero' si verifica il fenomeno del trashing in cui il throughtput scende bruscamente a fronte di un maggiore carico sul sistema. In altri casi ancora, superato un certo limite, il sistema non elabora piu' correttamente e le transazioni richieste dagli utenti vanno in errore.

Immagine benchmark

E' spesso utile utilizzare programmi appositi per l'esecuzione di benchmark come quelli descritti nel seguito (The Grinder, Load Runner, Benchmark Factory, JMeter). Il questo caso la raccolta dei dati, il lancio dei programmi, ... vengono eseguiti in automatico. Ma attenzione, nessuno strumento rendera' significativo un bechmark quando gli obiettivi non sono correttamente impostati!

The Grinder

The Grinder e' un ottimo software free source per eseguire benchmark su applicazioni web. The Grinder puo' essere utilizzato facilmente anche per la realizzazione di benchmark su un DB relazionale. Infatti i test vengono scritti in Jython e possono quindi accedere ai DB via JDBC.

Nella figura che segue viene riportata l'esecuzione del benchmark TPC-B eseguita con questo script Jython. Prima di eseguire il test va creata la base dati lanciando lo script di creazione DB.

Benchmark TPC-B

Altro esempio e' il benchmark emp7. In realta' le possibilita' del Grinder sono molto piu' ampie che la semplice esecuzione di benchmark su una base dati.

The Grinder e' fornito un HTTP Proxy per la registrazione della navigazione su applicazioni web. Gli script registrati possono essere modificati e composti tra loro realizzando scenari molto completi. E' inoltre possibile preparare script con qualsiasi livello di complessita' programmando con il semplice e potente linguaggio Jython (Python+Java). I test vengono sottomessi da una batteria di Agenti (generalmente ospitati su sistemi differenti) coordinati da un'unica console. I parametri di esecuzione sono modificabili agendo su un semplice file di configurazione che consente di definire il test in ogni sua componente (eg. numero di run, utenti simulati, distribuzione dei tempi, variazione del think time, ...) I risultati raccolti sono di semplice intepretazione e verifica da parte degli utenti e programmatori. Infine i file che contengono i risultati dei test (gia' mediati e con il calcolo del TPS) sono facilmente importabili su uno spreadsheet per generare report e statistiche molto efficaci, ...

Per maggiori dettagli sul Grinder si puo' fare riferimento alla documentazione in linea sul sito ufficiale ed anche a questa paginetta in italiano.

Load Runner

Load Runner - architettura
Load Runner e' il piu' diffuso e noto strumento per l'esecuzione di benchmark. Load Runner e' stato sviluppato dalla Mercury che e' stata acquisita da HP nel 2006.

Load Runner - script
Nella fase iniziale si effettua la registrazione degli scenari (VuGen: Virtual User Generator) che possono essere poi programmati definendo cicli, rendez-vous, ...

La programmazione puo' essere effettuata con un interfaccia grafica o con un opportuno linguaggio di scripting.

I test vengono gestiti da una console centrale (Controller). Il Controller invia i comandi alle stazioni di test (Load Generator Agent o iniettori) che sottomettono le prove simulando il comportamento reale di centinaia o migliaia di utenti. La schedulazione dei test consente di definire in modo semplice test sequenziali, intervalli di ramp-up, ...

I dati vengono raccolti su un repository centrale che consente la loro sucessiva analisi. La reportistica generata da Load Runner e' molto completa ed integra i dati raccolti sui vari test con lo stato dei sistemi e delle basi dati interessati (se il tutto e' configurato opportunamente).

Oltre al sito ufficiale ho trovato questa concisa, tecnicamente assai completa, pagina su web.

Benchmark Factory

Benchmark Factory e' un prodotto della Quest che consente la conduzione di Benchmark complessi su DB relazionali con un interfaccia grafica semplice e completa. La Quest (ora acquisita da Dell) e' molto conosciuta per i suoi prodotti di gestione delle basi dati come TOAD e Spotlight. Con Benchmark Factory una serie di wizard guidano nella creazione degli scenari di test e la conduzione dei benchmark un modo semplice e veloce. Il software gestisce automaticamente il ramp-up delle sessioni, la raccolta delle statistiche e la generazione dei grafici dei risultati. Tra i benchmark che e' possibile generare con pochi click vi e' il TPC-B gia' descritto in precedenza. Il prodotto puo' essere acquisito con un numero variabile di utenti virtuali simulati ed utilizza una connessione diretta netX all'RDBMS Oracle.

JMeter

JMeter e' un'applicazione Open Source mantenuta da Apache che consente di effettuare test prestazionali. Sviluppata in Java ed inizialmente utilizzata per il test di applicazioni Web e' in realta' molto adatta anche per il test di basi dati connesse via JDBC.


Varie ed eventuali

Un benchmark non risolve tutto. Un'architettura o un'applicazione disegnate in modo errato possono essere misurate, i sistemi possono avere un tuning accurato, ... ma il problema va corretto alla fonte. Da questo punto di vista l'esecuzione di un benchmark standard o di un'applicazione con funzionalita' simili servono da raffronto.

Eseguire un test sulle prestazioni e' come andare da un oracolo. La cosa piu' importante non sono le risposte che ci da' ma le domande che noi facciamo!
Sembra una banalita' filosofica ma prima di analizzare al millesimo di secondo i risultati ottenuti su un complesso scenario eseguito da migliaia di utenti... e' meglio prima capire a cosa servono il sistema e le applicazioni, comprendere l'architettura dei vari componenti e realizzare cosi' un test significativo!

Per un test di carico significativo e' a volte necessario disporre di piu' macchine su cui lanciare gli agenti. Altrettanto importanti sono le connessioni di rete che debbono essere ad alta velocita' e connesse direttamente, o il piu' direttamente possibile, ai sistemi che si vogliono misurare. In caso contrario ci si puo' accorgere che le nostre misurazioni, con un meccanismo complesso e con lunghe rilevazioni, stanno solo rilevando qualcosa che e' gia' perfettamente noto come la banda della rete o le prestazioni del nostro PC!

Quando si effettuano i test e' opportuno (leggi necessario) monitorare il comportamento dei sistemi coinvolti con gli strumenti di controllo delle performance (eg. vmstat 5). Questo vale sia per i sistemi che ospitano le applicazioni sotto test (eg. web server, application server, DB Server), che per i sistemi su cui viene generato il carico che per eventuali sistemi di supporto (eg. proxy server, load balancer, ...).

Formule e trucchi

I dati ottenuti nell'esecuzione dei test possono essere estrapolati per cercare di effettuare previsioni sul comportamento di un sistema sotto condizioni differenti.

Teoricamente i test restituiscono valori solo per i casi testati. Ma quando la risposta di un sistema e' "regolare" (non presenta una varianza elevata) e' possibile utilizzare l'interpolazione per calcolare i risultati dei casi non sperimentati. Personalmente uso solo l'interpolazione lineare (l'unica che riesco a capire ;-).

In molti casi un sistema presenta un bottleneck. E spesso questo si comporta con un tempo di servizio che non dipende dalla coda. Oltre a semplificare l'analisi del sistema riducendola all'analisi di una sola coda e quindi trattandola con metodi analitici e' inoltre possibile indicare dove agire per poter scalare le prestazioni sino al livello necessario. In pratica si tratta di determinarne la massima quantita' di servizio ed utilizzare questo come fattore per la valutazione dell'impatto.

Ma quali sono le formule per l'analisi di una coda? Dipende dalla coda! La teoria parla di D/D/1, M/M/1 ... con un bel numero di casi e combinazioni.
Una formula utilizzata e' (utenti*richieste) / (periodo*TPS) dove il periodo e' in secondi ed il TPS e' relativo agli oggetti richiesti. In modo un po' piu' formale e' la legge dell'utilizzazione: U = XS. X ovviamente e' il Throughtput, S il tempo di servizio ed U l'utilizzazione. Molto utilizzata sono anche la formula di Little: N = XR (N numero medio di richieste, R tempo medio di risposta) e la Response Law per i sistemi chiusi R=N/X-Z (Z tempo di pensamento). Utilizzando la formula w=N/X e conoscendo il throughtput massimo, si riesce ad ottenere analiticamente il numero massimo di utenti concorrenti che soddisfano le condizioni desiderate. Ad esempio utilizzando la formula di Little e' possibile determinare il numero massimo di utenti simultanei (eg. con tempo di pensamento=10) che consentano un tempo di risposta medio inferiore ai 5 secondi.
Le Bound Analisys consentono di modellere il comportamento di sistemi complessi partendo da dati raccolti in modo molto semplice.

Naturalmente i risultati ottenuti con formule e calcoli non sono certi ma forniscono un'indicazione di massima. Per altro anche i dati di partenza non sono certi... ma ottenuti con un test. Tuttavia se le condizioni non cambiano in modo significativo il grado di affidabilita' e' comunque buono. Nel caso di variazioni significative (naturalmente ci si augura variazioni migliorative) su applicazioni o sul SW di base e' necessario effettuare nuove misurazioni.

Glossario di base

Nello strano mondo della simulazione, dell'analisi delle prestazioni, degli stress test, ... si utilizzano una serie di termini che e' importante conoscere:

Anche un po' di matematica e statistica non farebbero male: distribuzione normale, uniforme, Erlang, generazione di numeri casuali, esponenziale negativa, teoria delle code, bottleneck analysis, coda M/M/1 (e' la classificazione delle catene di Markov: famoso gioielliere degli zar), ... ma ve le risparmio!


Titolo: Benchmarking Relational DBMS
Livello: Avanzato (3/5)
Data: 1 Novembre 2005
Versione: 1.0.17 - 14 Febbraio 2013
Autore: mail [AT] meo.bogliolo.name