Il cattivo : Partizionamento
Una delle cose difficili a cui abituarsi all’inizio è che senza alcun indice le query che si estendono su righe possono (molto) essere cattive. Ripensando al nostro modello di archiviazione, tuttavia, ciò non sorprende. La strategia che Cassandra utilizza per distribuire le righe tra gli host è chiamata Partizionamento.
Il partizionamento è l’atto di ritagliare l’intervallo di rowkey assegnandoli nel “token ring”, che assegna anche la responsabilità di un segmento (cioè partizione) dell’intervallo rowkey a ciascun host. Probabilmente l’hai visto quando hai inizializzato il tuo cluster con un “token”. Il token fornisce all’host una posizione lungo il token ring, che assegna la responsabilità per una sezione dell’intervallo di token. Il partizionamento è l’atto di mappare la rowkey nell’intervallo token.
Ci sono due partizionatori primari: Random e Order Preserving. Essi sono opportunamente nominati. Il RandomPartitioner hash i rowkeys in token. Con il RandomPartitioner, il token è un hash della rowkey. Questo fa un buon lavoro di distribuire uniformemente i dati su un insieme di nodi, ma rende incredibilmente difficile interrogare un intervallo dello spazio rowkey. Da solo un valore “start rowkey” e un valore “end rowkey”, Cassandra non può determinare quale intervallo dello spazio token è necessario. Essenzialmente deve eseguire una ” scansione della tabella “per rispondere alla query e una” scansione della tabella ” in Cassandra è errata perché deve andare su ogni macchina (molto probabilmente TUTTE le macchine se si dispone di una buona funzione hash) per rispondere alla query.
The Good: Secondary Indexes
Cassandra fornisce un meccanismo di indicizzazione nativo negli indici secondari. Gli indici secondari funzionano fuori dei valori delle colonne. Si dichiara un indice secondario su una famiglia di colonne. Datastax ha una buona documentazione sull’utilizzo. Sotto il cofano, Cassandra mantiene una” famiglia di colonne nascoste ” come indice. (Vedere la presentazione di Ed Anuff per le specifiche) Poiché Cassandra non mantiene le informazioni sul valore della colonna in nessun nodo e gli indici secondari sono sul valore delle colonne (piuttosto che sulle chiavi di riga), una query deve ancora essere inviata a tutti i nodi. Inoltre, gli indici secondari non sono raccomandati per i set di alta cardinalità. Non ho ancora guardato, ma presumo che questo sia dovuto al modello di dati utilizzato all’interno della “famiglia di colonne nascoste”. Se la famiglia di colonne nascoste memorizza una riga per valore univoco (con rowkeys come colonne), significherebbe scansionare le righe per determinare se si trovano all’interno dell’intervallo nella query.
Dalla presentazione di Ed:
- Non raccomandato per valori di cardinalità elevati(ad esempio timestamp,date di nascita,parole chiave,ecc.)
- Richiede almeno un confronto di uguaglianza in una query–non di minore/maggiore/range query
- Unsorted – i risultati sono in token ordine, non al valore di query ordine
- Limited ricerca sui tipi di dati, Cassandra nativamente capisce
Con tutto ciò detto, gli indici secondari out of the box e abbiamo avuto un buon successo al loro utilizzo su valori semplici.
Il brutto: Fai da te (fai da te) / Righe larghe
Ora, la bellezza è negli occhi di chi guarda. Una delle cose belle di NoSQL è la semplicità. I costrutti sono semplici: spazi delle chiavi, famiglie di colonne, righe e colonne. Mantenerlo semplice tuttavia significa a volte è necessario prendere le cose nelle proprie mani.
Questo è il caso degli indici a righe larghe. Utilizzando il modello di archiviazione di Cassandra, è facile creare i propri indici in cui ogni chiave di riga diventa una colonna nell’indice. Questo a volte è difficile da capire, ma immaginiamo di avere un caso in cui vogliamo selezionare tutti gli utenti in un codice postale. La famiglia di colonne degli utenti principali è digitata su userid, il codice postale è una colonna su ogni riga utente. Potremmo usare indici secondari, ma ci sono alcuni codici di avviamento postale. Invece potremmo mantenere una famiglia di colonne con una singola riga chiamata “idx_zipcode”. Potremmo quindi scrivere colonne in questa riga del modulo “zipcode_userid”. Poiché le colonne sono memorizzate in ordine ordinato, è veloce interrogare tutte le colonne che iniziano con “18964” (ad esempio, potremmo usare 18964_ e 18964_ZZZZZZ come valori iniziali e finali).
Uno svantaggio ovvio di questo approccio è che le righe sono autonome su un host. (ancora una volta ad eccezione delle repliche) Ciò significa che tutte le query colpiranno un singolo nodo. Non ho ancora trovato una buona risposta per questo.