2012-01-24 5 views
10

Ho una domanda relativa alla progettazione dello schema HBase. Il problema è abbastanza semplice: sto memorizzando "notifiche" in hbase, ognuna delle quali ha uno stato ("nuovo", "visto" e "letto"). Qui ci sono le API ho bisogno di fornire:Progettazione dello schema HBase per supportare al meglio query specifiche

  • Ottenere tutte le notifiche di un utente
  • Ricevi tutte le "nuove" le notifiche per un utente
  • ottenere il conteggio di tutti i "nuovi" notifiche per un utente
  • Aggiorna stato per una notifica
  • Aggiorna stato per tutte le notifiche di un utente
  • ottenere tutti i "nuovi" notifiche attraversato la banca dati
  • notifiche sho potrebbe essere scansionabile in ordine cronologico inverso e consentire l'impaginazione.

Ho un paio di idee, e volevo vedere se uno di loro è chiaramente migliore, o se ho perso una buona strategia del tutto. Comune a tutti e tre, penso che avere una riga per ogni notifica e avere l'id utente nella riga di comando sia la strada da percorrere. Per ottenere un ordinamento cronologico per l'impaginazione, devo anche avere un timestamp inverso. Mi piacerebbe mantenere tutte le note in una tabella (quindi non devo unire l'ordinamento per la chiamata "get all notificatiosn per un utente") e non voglio scrivere processi batch per le tabelle indice secondarie (poiché gli aggiornamenti a il conteggio e lo stato dovrebbero essere in tempo reale).

Il modo più semplice per farlo sarebbe (1) la chiave di riga è "userId_reverseTimestamp" e filtrare lo stato sul lato client. Ciò sembra ingenuo, poiché invieremo molti dati non necessari attraverso la rete.

La prossima possibilità è di (2) codificare lo stato anche nel tasto di riga, quindi "userId_reverseTimestamp_status" e quindi eseguire il filtro regex di rowkey sulle scansioni. Il primo problema che vedo è la necessità di cancellare una riga e copiare i dati di notifica in una nuova riga quando lo stato cambia (che presumibilmente dovrebbe accadere esattamente due volte per ogni notifica). Inoltre, poiché lo stato è l'ultima parte della riga di comando, per ciascun utente, analizzeremo molte altre righe. È un grande successo per le prestazioni? Infine, per cambiare lo stato, dovrò sapere qual è stato lo stato precedente (per costruire la chiave di riga) altrimenti avrò bisogno di fare un'altra scansione.

L'ultima idea che ho avuto è (3) avere due famiglie di colonne, una per i dati statici notif e una come flag per lo stato, cioè "s: read" o "s: new" con 's 'come il cf e lo stato come il qualificatore. Ci sarebbe esattamente uno per riga, e posso fare un MultipleColumnPrefixFilter o SkipFilter w/ColumnPrefixFilter contro quel cf. Anche in questo caso, dovrei eliminare e creare colonne sul cambio di stato, ma dovrebbe essere molto più leggero rispetto alla copia di intere righe. La mia unica preoccupazione è l'avviso nel libro HBase che HBase non funziona bene con "più di 2 o 3 famiglie di colonne" - forse se il sistema deve essere esteso con più capacità di interrogazione, la strategia multi-cf non verrà ridimensionata .

Quindi (1) sembra che ci sarebbe troppo sovraccarico di rete. (2) sembra che avrebbe sprecato i costi spesi per copiare i dati e (3) potrebbe causare problemi con troppe famiglie. Tra (2) e (3), quale tipo di filtro dovrebbe fornire prestazioni migliori? In entrambi i casi, la scansione esaminerà ciascuna riga per un utente, che presumibilmente ha in gran parte le notifiche di lettura, il che avrebbe prestazioni migliori. Penso di essere inclinato verso (3) - ci sono altre opzioni (o ritocchi) che mi sono perso?

+0

Gli stati di notifica "nuovi" e "letti" solo con una singola transizione possibile da nuovo a letto? Qual è il volume di queste notifiche? – Gevorg

risposta

2

Avete riflettuto molto su questo e penso che tutti e tre siano ragionevoli!

Si desidera che la chiave principale sia il nome utente concatenato con il timestamp poiché la maggior parte delle query è "utente". Questo aiuterà a facilitare l'impaginazione con una scansione e può recuperare le informazioni dell'utente abbastanza rapidamente.

Penso che il punto cruciale del problema sia questa parte dello stato di modifica. In generale, qualcosa come "read" -> "delete" -> "rewrite" introduce tutti i tipi di problemi di concorrenza. Cosa succede se l'attività non riesce tra? Hai dati in uno stato non valido? Eliminerai un disco?

Suggerisco invece di considerare la tabella come "aggiungi solo". Fondamentalmente, fai ciò che suggerisci per il # 3, ma invece di rimuovere la bandiera, mantienila lì. Se qualcosa è stato letto, può avere i tre "s: seen", "s: read" lì (se è nuovo, possiamo solo supporre che sia vuoto). Potresti anche essere fantasioso e inserire un timestamp in ognuna delle tre per mostrare quando quell'evento è stato soddisfatto. Non dovresti vedere gran parte delle prestazioni colpite da questo e quindi non devi preoccuparti della concorrenza, dal momento che tutte le operazioni sono di sola scrittura e atomiche.

Spero che questo sia utile. Non sono sicuro di aver risposto a tutto dato che la tua domanda era così ampia. Ti preghiamo di rispondere con ulteriori domande e mi piacerebbe elaborare o discutere qualcos'altro.

+0

Gppd punta a renderlo di sola scrittura. Non dover hackerare aggiornamenti atomici lo rende molto meno complesso - il mio filtro sarà "finché non ci saranno stati non letti". Un'altra opzione suggerita da qualcuno è di avere più colonne per riga, dove una riga è tutte le notifiche per un utente. Presumibilmente, le colonne sono ordinate in modo simile alle righe. La mia domanda è, questo ci dà qualcosa? Hanno anche suggerito di fare un ValueFilter sul notif (quindi avere lo stato in diretta nei dati stessi, che deve essere aggiornato, piuttosto che un CF separato). La mia ipotesi è che questo avrebbe prestazioni peggiori. Pensieri? – dyross

1

La mia soluzione è:

Non salvare lo stato notifiche (visto, nuovo) in HBase per ogni notifica. Per le notifiche utilizzare lo schema semplice. Chiave: userid_timestamp - column: notification_message.

Una volta che il client chiede all'API "Ricevi tutte le nuove notifiche", salva il timestamp (tutte le nuove notifiche sono state inserite). Legenda: userid - colimn: All_new_notifications_pushed_time

Ogni notifica con data e ora è inferiore a "Tutte le nuove notifiche spinto" assunto "visti", e se più grande assumere "Nuovo"

per ottenere tutte le nuove notifiche: primo luogo ottenere valore (timestamp) per All_new_notifications_pushed_time da userid quindi eseguire intervallo di scansione sulla colonna notification_message per chiave: da current_timestamp a All_new_notifications_pushed_time.

Ciò limiterà in modo significativo le colonne interessate e la maggior parte di esse dovrebbe trovarsi nel memstore.

Contare le nuove notifiche sul client.

Problemi correlati