Problemi di Datatype nei database. Come evitare errori di performance e overflow

Una scelta sbagliata del datatype può causare gravi problemi di performance e overflow nel database. Scopri come conversioni implicite e limiti di tipo possono bloccare le tue applicazioni e impara a risolverli con le strategie giuste.

Le applicazioni aziendali odierne si basano sui dati e sui software che li gestiscono. Nel processo di digitalizzazione dei flussi di lavoro e nell’ottimizzazione delle query, gli sviluppatori spesso si concentrano esclusivamente sui requisiti di business e sulla logica delle interrogazioni al database, trascurando un aspetto fondamentale: la scelta sui tipi di dato scelti per il database e l’applicazione. Questa sottovalutazione non è un semplice dettaglio implementativo, ma un errore strategico e una lacuna progettuale che può causare prestazioni scadente, insoddisfazione degli utenti e, nei casi più critici, interruzioni di servizio (downtime) imprevisti compromettono la continuità operativa.
 

Il Contesto

Nella progettazione delle applicazioni, le software house si concentrano spesso sulla digitalizzazione dei flussi aziendali e sulle logiche interne dell'applicativo, trascurando la scelta in merito al tipo di dato da assegnare ai vari componenti. Questa negligenza nella scelta può avere origine sia da un approccio superficiale, che sottovaluta l’importanza di questi aspetti architetturali; sia dal tentativo di offrire una compatibilità universale tra sistemi diversi (e diversi clienti) che spinge verso l’adozione di tipi di dato generici a discapito della specificità. Ciò può portare principalmente a due problematiche significative.

1. Conversioni implicite - I database devono convertire i dati. Ciò è quello che generalmente accade quando c’è una mancata corrispondenza dei tipi di dato tra tabelle diverse o tra i tipi di dati forniti dall'applicazione e quelli con cui i dati sono memorizzati nelle tabelle. Le conversioni sono operazioni con ripercussioni particolarmente onerose che aumentano in modo significativo i tempi di esecuzione delle query.

2. Rischi di overflow a lungo termine - Per le logiche di un'applicazione, frequentemente vengono utilizzate colonne di tabelle con funzione di contatore o identificatore univoco. Quando i tipi di dato vengono assegnati senza valutare il carico applicativo previsto e i volumi di dati che il database dovrà gestire, la scelta di un datatype inadeguato può causare un overflow, esaurendo le combinazioni possibili. Di conseguenza si verifica un'interruzione dei servizi applicativi, costringendo i team IT a interventi d’emergenza, spostandosi rapidamente su un database di supporto e perdendo dati, o utilizzando soluzioni temporanee e artificiose per aggirare il problema.

I Problemi

In questo contesto non è sempre facile capire (e gestire) cosa succede durante la fase di build, in cui le dipendenze compile di Java vengono gestite abbastanza agilmente da gradle, e quello che invece succede durante le fasi di pacchettizzazione o di start dei bundle.

In queste due fasi il compilatore prima e il portale poi devono essere in grado di avere tutte le dipendenze necessarie per potere eseguire il codice del modulo a cui stiamo lavorando.
 

Il problema di Performance

Per capire nel concreto come si manifesti il problema di performance esaminiamo ora un caso verificatosi su un'istanza di database SQL Server. Quanto è qui illustrato può essere esteso ad altri tipi di database relazionali. 

Durante l'analisi di un'istanza SQL Server, ci siamo imbattuti in una query di questo tipo:

DECLARE @codPratica  nvarchar(4000) 
SELECT TOP 1 servizio  
FROM servizi AS [f]  
WHERE [f].[codice_pratica] = @codPratica 

scelta datatype performance
Grafico 1 - Dati riferiti ai due mesi analizzati

Analizzando la definizione della tabella, si è notato che la colonna codice_pratica era di tipo varchar(20). Poiché questa colonna veniva confrontata con un parametro di tipo diverso, ovvero nvarchar(4000), il database (in questo caso SQL Server) era costretto per ogni valore del campo codice_pratica ad applicare una funzione di conversione implicita. Questo impediva al database di utilizzare in modo efficiente gli indici per eseguire la query, costringendolo a effettuare operazioni inefficienti come i full scan. In termini di database administrator, si può affermare che il predicato di questa query era non-sargable, ovvero impediva al database di utilizzare gli indici, nonostante non ci fossero funzioni esplicite.

Nel caso della query sopracitata, il problema appariva nel piano di esecuzione della query come uno step di Index Scan (scansione completa dell'indice) invece di Index Seek (ricerca puntuale sull'indice).

Questo tipo di problema per i database OLTP (Online Transaction Processing) è molto impattante. In questi database, infatti, è fondamentale avere query veloci e performanti per sostenere l'elevato numero di richieste giornaliere. Anche un valore di 100 ms, che potrebbe sembrare accettabile, può costituire un serio problema, causando l'accumulo di attese e blocchi. Nel caso in esame, ad esempio, il database generava circa 30 ore di attesa su questa query, rallentando i processi.

Il problema, inoltre, è spesso latente ed eseguire operazioni superficiali di troubleshooting può portare a soluzioni sbagliate e costose, quando non persino a un peggioramento delle performance.

Spesso, infatti, il rallentamento nelle query è causato da una conversione implicita dei dati che non viene riconosciuta come sua vera origine. Di conseguenza, si interviene tentando di trasformare scansioni di tabelle in ricerche puntuali mediante metodi inefficaci, come l’aggiunta di nuovi indici. Questi, oltre a non risolvere il problema, possono appesantire ulteriormente le operazioni di scrittura a causa della necessità di aggiornamenti continui.

In altri casi, si arriva erroneamente a pensare che l’unica soluzione risieda nel potenziamento dell’hardware del server. Tale approccio comporta un aumento dei costi e della complessità gestionale senza però ottimizzare realmente le performance della query originale né sfruttare al meglio l’infrastruttura esistente.

Il problema del raggiungimento del limite massimo di combinazioni per i contatori 

Per quanto riguarda invece il secondo problema, proponiamo un caso tipico relativo all’uso di sequenze numeriche per l’aggiornamento delle tabelle. L’incidente specifico ha interessato un software Java che interagiva con un database Oracle, ma il principio sottostante è applicabile a molti altri ambienti.

Il caso è emerso a seguito di una segnalazione critica da parte del team applicativo di un cliente: il software si era improvvisamente bloccato, impedendo qualsiasi nuovo inserimento di documenti in una tabella chiave, interrompendo completamente l'operatività degli utenti. L'origine del problema è stata rapidamente individuata in una sequenza progressiva, creata con il seguente comando SQL

CREATE SEQUENCE id
 START WITH     0
 INCREMENT BY   1
 NOCACHE
 NOCYCLE;

A seguito della prima analisi, si è riscontrato che né la sequenza del database (che di default supporta valori fino a 1028 ) né il tipo di dato della colonna di destinazione rappresentavano un problema.

L'indagine ha invece rivelato che la root cause risiedeva nell'applicazione Java. Il tipo di dato utilizzato nel software per gestire il valore proveniente dalla sequenza era un INT. Questo tipo di dato a 32 bit ha un limite massimo intrinseco di 2.147.483.647 (ovvero 231-1). Non appena la sequenza del database ha superato questa soglia, l'applicazione non è più stata in grado di processare il valore, generando un errore bloccante che ha fermato ogni nuova transazione.

Le conseguenze di questo disallineamento tecnico sono state immediate e hanno causato un grave disservizio scatenando un effetto a catena:

  • Blocco operativo: l'impossibilità di inserire dati ha paralizzato le attività per diverse ore.
  • Insoddisfazione dei clienti: il disservizio diretto ha generato frustrazione e numerose lamentele.
  • Impatto sul team: i dipendenti sono rimasti bloccati nell’impossibilità di portare a termine le loro attività, alimentando la pressione e la frustrazione interna.
  • Perdita di revenue: l'interruzione delle operazioni si è tradotta in una perdita economica diretta per l'azienda.

Questo incidente, quindi, mette in luce come una scelta di progettazione a livello di codice, se non allineata con la potenziale crescita dei dati, può portare a conseguenze operative ed economiche critiche.

Gli obiettivi

A seguito delle analisi delle due problematiche, sono stati definiti specifici obiettivi per ciascuna.

  • Nel primo caso, l'obiettivo era trovare soluzioni a basso impatto che risolvessero le cause alla radice. La strategia consisteva nell'ottimizzare le query fondamentali dei processi OLTP per ridurre in modo consistente i tempi di attesa del database e, di conseguenza, ridurre i costi sia on-premise che in cloud.
  • Nel secondo caso, l’obiettivo era trovare una soluzione rapida e di semplice implementazione. La priorità era minimizzare l'impatto sugli utenti e far ripartire il business nel minor tempo possibile, contenendo il disagio applicativo.

La Soluzione

Per affrontare il primo problema (performance), esistono principalmente due soluzioni, elencate in ordine di preferenza:

  1. Correggere il tipo del parametro passato in input alla query.
  2. Convertire il dato passato in input alla query prima di eseguirla.

In entrambi i casi l’obiettivo è allineare i datatype dei dati da confrontare nel predicato, in particolare nella seguente clausola WHERE:

    WHERE [f].[codice_pratica] = @codPratica    

Nel caso specifico, si mira a garantire che il parametro @codPratica abbia il datatype varchar(20).

La prima soluzione è indubbiamente la più efficace, perché interviene direttamente sulla struttura dell'applicativo, ottimizzando l'integrazione con il database. In questo modo si evita di effettuare due operazioni, anziché una, sul dato di input. Tuttavia, se risulta più semplice modificare il codice rispetto al tipo di dato in entrata, anche la seconda strada può rappresentare una soluzione valida.

Se il datatype scelto per la colonna è corretto, l'errore è infatti di progettazione dell'applicativo a monte. Pertanto si suggerisce di eseguire la modifica sull’applicativo, coinvolgendo il team appropriato. In questo modo si evita di alterare il database o di ricorrere a workaround che potrebbero risultare particolarmente impattanti. 

Modificare la definizione di una tabella, ad esempio, è un'operazione che comporta l'irraggiungibilità della tabella durante le operazioni e, data la frequenza di interrogazione, un periodo di downtime. L'utilizzo di un workaround con una tabella di appoggio, invece, rappresenta una soluzione inefficace, in quanto aggiunge un passaggio ulteriore a una situazione già critica.

Per il secondo problema (software fermo) sono state valutate quattro possibili soluzioni, presentate di seguito in ordine di complessità crescente.
 

 

Soluzioni Valutate per Overflow

1. Reset della sequenza a zero

Questa opzione prevede di far ripartire la sequenza da capo. Sebbene fosse la soluzione più semplice dal punto di vista tecnico, è stata scartata immediatamente. Il valore generato dalla sequenza è utilizzato come chiave primaria e il reset avrebbe creato valori duplicati, violando il vincolo di unicità della tabella.

2. Utilizzo di una sequenza negativa

Questa alternativa consiste nel modificare la sequenza a livello database per farla procedere con valori negativi. La configurazione sarebbe stata la seguente:

CREATE SEQUENCE id
 START WITH     -1
 INCREMENT BY   -1
 NOCACHE
 NOCYCLE;

In questo modo, i nuovi valori inseriti sarebbero proseguiti da -1 in poi, garantendo l'unicità e sbloccando l'operatività. Tuttavia, questa soluzione rappresentava un workaround temporaneo, poiché anche l'intervallo di numeri negativi è finito. Va inoltre verificato che la colonna non sia usata per l'ordinamento.

3. Modifica strutturale del software

La soluzione più corretta dal punto di vista architetturale è modificare il codice sorgente, cambiando il tipo di dato utilizzato in Java da INT (32 bit) a LONG (64 bit). Questo avrebbe ampliato enormemente il numero di valori disponibili, risolvendo il problema in modo permanente e garantendo un'operatività pressoché infinita.

4. Ricreazione di una nuova struttura database

Questa è l'opzione più drastica, che prevede di creare un nuovo database e ripartire da zero. È stata subito esclusa. L'impatto sarebbe stato insostenibile, comportando la perdita totale dei dati storici e tempi di implementazione molto lunghi.

Dopo la prima valutazione, la scelta si è quindi ridotta alle opzioni 2 e 3.

Il passaggio a LONG 64 bit (la soluzione 3) è stata scelta come la strategia risolutiva a lungo termine. Questo intervento garantisce, infatti, la coerenza dei dati, mantiene lo storico e risolve il problema alla radice. Tuttavia, la sua implementazione richiedeva un ciclo completo di rilascio software (ricompilazione, test e deployment in produzione), un processo incompatibile con l'urgenza dettata dal fermo dei sistemi.

Di conseguenza, per minimizzare il downtime e ripristinare il servizio nel più breve tempo possibile, è stato scelto di implementare il workaround della soluzione 2, con la forte raccomandazione di pianificare un intervento definitivo simile alla soluzione 3 in un rilascio futuro.

I Risultati

Si presentano di seguito i risultati ottenuti per le due problematiche presentate.

Per il primo problema, durante l’analisi erano stati eseguiti dei test di performance usando la variabile @codPratica come nvarchar(4000) e varchar(20) (il datatype della colonna codice_pratica), mettendo in evidenza il netto miglioramento della query che utilizzava il datatype varchar(20). Si riporta di seguito il grafico presentato al cliente che mostra in scala logaritmica i tempi di esecuzione su 1000 tentativi della query con il datatype originale (in blu) confrontati con i tempi di esecuzione della query che utilizzava lo stesso valore per la variabile @codPratica ma con datatype varchar(20) (in rosso).

Conversioni implicite SQL e tempi lunghi
Grafico 2 - Tempi di esecuzione

Come si può osservare, i tempi di esecuzione con il parametro che utilizza il datatype corretto erano quasi nulli, mentre i tempi di esecuzione medi per il datatype originale si aggiravano attorno ai 100 ms.

Si riporta di seguito invece il grafico relativo al guadagno percentuale dei tempi di esecuzione della query con il parametro convertito rispetto ai tempi di esecuzione della query con il parametro con datatype originale. Come si osserva il guadagno è quasi costantemente al 100%.
 

Ottimizzazione performance database dopo errore overflow database
Grafico 3 - Nuovi tempi di esecuzione

A fronte di questi risultati, il team applicativo ha modificato il datatype del parametro in input. Nei giorni immediatamente successivi alla modifica, il tempo di esecuzione della nuova query è stato di circa 0,05 ms. Ad oggi, la query è talmente veloce da non essere più rilevata dal monitoraggio.

A dimostrazione di quanto si è illustrato, si riportano di seguito i dati delle attese totali e delle 15 query più impattanti a cavallo della modifica (effettuata il 24 aprile 2025) che mostrano che le circa 20/30 ore di attesa di tipo Memory/CPU sono scomparse a seguito della modifica.

Scelta datatype query performanti
Grafico 4 - Tempi di attesa totali

 

Velocizzare query con giusti datatype
Grafico 5 - Tempi delle 15 Query più impattanti

La query analizzata è la query identificata con l’ID 4370546729.

Per quanto riguarda il secondo problema, il risultato ottenuto è stato il ripristino dell'operatività del business nel minor tempo possibile, limitando al minimo gli impatti applicativi. Questa rapidità di intervento ha permesso di contenere il disservizio e le relative perdite economiche, in linea con gli SLA e le esigenze aziendali.  È importante sottolineare, tuttavia, che la soluzione adottata è un workaround temporaneo. Se non si vi sarà un intervento per modificare alla radice il datatype del software, il problema si ripresenterà inevitabilmente. A quel punto, non sarebbe però più possibile applicare soluzioni palliative e diventeranno necessarie misure drastiche come le soluzioni 3 e 4 presentate nell’apposita sezione.

Problemi di datatype nei database: le raccomandazioni finali
 

Nell’operatività quotidiana, i dipartimenti IT sono sempre più impegnati a soddisfare le richieste del business, sviluppare nuove funzionalità e ottimizzare il codice. In questo contesto, la progettazione dei database e la scelta accurata dei tipi di dato, sia per le colonne del database sia per i dati gestiti dal software, rischiano di passare in secondo piano. Tuttavia, questa decisione rappresenta un elemento cruciale che condiziona direttamente le prestazioni, la stabilità e la longevità di qualsiasi sistema software.

Una selezione affrettata o superficiale del datatype può innescare una serie di problemi latenti, che si manifestano nel tempo come rallentamenti e cali di performance del database. Questi problemi spesso costringono a interventi correttivi urgenti e dispendiosi, nonché a una complessiva revisione dell’ottimizzazione delle query e della struttura dati.

Non di rado, nell’incapacità di individuare la vera radice del problema legata ai datatype, si assume erroneamente che la soluzione consista nell’aumento delle risorse hardware. Tale approccio, oltre a incrementare inutilmente i costi, nasconde l’inefficienza sottostante senza risolverla.

Una visione miope che trascura l’infrastruttura, il volume previsto di dati e le reali esigenze di business può avere conseguenze ancora più severe. Tipi di dato incompatibili o con range di valori esauriti possono provocare fermi improvvisi del sistema, interrompendo l’operatività quotidiana e causando rilevanti danni economici e di reputazione.

La scelta del tipo di dato non è pertanto un dettaglio tecnico secondario, ma una decisione strategica che impatta direttamente sull’efficienza operativa e sui costi. Una progettazione attenta e mirata all’ottimizzazione dei tipi di dato in database è fondamentale per garantire l’affidabilità e la performance del software nel lungo termine, ottimizzando al contempo i costi, sia in infrastrutture on-premise sia in ambienti cloud.
 

 

 

Ti è piaciuto quanto hai letto? Iscriviti a MISPECIAL, la nostra newsletter, per ricevere altri interessanti contenuti.

Iscriviti a MISPECIAL

 

Contenuti simili
DIGITAL ENTERPRISE
Sep 01, 2025

Come gestire le dipendenze OSGi su Liferay 7.4. Questa guida pratica ti mostra come risolvere i conflitti e integrare librerie di terze parti nei tuoi moduli, con esempi concreti come Apache POI.

DIGITAL ENTERPRISE
Jul 29, 2025

L'Observability è un approccio che promette di offrire una visione completa e robusta del proprio ecosistema dati. Ma cosa significa esattamente e perché sta diventando così cruciale per le aziende?