Riduzione al minimo delle letture indicizzate con criteri complessi


12

Sto ottimizzando un database Firebird 2.5 di ticket di lavoro. Sono memorizzati in una tabella dichiarata come tale:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS str256 DEFAULT 'Pending'
);

In genere desidero trovare il primo ticket che non è stato elaborato ed è in Pendingstato.

Il mio ciclo di elaborazione sarebbe:

  1. Recupera il primo biglietto dove Pending
  2. Lavora con Ticket.
  3. Aggiorna stato ticket => Complete
  4. Ripetere.

Niente di troppo elegante. Se sto guardando il database mentre questo ciclo è in esecuzione, vedo il numero di salite indicizzate delle letture per ogni iterazione. Le prestazioni non sembrano peggiorare terribilmente, ma la macchina su cui sto testando è piuttosto veloce. Tuttavia, ho ricevuto segnalazioni di degrado delle prestazioni nel tempo da alcuni dei miei utenti.

Ho un indice attivo Status, ma sembra comunque che scansiona la Ticket_Idcolonna ogni iterazione. Sembra che stia trascurando qualcosa, ma non sono sicuro di cosa. Il numero crescente di letture indicizzate per qualcosa di simile è previsto o l'indice si sta comportando in modo inappropriato?

- Modifiche per commenti -

In Firebird limiti il ​​recupero delle righe come:

Select First 1
  Job_ID, Ticket_Id
From
  Tickets
Where
  Status = 'Pending'

Quindi quando dico "prima", sto solo chiedendo un set di dischi limitato dove Status = 'Pending'.


Che cosa intendi con "primo" in "Recupera 1 ° biglietto dove" In attesa " ?
ypercubeᵀᴹ

Se "primo" significa più piccolo ticket_id, hai bisogno di un indice su(status, ticket_id)
ypercubeᵀᴹ

E quanto sei sicuro che il degrado delle prestazioni sia causato da questa procedura e non da altre domande / dichiarazioni?
ypercubeᵀᴹ

@ypercube - No, non sono sicuro che sia lì che si trova il degrado delle prestazioni. Ecco perché la mia domanda era "devo occuparmene o è un comportamento normale di un indice?". È qualcosa che ho notato durante il monitoraggio del database e l'ho considerato inaspettato. Non mi aspetto che continui a scansionare le righe precedenti quando fornisco una clausola where contro una colonna indicizzata. FWIW, la modifica dell'indice da includere ha ticket_ideffettivamente comportato risultati peggiori rispetto alla semplice indicizzazione dello stato.
gddc,

È id(il tipo di dati) di un dominio definito?
a_horse_with_no_name il

Risposte:


1

Il degrado nel tempo si verifica a causa dell'aumento del numero di elementi che si trovano nello stato "Completo". Pensaci per un secondo: non otterrai alcun peggioramento delle prestazioni durante il test poiché probabilmente hai un piccolo numero di righe con lo stato "Completo". Ma in produzione, possono avere milioni di righe con lo stato "Completo" e questo numero aumenterà nel tempo. Questo, in sostanza, rende il tuo indice sullo stato sempre meno utile nel tempo. Pertanto, il database probabilmente decide che, poiché Status ha quasi sempre il valore "Completo", eseguirà la scansione della tabella anziché utilizzare l'indice.

In SQL Server (e forse altri RDBMS?), Questo può essere aggirato usando gli indici filtrati. In SQL Server aggiungere una condizione WHERE alla fine della definizione dell'indice per dire "applica questo indice solo ai record con stato <> 'Completo'". Quindi, qualsiasi query che utilizza questo predicato utilizzerà molto probabilmente l'indice sulla piccola quantità di record non impostata su "Completa". Tuttavia, in base alla documentazione qui: http://www.firebirdsql.org/refdocs/langrefupd25-ddl-index.html , non sembra che Firebird supporti gli indici filtrati.

Una soluzione alternativa consiste nel mettere record "completi" in una tabella ArchiveTickets. Crea una tabella con la stessa identica definizione (anche se senza ID generato automaticamente) della tabella Ticket e gestisci le righe tra di loro spingendo i record "Completi" nella tabella ArchiveTickets. L'indice sulla tua tabella dei Biglietti sarà quindi su un numero molto più piccolo di record e avrà prestazioni molto più elevate. Ciò significa probabilmente che dovrai modificare eventuali rapporti, ecc. Che fanno riferimento a ticket "Completi" per puntare alla tabella Archive o eseguire un UNION su sia Ticket sia ArchiveTicket. Ciò avrà il vantaggio non solo di essere veloce, ma anche di creare indici specifici per la tabella ArchiveTickets per migliorarne le prestazioni per altre query (ad esempio:

Dovresti preoccuparti di questo se la tua produzione andrà in migliaia di file. Le prestazioni peggioreranno nel tempo e avranno un impatto negativo sulla tua esperienza utente.


0

L'eventuale influenza delle prestazioni dipenderà dal volume di dati e dalla capacità della macchina. Data la capacità dell'hardware moderno, è difficile immaginare il volume delle vendite dei biglietti che non potrebbe essere gestito dal design che descrivi. Tuttavia, ci sono cambiamenti che consiglierei per correttezza e potrebbero migliorare le prestazioni come vantaggio secondario.

La tua prima query in sospeso non è deterministica. Prima secondo quale ordine? Una tabella SQL non ha un ordine intrinseco; l' First 1hacking ti sta dando solo un primo arbitrario. Per renderlo deterministico, perché non elaborare i lavori in sospeso nell'ordine Job_ID?

Se hai due indici {Job_ID} e {Status, Job_ID}, questa query restituirà una riga in modo prevedibile ed efficiente:

Select Job_ID, Ticket_Id
From   Tickets
Where Job_ID = ( 
  select min(Job_ID) from Tickets 
  where Status = 'Pending'
);

Non sono un utente Firebird, quindi dovrete controllare il piano di query, ma dovrebbe essere efficiente perché la sottoquery fa riferimento solo al secondo indice, produce un valore per il primo. (Potrebbero esserci altri trucchi di efficienza disponibili. Potresti essere in grado di organizzare la tabella fisica come un albero B +, o avere accesso a un row_id nascosto, per esempio.)

L'altra modifica che farei per correttezza è quella di creare Statusun singolo byte vincolato e lasciare che l'applicazione fornisca la stringa "In sospeso". Ciò proteggerà da Statusvalori errati e probabilmente ridurrà l'indice nell'affare. Qualcosa di simile a:

CREATE TABLE TICKETS (
  TICKET_ID id PRIMARY KEY,
  JOB_ID id,
  ACTION_ID id,
  STATUS char(1) not NULL 
     DEFAULT 'P'
     CHECK( STATUS in ('P', 'C', 'X') ) -- whatever the domain is
);

Ovviamente, puoi usare una vista (o forse una colonna derivata) per fornire le stringhe canoniche per Status.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.