---
title: "UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB"
description: "UUID v4 causa 500× più page split di ID sequenziali in InnoDB. UUID v7 (RFC 9562) risolve il problema: HasUuids in Laravel 12 genera v7 di default, non più v4."
canonical: "https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html"
author: "Maurizio Fonte"
date: "2025-02-25T11:13:45+01:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Database"
tags: ["Database", "Backend Development"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Database](https://www.mauriziofonte.it/blog/cat/database.html) [Backend Development](https://www.mauriziofonte.it/blog/cat/backend-development.html)

# [UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB](https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html)

Martedì 25 Febbraio 2025, alle 11:13 ![UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB](https://www.mauriziofonte.it/image/001430200-refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.jpg)

In una piattaforma marketplace con migliaia di utenti attivi, la tabella `orders` usava UUID v4 come chiave primaria - una scelta fatta tre anni prima per evitare l'esposizione di ID sequenziali nelle API. Con 2.8 milioni di righe, le performance di insert erano degradate del 40% rispetto alle tabelle con ID auto-increment, e il file `.ibd` di InnoDB occupava quasi il doppio dello spazio atteso. La causa è documentata da [Percona](https://www.percona.com/blog/uuids-are-popular-but-bad-for-performance-lets-discuss/): l'inserimento casuale di UUID v4 in un indice B-tree clusterizzato causa page split continui - fino a 500× più frequenti rispetto a chiavi sequenziali - con pagine che si riempiono solo al 50-69% invece del 94%. La [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562.html), pubblicata nel maggio 2024, definisce UUID v7 come la soluzione: un timestamp a 48 bit (precisione al millisecondo) nei primi byte garantisce l'ordinamento cronologico, eliminando la frammentazione degli indici.

## Cosa cambia con HasUuids in Laravel 12 rispetto a Laravel 9/10?

Il trait [`HasUuids`](https://laravel.com/docs/12.x/eloquent), introdotto in Laravel 9.30, in Laravel 12 genera UUID v7 di default - il trait `HasVersion7Uuids`, aggiunto in Laravel 11, è stato rimosso e il suo comportamento è stato fuso in `HasUuids`. Per chi necessita del vecchio comportamento casuale, esiste ora `HasVersion4Uuids`. La distinzione è cruciale: `Str::orderedUuid()`, presente da Laravel 5.6, non genera UUID v7 reali - riordina i byte di un UUID v4 con `TimestampFirstCombCodec`, un approccio non conforme alla RFC.

Il model con UUID v7 in Laravel 12 è minimale:

```php
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    use HasUuids;

    /* HasUuids imposta automaticamente:
     * - $incrementing = false
     * - $keyType = 'string'
     * - newUniqueId() genera UUID v7 (RFC 9562)
     */

    protected $fillable = ['customer_id', 'total', 'status'];

    public function items(): HasMany
    {
        return $this->hasMany(OrderItem::class);
    }
}

/* Migrazione */
Schema::create('orders', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignId('customer_id')->constrained();
    $table->decimal('total', 10, 2);
    $table->string('status', 20);
    $table->timestamps();
});

Schema::create('order_items', function (Blueprint $table) {
    $table->uuid('id')->primary();
    $table->foreignUuid('order_id')->constrained()->cascadeOnDelete();
    $table->foreignId('product_id')->constrained();
    $table->integer('quantity');
    $table->decimal('unit_price', 10, 2);
    $table->timestamps();
});
```

`foreignUuid()` crea la colonna foreign key come `CHAR(36)` - lo stesso tipo della primary key UUID. `HasUuids` genera l'UUID v7 automaticamente nella callback `creating`, sia per la chiave primaria che per le colonne definite in `uniqueIds()`.

## Quando conviene migrare da ID auto-increment o UUID v4 a UUID v7?

La migrazione a UUID v7 per tabelle esistenti è un'operazione ad alto rischio che richiede la modifica della chiave primaria e di tutte le foreign key correlate. Conviene in tre scenari specifici:

Il primo è quando le API espongono ID sequenziali. Un endpoint `/api/orders/1547` rivela che esistono almeno 1547 ordini e permette attacchi di enumerazione. UUID v7 (`/api/orders/019480f4-8e2a-7b3c-...`) mantiene l'ordinamento interno ma non espone la cardinalità - un requisito che le [API REST](https://www.mauriziofonte.it/blog/post/api-rest-gestionali-laravel-sicurezza-pmi-vulnerabilita-legacy.html) di qualsiasi applicativo dovrebbero soddisfare.

Il secondo è quando le performance di insert degradano su tabelle grandi con UUID v4. Se `SHOW ENGINE INNODB STATUS` mostra un alto numero di page split nella sezione `INSERT BUFFER AND ADAPTIVE HASH INDEX`, UUID v7 risolve il problema alla radice - le insert tornano sequenziali come con auto-increment, con page fill al 94%.

Il terzo è in architetture distribuite dove più sistemi generano record per la stessa tabella. UUID v7 garantisce unicità globale senza coordinamento tra nodi - a differenza di auto-increment che richiede sequenze distribuite o offset.

Per tabelle piccole (<100K righe) con ID auto-increment e nessuna esposizione nelle API, la migrazione non porta benefici misurabili - l'overhead di `CHAR(36)` vs `BIGINT(8 byte)` sugli indici secondari è un costo senza ritorno.

## Errori comuni nella migrazione a UUID

Il primo errore è usare `CHAR(36)` senza valutare `BINARY(16)`. Un UUID come stringa occupa 36 byte; come binario, 16 byte. Su una tabella con 10 milioni di righe e 5 indici secondari (ognuno include la primary key in InnoDB), la differenza è significativa. [Percona documenta](https://www.percona.com/blog/store-uuid-optimized-way/) che una tabella da 1 miliardo di righe con UUID `CHAR(36)` occupava ~1TB; riorganizzata con formato binario, è scesa a ~450GB. Laravel usa `CHAR(36)` di default con `$table->uuid()` - per sistemi ad alto volume, la conversione a `BINARY(16)` con cast custom nel model è un'ottimizzazione necessaria.

Il secondo è confondere UUID v7 con ULID. Entrambi hanno un timestamp a 48 bit, ma ULID usa codifica base32 (26 caratteri) e non è conforme alla RFC UUID. In Laravel, `HasUlids` genera ULID - un formato più compatto e URL-friendly ma incompatibile con colonne `UUID` in PostgreSQL e con librerie che validano il formato UUID. Per nuovi progetti, UUID v7 è la scelta standard.

Il terzo è migrare la primary key in produzione senza piano di rollback. La sequenza corretta: aggiungere colonna UUID v7 nullable → popolare con script batch → aggiornare le foreign key → switch della primary key. Ogni step deve essere reversibile. I [test automatici](https://www.mauriziofonte.it/blog/post/test-automatici-codebase-php-legacy-senza-riscrittura.html) delle relazioni Eloquent devono passare sia con la vecchia che con la nuova chiave durante la transizione.

La scelta della chiave primaria determina le performance di ogni query che tocca quella tabella - è una decisione che costa quasi nulla se presa all'inizio del progetto e può costare settimane di migrazione se rimandata. L'[ottimizzazione delle query Eloquent](https://www.mauriziofonte.it/blog/post/ottimizzazione-query-eloquent-laravel-performance-gestionali-ecommerce-pmi.html) include l'analisi degli indici e dei page split come primo step diagnostico. Per conoscere il mio approccio alla progettazione di database per applicativi Laravel, visita [la mia pagina professionale](https://www.mauriziofonte.it/pages/about). Se le performance del tuo database degradano con la crescita e sospetti che la strategia di chiavi primarie sia la causa, [contattami per una consulenza dedicata](https://www.mauriziofonte.it/pages/contattami) - partiamo dall'analisi degli indici InnoDB e dalla valutazione dell'impatto della migrazione.

Dall'articolo al lavoro vero

Se questo server lo devi tenere in piedi tu

Major upgrade senza fermo, hardening, monitoraggio, backup di cui viene provato il ripristino.

[Vedi come lavoro sui server](https://www.mauriziofonte.it/servizi/consulenza-it-devops.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteStrategy pattern in Laravel: selezione dinamica di implementazioni con Service Container, contextual binding e Pennant24 Febbraio 2025](https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html)[Articolo successivo »Event discovery in Laravel 12: da EventServiceProvider a auto-discovery per listener disaccoppiati e testabili26 Febbraio 2025](https://www.mauriziofonte.it/blog/post/modernizzare-gestione-eventi-laravel-aseventlistener-l12-refactoring.html)

### Articoli correlati a UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB

- [Costruire API webhook robuste in Laravel: delivery garantita, retry e sicurezza](https://www.mauriziofonte.it/blog/post/api-webhook-robuste-laravel-delivery-garantita-retry-sicurezza.html)

  Un sistema di webhook che invia notifiche a 200 integratori terzi non può permettersi delivery inaffidabile. Ho costruito un sistema con coda · 13 Marzo 2026

  ![Costruire API webhook robuste in Laravel: delivery garantita, retry e sicurezza](https://www.mauriziofonte.it/image/003030070-api-webhook-robuste-laravel-delivery-garantita-retry-sicurezza.jpg)
- [Refactoring database MySQL su Laravel: report da 47 minuti a 11 secondi senza upgrade hardware](https://www.mauriziofonte.it/blog/post/rifacimento-strategico-database-mysql-laravel.html)

  Un database MySQL da 12 GB su un VPS Contabo, un report mensile che impiegava 47 minuti, 38 indici su una tabella di cui 12 mai utilizzati, e · 26 Giugno 2025

  ![Refactoring database MySQL su Laravel: report da 47 minuti a 11 secondi senza upgrade hardware](https://www.mauriziofonte.it/image/001870070-rifacimento-strategico-database-mysql-laravel.jpg)
- [Integrazione con sistemi ERP legacy tramite API PHP: pattern e insidie comuni](https://www.mauriziofonte.it/blog/post/integrazione-erp-legacy-api-php-pattern-insidie.html)

  Ho integrato quattro gestionali italiani diversi con applicazioni Laravel: ognuno aveva una API SOAP degli anni 2000 con documentazione parziale · 20 Marzo 2026

  ![Integrazione con sistemi ERP legacy tramite API PHP: pattern e insidie comuni](https://www.mauriziofonte.it/image/002370070-integrazione-erp-legacy-api-php-pattern-insidie.jpg)
- [LLM per la generazione di migration SQL: gestire l'evoluzione dello schema senza errori](https://www.mauriziofonte.it/blog/post/llm-generazione-migration-sql-evoluzione-schema-database.html)

  Ho iniziato a usare Claude per generare migration SQL complesse su schemi di database con 200 tabelle e vincoli FK intricati. Il risultato non · 26 Febbraio 2026

  ![LLM per la generazione di migration SQL: gestire l'evoluzione dello schema senza errori](https://www.mauriziofonte.it/image/002470070-llm-generazione-migration-sql-evoluzione-schema-database.jpg)
- [Eloquent ORM avanzato: evitare le query N+1 nei gestionali Laravel](https://www.mauriziofonte.it/blog/post/eloquent-orm-avanzato-evitare-n1-query-gestionali-laravel.html)

  Le query N 1 non rompono nulla in sviluppo e affondano la produzione: un gestionale con molte relazioni le genera senza accorgersene. Come individuarle · 4 Settembre 2026

  ![Eloquent ORM avanzato: evitare le query N+1 nei gestionali Laravel](https://www.mauriziofonte.it/image/005230070-eloquent-orm-avanzato-evitare-n1-query-gestionali-laravel.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html

Pubblicato: 2025-02-25 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
