it-swarm.it

Come progettare un database per la memorizzazione di un elenco ordinato?

Sto cercando di memorizzare un elenco ordinato all'interno di un database. Voglio eseguire le seguenti operazioni in modo efficiente.

  1. Inserisci (x) - Inserisci il record x nella tabella
  2. Elimina (x) - Elimina il record x dalla tabella
  3. Prima (x, n) - Restituisce i record 'n' che precedono il record x nell'elenco ordinato.
  4. Dopo (x, n) - Restituisce i record 'n' successivi al record x nell'elenco ordinato.
  5. Primo (n) - Restituisce i primi 'n' record dall'elenco ordinato.
  6. Last (n) - Restituisce gli ultimi 'n' record dall'elenco ordinato.
  7. Confronta (x, y) - Dati due record xey dalla tabella, scopri se x> y.

Il metodo semplice che mi viene in mente è quello di memorizzare una sorta di attributo 'rank' nella tabella e interrogare ordinando quell'attributo. Ma in questo metodo inserire/modificare un record con un rango diventa un'operazione costosa. C'è un metodo migliore?

In particolare, sto cercando di implementare la tabella utilizzando Amazon SimpleDB. Ma dovrebbe essere utile anche una risposta generale per un database relazionale.

Aggiornamento sul profilo di caricamento:

Dal momento che sto pianificando questo per un'applicazione Web, dipende dal numero di utenti che utilizzano l'app.

Se ci sono 100k utenti attivi (super ottimismo: P), allora la mia stima molto approssimativa al giorno sarebbe

500k seleziona, 100k inserisce ed elimina, aggiornamenti 500k

Mi aspetto che il tavolo cresca fino a 500k in totale.

Sto cercando di ottimizzare gli aggiornamenti, inserire e confrontare le operazioni. Il rango degli articoli cambierà costantemente e devo tenere aggiornato il tavolo.

44
chitti

Se il grado non è completamente arbitrario ma è invece derivabile da qualche altra proprietà (ad es. Nome, punteggio del giocatore, ecc.), Dai un'occhiata a risposta di Joel .

Se è una proprietà arbitraria dei tuoi dati, dovrebbe essere archiviata come colonna nella tabella dei record. Supponendo che il SimpleDB di Amazon sia simile al tipico RDBMS, è quindi possibile indicizzare questa colonna e soddisfare rapidamente tutte le query precedenti con la strategia di indicizzazione appropriata. Questo è normale per un RDBMS.

Dato che ti aspetti un'attività di inserimento e aggiornamento elevata, ma anche un'attività di lettura relativamente alta, ti consiglio di fare quanto segue:

  • Raggruppa la classifica in classifica, specialmente se la stragrande maggioranza delle tue domande sono in contrasto con la classifica. In caso contrario, o se la scelta di una chiave di clustering non è disponibile in SimpleDB, creare semplicemente un indice con rango come colonna principale. Ciò soddisferebbe le domande 3-6.
  • Un indice sul record prima e poi in classifica (o, nel mondo di SQL Server, registra e INCLUDE- in classifica, o semplicemente registra se sei stato raggruppato in classifica) soddisfarrebbe la query 7.
  • Le operazioni 1 e 2 possono essere ottimizzate distanziando i dati in modo appropriato (ovvero impostando FILLFACTOR in SQL Server). Ciò è particolarmente importante se si raggruppa in classifica.
  • Man mano che si inseriscono o si aggiornano le classifiche, mantenere il più possibile uno spazio tra i numeri delle classifiche per ridurre al minimo la possibilità che sarà necessario riordinare un record esistente per adattarsi a un inserimento o aggiornamento delle classifiche. Ad esempio, se classifichi i tuoi record con incrementi di 1000, lasci abbastanza spazio per circa la metà di molte modifiche e inserimenti con la minima possibilità che dovrai riordinare un record non direttamente coinvolto in tali modifiche.
  • Ogni notte ri-classifica tutti i record per ripristinare i vuoti di rango tra di loro.
  • È possibile ottimizzare la frequenza dei ri-classifiche di massa e le dimensioni del gap in base al numero previsto di inserimenti o aggiornamenti relativi al numero di record esistenti. Quindi, se hai record da 100K e ti aspetti che inserimenti e aggiornamenti siano il 10% di quello, lascia abbastanza spazio per 10K nuovi ranghi e ri-classifica di notte.
  • Riclassificare i record da 500.000 è un'operazione costosa, ma eseguita una volta al giorno o una settimana fuori orario dovrebbe andare bene per un database del genere. Questa riclassificazione di massa fuori orario per mantenere le lacune di rango è ciò che ti fa risparmiare dover riordinare molti record per ogni aggiornamento o inserimento di rango durante le ore normali e di punta.

Se prevedete letture di oltre 100 KB su una tabella con dimensioni di oltre 100 KB, non consiglio di utilizzare l'approccio elenco collegato. Non si adatta bene a quelle dimensioni.

22
Nick Chammas

In genere utilizzo il metodo "rango" che descrivi. Invece di scherzare con l'aggiornamento delle righe quando è necessario riordinare gli articoli, sono stato spesso in grado di cavarmela eliminando tutti i record nell'elenco e reinserendo nuovi elementi nell'ordine corretto. Questo metodo è chiaramente ottimizzato per il recupero.

Un approccio alternativo sarebbe quello di modellare i record come un elenco collegato utilizzando una colonna di chiave esterna riflessa "precedente" sulla tabella:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Puoi facilmente recuperare un elenco e aggiungere e rimuovere elementi con un piccolo sovraccarico, ma sarà difficile ottenere i record nell'ordine corretto. Forse c'è un modo intelligente per farlo in una singola query, probabilmente con molti join di tabelle con alias.

Uso quest'ultimo approccio spesso quando modello una relazione stile albero (categorie, cartelle, set e sottoinsiemi). In genere ho avuto una funzione ricorsiva di qualche tipo per ricostruire l'intero albero nella mia applicazione.

13
bpanulla

Penso che la cosa da fare sia memorizzare la proprietà o le proprietà utilizzate per calcolare il grado e quindi costruire un indice su di essi. Invece di provare a forzare il database a archiviare fisicamente i dati in ordine classificato o utilizzando un elenco collegato gestito manualmente, perché non lasciare che il motore di database faccia ciò per cui è stato progettato?

6
Joel Brown

Queste sono le limitazioni di un non RDBMS come simpleDB. Le funzioni richieste non possono essere implementate sul lato DB in simpleDB, devono essere implementate dal lato/applicazione di programmazione.

Per un RDBMS come SQL server, le funzionalità richieste sono rudimentali all'indice cluster.

  • Inserisci (x) - Inserisci il record x nella tabella> Inserisci semplice.
  • Elimina (x): elimina il record x dalla tabella> Elimina semplice.
  • Prima (x, n) - Restituisce i record 'n' che precedono il record x nell'elenco ordinato. > Seleziona i primi n risultati in cui x è inferiore al valore e ordina per clausola.

  • Dopo (x, n) - Restituisce i record 'n' successivi al record x nell'elenco ordinato. > Seleziona i primi n risultati dove x maggiore del valore e ordina per clausola.

  • Primo (n) - Restituisce i primi 'n' record dall'elenco ordinato. > Seleziona i primi n risultati.

  • Last (n) - Restituisce gli ultimi 'n' record dall'elenco ordinato. > Seleziona i primi n risultati dopo l'ordine per descrizione.

  • Confronta (x, y) - Dati due record xey dalla tabella, scopri se x> y. > Dichiarazione TSQL IF.
1
StanleyJohns

Ecco cosa ho usato per riordinare la mia tabella Postgres dopo ogni inserimento:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Per il mio caso d'uso, le prestazioni non sono un problema, ma la fiducia che non si romperà o agirà in modo strano è importante.

0
Mark