Devo eseguire ricerche rapide su una combinazione di tag pur includendo intervalli di date:
Esempio:
- Utenti
- che hanno richiesto le notifiche
- che non ha risposto a una notifica inviata almeno 3 giorni fa
- e a chi non sono state inviate altre notifiche negli ultimi 3 giorni
Struttura dati
La struttura dei dati dell'evento è piuttosto semplice:
- Evento
- EntityID
- EventType
- EventName
- Data
Preoccupazioni relative alle prestazioni della struttura del database normalizzate
- Con miliardi di eventi, la scansione di una tabella non funzionerà
- L'unica colonna che indicizza bene è Data e non sarà sempre inclusa in ogni filtro
- EventName non verrà distribuito correttamente per un indice (alcuni nomi di eventi potrebbero includere 1/4 dei record)
- Fare una semplice query WHERE su questa tabella normalizzata richiederebbe molto probabilmente una scansione completa della tabella che non sarebbe abbastanza veloce
LIKE o ricerca full-text
Un altro approccio consiste nel convertire questi tag in una singola colonna di testo, uno per entità.
- EntryType_EntryName_Date_Time1, EntryType_EntryName_Date_Time2, EntryType_EntryName_Date_Time3
Quindi, posso eseguire una ricerca full-text SQL.
Questo ridurrebbe il numero di righe di almeno 10 volte, ma non riesco a capire come cercare per intervallo di date:
- Utente
- CONTAINS (RequestedNotifications *)
- NOT CONTAINS (OpenNotification_ID4 *) (Prima di 3 giorni fa ???)
- NOT CONTAINS (SentNotification *) (Da 3 giorni fa ???)
Al massimo, potrei ridurre la tabella e scansionare una partizione più piccola, ma non credo che possa essere di grande aiuto.
Soluzione in memoria
Ho pensato di creare una macchina virtuale dedicata con una struttura dati in memoria dell'intero set di dati complessi.
Fondamentalmente, vorrei creare un dizionario per ogni tipo di tag con intervalli per intervalli di data e tenere tutto in hashtables per le intersezioni rapide:
// Some structures like these
Dictionary<EventType, Dictionary<EventName, HashSet<int>>> nameIndex;
Dictionary<EventType, Dictionary<EventName, Dictionary<Day, HashSet<int>>>> dayIndex;
Dictionary<EventType, Dictionary<EventName, Dictionary<Day, Dictionary<Hour, HashSet<int>>>>> hourIndex;
// To search like this (kind of)
var entityIds = filters.Select(f => hourIndex[f.tagType][f.tagName][f.day][f.hour])
.IntersectMultiple()
.ToList();
// Note: In order to perform an operation like before 3 days
var filterResults = nameIndex[eventType][eventName].except(dayIndex[...][...][today].union(...[today-1]).union(...[today-2]));
Su un server dedicato, potrebbe gestire circa un miliardo di eventi in memoria.
- Ogni evento richiederebbe ~ 12 byte (~ 4 byte per ciascun indice mantenuto)
- 1 miliardo di eventi ~ 12 GB di memoria ~ $ 250 / mese A5 VM
Questo potrebbe essere ridimensionato partionando le entità e la persistenza non sarebbe troppo difficile.
Ma prima di intraprendere questo percorso molto personalizzato, vorrei sapere se esiste un modo più semplice per farlo.
Domanda
- Come posso strutturare un indice in modo che le ricerche rapide possano essere eseguite su più filtri di testo e intervallo di date, come nel mio esempio?
- Quale soluzione di Azure sarebbe il modo migliore per risolvere questo problema?