PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

postgresql genera una sequenza senza interruzioni

Le sequenze non generano insiemi di numeri privi di gap e non c'è davvero modo di farli fare perché un rollback o un errore "utilizzeranno" il numero di sequenza.

Ho scritto un articolo su questo tempo fa. È diretto a Oracle, ma riguarda in realtà i principi fondamentali dei numeri privi di gap e penso che lo stesso si applichi qui.

Bene, è successo di nuovo. Qualcuno ha chiesto come implementare un requisito per generare una serie di numeri senza gap e uno sciame di oppositori è sceso su di loro per dire (e qui parafrasando leggermente) che questo ucciderà le prestazioni del sistema, è raramente un requisito valido , che chiunque abbia scritto il requisito è un idiota bla bla bla.

Come ho sottolineato nel thread, a volte è un vero e proprio requisito legale generare serie di numeri prive di gap. I numeri di fattura per le oltre 2.000.000 di organizzazioni nel Regno Unito che sono registrate con partita IVA (imposta sulle vendite) hanno un tale requisito e il motivo è piuttosto ovvio:rende più difficile nascondere la generazione di entrate dalle autorità fiscali. Ho visto commenti sul fatto che è un requisito in Spagna e Portogallo e non sarei sorpreso se non fosse un requisito in molti altri paesi.

Quindi, se accettiamo che sia un requisito valido, in quali circostanze le serie di numeri prive di gap* sono un problema? Il pensiero di gruppo spesso ti fa credere che lo sia sempre, ma in realtà è solo un potenziale problema in circostanze molto particolari.

  1. La serie di numeri non deve avere spazi vuoti.
  2. Più processi creano le entità a cui è associato il numero (es. fatture).
  3. I numeri devono essere generati al momento della creazione dell'entità.

Se tutti questi requisiti devono essere soddisfatti, allora hai un punto di serializzazione nella tua applicazione e ne parleremo tra poco.

Per prima cosa parliamo dei metodi per implementare un requisito di serie di numeri se puoi eliminare uno qualsiasi di questi requisiti.

Se la tua serie di numeri può avere spazi vuoti (e hai più processi che richiedono la generazione istantanea del numero), usa un oggetto Oracle Sequence. Sono prestazioni molto elevate e le situazioni in cui ci si può aspettare divari sono state discusse molto bene. Non è troppo difficile ridurre al minimo la quantità di numeri saltati compiendo sforzi di progettazione per ridurre al minimo la possibilità di un errore di processo tra la generazione del numero e il commit della transazione, se è importante.

Se non disponi di più processi che creano le entità (e hai bisogno di una serie di numeri priva di gap che deve essere generata istantaneamente), come potrebbe essere il caso con la generazione batch di fatture, allora hai già un punto di serializzazione. Questo di per sé potrebbe non essere un problema e potrebbe essere un modo efficiente per eseguire l'operazione richiesta. Generare i numeri senza gap è piuttosto banale in questo caso. Puoi leggere il valore massimo corrente e applicare un valore incrementale a ogni entità con un certo numero di tecniche. Ad esempio, se stai inserendo un nuovo batch di fatture nella tabella delle fatture da una tabella di lavoro temporanea, potresti:

insert into
  invoices
    (
    invoice#,
    ...)
with curr as (
  select Coalesce(Max(invoice#)) max_invoice#
  from   invoices)
select
  curr.max_invoice#+rownum,
  ...
from
  tmp_invoice
  ...

Ovviamente proteggeresti il ​​tuo processo in modo che una sola istanza possa essere eseguita alla volta (probabilmente con DBMS_Lock se stai usando Oracle) e proteggeresti la fattura # con un vincolo di chiave univoco e probabilmente controlleresti i valori mancanti con un codice separato se ci tieni davvero, davvero.

Se non hai bisogno della generazione istantanea dei numeri (ma ne hai bisogno senza interruzioni e più processi generano le entità), puoi consentire la generazione delle entità e il commit della transazione, quindi lasciare la generazione del numero a un singolo batch lavoro. Un aggiornamento sulla tabella dell'entità o un inserto in una tabella separata.

Quindi se abbiamo bisogno della tripletta della generazione istantanea di una serie di numeri priva di gap mediante processi multipli? Tutto ciò che possiamo fare è cercare di ridurre al minimo il periodo di serializzazione nel processo, e io offro i seguenti consigli e accolgo con favore qualsiasi consiglio aggiuntivo (o contro-consiglio ovviamente).

  1. Memorizza i tuoi valori attuali in una tabella dedicata. NON utilizzare una sequenza.
  2. Assicurati che tutti i processi utilizzino lo stesso codice per generare nuovi numeri incapsulandolo in una funzione o procedura.
  3. Serializza l'accesso al generatore di numeri con DBMS_Lock, assicurandoti che ogni serie abbia il proprio lucchetto dedicato.
  4. Mantieni il blocco nel generatore di serie fino al completamento della transazione di creazione dell'entità rilasciando il blocco al commit
  5. Ritarda la generazione del numero fino all'ultimo momento possibile.
  6. Considera l'impatto di un errore imprevisto dopo aver generato il numero e prima che il commit sia completato:l'applicazione eseguirà il rollback senza problemi e rilascerà il blocco, oppure manterrà il blocco sul generatore di serie fino a quando la sessione non si disconnetterà in seguito? Qualunque sia il metodo utilizzato, se la transazione fallisce, i numeri di serie devono essere "restituiti al pool".
  7. Puoi incapsulare il tutto in un trigger sulla tabella dell'entità? Puoi incapsularlo in una tabella o in un'altra chiamata API che inserisce la riga e esegue il commit dell'inserimento automaticamente?

Articolo originale