Perchè usare SQLite per un sito web o un Blog

SQLite è un sistema DBMS compatibile con lo standard SQL e totalmente ACID. Scopriamo perchè usarlo come database per un sito web dinamico.

Articolo pubblicato il 06/02/2012

SQLite è una libreria scritta in linguaggio C che implementa un DBMS di tipo ACID ed è totalmente compatibile con lo standard SQL. SQLite è progettato per essere direttamente implementato all'interno di un'applicazione e non appartiene quindi alla famiglia di Database di tipo client/server; questo tipo di architettura lo rende un sistema limitato ed utilizzabile soltanto in alcune categorie di applicazioni, prima fra tutte l'integrazione in applicazioni di tipo desktop (Firefox e Thunderbird ad esempio ne fanno ampio utilizzo). Può anche essere utilizzato come backend per siti web dinamici e blog di medie dimensioni grazie alle sue ottime performance e alle funzionalità offerte che, pur essendo ridotte rispetto a sistemi come MySQL o PostgreSQL, sono comunque sufficienti per memorizzare e gestire la limitata quantità di dati necessaria ad un blog.

Le performance di SQLite

Le performance di SQLite sono ottime ed in parecchie occasioni risultano addirittura superiori a sistemi come MySQL e PostgreSQL. Le eccezionali performance di SQLite sono dovute alla sua semplicità e leggerezza: l'intera libreria occupa appena 500 KB e, cosa più importante, il database viene direttamente caricato nell'applicazione non coinvolgendo quindi alcun tipo di connessione di rete che causerebbe dell'overhead.

Il Locking durante le operazioni di I/O

Uno dei principali limiti (a livello di performance) di SQLite è la Concorrenza (Concurrency). SQLite implementa un sistema di Locking a 5 stati (UNLOCKED, SHARED, RESERVED, PENDING, EXLUSIVE) che sebbene garantisca un ottimo livello di concorrenza in lettura (possono essere eseguiti molti SELECT contemporaneamente) non riesce a garantire la lettura dei dati se un processo sta scrivendo sul file contenente il DB. Il sistema di locking agisce infatti sul file stesso e durante un lock di tipo EXCLUSIVE o PENDING non accetta alcun lock di tipo SHARED bloccando di fatto la lettura del database - nel caso del lock PENDING le letture già avviate vengono comunque portate a termine, viene però respinta qualsiasi nuova lettura.

I cinque stati di locking possono essere semplicemente schematizzati:

UNLOCKED: Nessun lock, il database può essere liberamente aperto in lettura o scrittura.
SHARED: Qualche processo sta leggendo il database, è possibile leggere ma non scrivere. Nessun processo può scrivere sul Database se esiste uno o più lock SHARED.
RESERVED: Un processo ha intenzione di scrivere nel futuro. Possono essere eseguite operazioni di lettura ma nessun altro processo può richiedere un lock di tipo RESERVED.
PENDING: Un processo deve scrivere il prima possibile ma è in attesa che vengano terminate le letture già in esecuzione. Non può essere accettato alcun lock di tipo SHARED (o RESERVED).
EXCLUSIVE: Un processo sta scrivendo, non può essere accettato alcun lock, il lock EXCLUSIVE è per l'appunto esclusivo.

SQLite è progettato per minimizzare al massimo il tempo in cui un database viene tenuto nello stato di lock EXCLUSIVE, ed in caso di scritture di modeste dimensioni, un database SQLite mantiene lo stato di EXCLUSIVE per poche decine di millisecondi.

Una soluzione al problema della concorrenza: il WAL

A partire dalla versione 3.7.0 di SQLite è stata implementata la tecnologia WAL (Write-Ahead Logging) che rivoluzionando il sistema di COMMIT, rende l'intero DBMS molto più performante nelle situzioni ad alta concorrenza di letture e scritture: la tecnologia WAL permette la lettura e la scrittura contemporanea evitando quindi i lock di tipo EXCLUSIVE. Il WAL agisce sul journaling e per attivarlo è sufficiente eseguire questo comando in una connessione attiva al Database: PRAGMA journal_mode=WAL;.
Per disattivare il WAL sarà necessario tornare al journaling di default con questo comando: PRAGMA journal_mode=DELETE;.
Ricordate che il WAL resta attivo anche dopo aver chiuso la connessione al database ed è quindi, se necessario, disattivarlo manualmente. È comunque sconsigliato utilizzare il WAL nelle applicazioni che fanno principalmente uso di letture ed eseguono poche scritture: è dimostrato che il WAL in lettura è più lento dell'1-2% rispetto al tradizionale sistema di journaling.

Perchè usare SQLite

Il poter offrire un'ottima concorrenza in lettura a scapito di quella in scrittura, rende SQLite una valida alternativa per un blog o un sito web dinamico rispetto a sistemi più avanzati, che molto spesso ne rendono l'architettura assai più complessa delle reali necessità. Se amministrate un blog, il vostro Database verrà principalmente usato in lettura dato che le uniche operazioni di scrittura avvengono durante il salvataggio di un post o di un commento. Sul sito ufficiale di SQLite si legge che SQLite può facilmente essere utilizzato come DBMS per siti web che generano ogni giorno un numero di hits compreso tra 100'000 e 1'000'000 e molto probabilmente il vostro sito rientra in questo range di hits.

È quindi importante considerare l'utilizzo di SQLite come backend per il vostro blog e nella maggior parte dei casi è possibile notare un miglioramento delle performance semplificando contemporaneamente l'intera architettura del vostro applicativo web.

Usare SQLite in PHP

Per utilizzare SQLite in PHP è consigliabile utilizzare la libreria PDO che è integrata all'interno di PHP ed è stabilmente mantenuta dalla comunità. Inoltre PDO è compatibile con più DBMS rendendo più semplici eventuali porting dell'intera piattaforma ad altri Database. Il seguente codice permette di creare un DB sqlite, creare una tabella ed inserire dei dati all'interno della tabella creata.

// Creare un file vuoto con touch()
touch("testdb.sqlite");
// Conettersi al database sqlite utilizzando PDO
$db = new PDO('sqlite:testdb.sqlite');
// Eseguire una Query senza variabili
$db->exec("CREATE TABLE IF NOT EXISTS test (id INT, text TEXT)");
/* Se è necessario inserire variabili all'interno della query è preferibile l'utilizzo del metodo prepare() (prepared statement) che accettando l'operatore ? permette un quoting automatico delle variabili evitando attacchi di tipo SQL injection */
$sql = $db->prepare("INSERT INTO test (id, text) VALUES(?, ?)");
/* Utilizzando il metodo execute() è possibile passare come argomento un array contenente le variabili da inserire al posto dell'operatore ?*/
$id = 1;
$text = "hello folks";
$sql->execute(array($id, $text));
// Per leggere i dati dal database è sufficiente:
$sql = $db->prepare("SELECT * FROM test");
$sql->execute();
$data = $sql->fetchAll();
foreach($data as $row){
    print_r($row);
}

Usare SQLite in Python

Usare SQLite in Python è ugualmente semplice e la libreria sqlite3 offre in'interfaccia completa - compatibile con le specifiche DB-API 2.0 - per SQLite. Di seguito del semplice codice per creare un DB SQLite ed eseguire alcune query.

# Importare la libreria sqlite3
import sqlite3
# Il metodo .connect() permette di accedere ad un database già creato oppure, nel caso il file specificato non esista, si prende cura della sua creazione Il metodo .connect() non sovrascrive database già esistenti.
conn = sqlite3.connect("testdb.sqlite")
# Per poter eseguire query nel db è necessario creare un cursore utilizzando il medoto .cursor()
c = conn.cursor()
# Adesso è possibile eseguire delle query utilizzando il metodo execute()
c.execute("CREATE TABLE IF NOT EXISTS test (id INT, text TEXT)")
# Per inserire delle variabili all'interno di una query è SEMPRE preferibile utilizzare l'operatore ? e passare una tupla come secondo argomento del metodo .execute()
c.execute("INSERT INTO test (id, text) VALUES(?, ?)", (2, "hello from python"))
# È poi importante eseguire il metodo .commit() che scrive i dati nel DB. Senza averlo eseguito, i dati scritti non sono disponibili alle altre connessioni attive sul DB.
conn.commit()
# Per leggere i dati è invece sufficiente
c.execute("SELECT * FROM test")
for i in c:
 print i
#Per chiudere il cursore basta chiamare il metodo .close(). Ricordatevi di chiamare il metodo .commit() prima del .close(), altrimenti tutte le scritture andranno perse.
c.close()
Hai trovato utile questo articolo?
Lasciaci un tuo sincero feedback