Scenariostoccaggio ottimale della struttura di dati per la ricerca rapida e la persistenza
Ho i seguenti metodi:
public void AddItemSecurity(int itemId, int[] userIds)
public int[] GetValidItemIds(int userId)
Inizialmente sto pensando di archiviazione sul modulo:
itemId -> userId, userId, userId
e
userId -> itemId, itemId, itemId
AddItemSecurity
si basa su come ottengo i dati da un'API di terze parti, GetValidItemIds
è come voglio usarlo in fase di esecuzione.
Ci sono potenzialmente 2000 utenti e 10 milioni di articoli. Gli ID articolo sono nel modulo: 2007123456, 20100(10 cifre in cui i primi quattro rappresentano l'anno).
AddItemSecurity
non è necessario eseguire super veloce, ma GetValidIds
deve essere subsecond. Inoltre, se c'è un aggiornamento su uno esistente itemId
ho bisogno di rimuovere quel itemId per gli utenti non più nell'elenco.
Sto cercando di pensare a come archiviare questo in modo ottimale. Preferibilmente su disco (con memorizzazione nella cache), ma voglio che il codice sia mantenibile e pulito.
Se l'ID dell'articolo era iniziato a 0, ho pensato di creare un array di byte della lunghezza di MaxItemId/8
per ciascun utente e impostare un bit vero/falso se l'elemento era presente o meno. Ciò limiterebbe la lunghezza dell'array a poco più di 1mb per utente e offrirà ricerche veloci e un modo semplice per aggiornare l'elenco per utente. Persistendo questo come Memory Mapped Files con il framework .Net 4, penso che anch'io otterrei un caching decente (se la macchina ha abbastanza RAM) senza implementare la logica di caching da solo. Analizzare l'ID, eliminare l'anno e archiviare una serie all'anno potrebbe essere una soluzione.
L'elenco ItemId -> UserId [] può essere serializzato direttamente su disco e leggere/scrivere con un normale FileStream
per mantenere l'elenco e diffarlo quando sono presenti modifiche.
Ogni volta che viene aggiunto un nuovo utente, tutte le liste devono essere aggiornate, ma può essere eseguita ogni notte.
Domanda
Devo continuare a provare questo approccio, o ci sono altre strade che dovrebbero essere esplorati come bene? Sto pensando che il server SQL non eseguirà abbastanza veloce, e darebbe un overhead (almeno se è ospitato su un server diverso), ma le mie supposizioni potrebbero essere sbagliate. Ogni pensiero o approfondimento sulla questione è apprezzato. E voglio cercare di risolverlo senza aggiungere troppo hardware :)
[Update 2010-03-31]
Ora ho provato con SQL Server 2008 nelle seguenti condizioni.
- tabella con due colonne (userid, itemid) entrambi sono Int
- indice cluster su due colonne
- Aggiunto ~ 800.000 articoli per 180 utenti - totale di 144 milioni di righe
- RAM assegnata 4GB per SQL Server
- dual core a 2,66 GHz portatile
- disco SSD
- Utilizzare uno SqlDataReader per leggere tutte le itemid di in un elenco 01.235.164,106174 millions
- Loop su tutti gli utenti
Se eseguo un thread, la media è di 0,2 secondi. Quando aggiungo un secondo thread sale a 0.4 secondi, che è ancora ok. Da lì i risultati stanno diminuendo. L'aggiunta di un terzo thread porta molte delle query fino a 2 seonds. Un quarto thread, fino a 4 secondi, un quinto spunta alcune delle query fino a 50 secondi.
La CPU è in fase di copertura mentre è in corso, anche su una filettatura. La mia app di test richiede un po 'a causa del ciclo veloce, e per il resto il resto.
Il che mi porta a concludere che non scala molto bene. Almeno non sul mio hardware testato. Ci sono modi per ottimizzare il database, ad esempio memorizzare una serie di int per utente invece di un record per articolo. Ma questo rende più difficile rimuovere gli oggetti.
[Update 2010-03-31 # 2]
Ho fatto un rapido test con gli stessi dati di metterlo come bit in file di memoria mappata. Funziona molto meglio. Sei thread producono tempi di accesso tra 0,02 e 0,06 secondi. Limitato alla memoria. I file mappati sono stati mappati da un processo e accessibili da altri sei contemporaneamente. E poiché la base sql ha preso 4 GB, i file su disco hanno richiesto 23 MB.
So che stai usando C# e non ho idea di come siano implementati i file di memoria mappati, ma potresti voler guardare questo per Java: 'http : //download.oracle.com/javase/6/docs/api/java/nio/channels/FileChannel.html#map (java.nio.channels.FileChannel.MapMode, long, long) ' – user183037
" Modifiche apportate al il buffer risultante verrà infine propagato al file e potrebbe essere reso visibile a altri programmi che hanno mappato lo stesso file. " - Se usi più thread, vorresti fare attenzione a questa parte. – user183037
Non ho avuto problemi con multi thread o multi proc con accesso allo stesso file. Se non sbaglio, due thread/proc accederanno alla stessa pagina di memoria nel sistema operativo se accedono agli stessi dati e il sistema operativo si occuperà della memorizzazione nella cache/paging/accodamento delle richieste. Detto questo, non sono esperto e nel mio scenario ho uno scrittore e più lettori, e ottenere una perdita una volta non è un grosso problema. Se devi essere sicuro al 100% sulla sequenza di eventi, allora potresti non voler usare mmf. Ma mi fiderei di questo dato che i MMF sono uno dei modi consigliati per condividere i dati tra le app. –