2011-11-16 13 views
8

Vorrei sapere ogni volta che è possibile in Cassandra specificare un vincolo univoco sulla chiave di riga. Qualcosa di simile a quello di SQL ServerCassandra - vincolo univoco sulla chiave di riga

In caso di inserimento con chiave di riga già esistente, i dati esistenti non verranno sovrascritti, ma ricevo tipo di eccezione o di risposta che l'aggiornamento non può essere eseguito a causa di violazione dei vincoli.

Forse c'è una soluzione per questo problema - ci sono contatori che aggiorna le cuciture di essere atomica

Grazie,

Maciej

risposta

7

Purtroppo no, perché Cassandra non esegue alcun controllo sulla scrittura . Per implementare qualcosa del genere, Cassandra dovrebbe fare una lettura prima di ogni scrittura, per verificare se è consentita la scrittura. Ciò rallenterebbe notevolmente le scritture. (Il punto è che le scritture sono trasmesse in sequenza in sequenza senza dover cercare alcun disco - le letture interrompono questo schema e la forza cerca di verificarsi.)

Non riesco a pensare a un modo in cui i contatori potrebbero aiutare. I contatori non sono implementati usando un test-and-set atomico. Al contrario, memorizzano essenzialmente molti delta, che vengono aggiunti insieme quando si legge il valore del contatore.

+0

grazie - come ho lo aspettavo. Potrebbe anche qualcun altro confermare che - Vorrei essere sicuro al 101% :) –

+0

Potresti ovviamente fare un sacco di scritture e poi controllare per vedere se il tuo vincolo è stato violato. Non so se sarebbe utile per te, comunque. –

+0

Theodore è corretto. In genere quando si desidera unicità, è necessario utilizzare un UUID o una combinazione di dettagli che garantisca univocità. –

2

Ovviamente non si può In cassandra tutte le operazioni di scrittura si riflettono in

  1. Commit accedere
  2. Memtable

per scalare milioni scrive & durata

Se consideriamo il vostro caso. Prima di fare questo cassandra necessario

  1. Verificare l'esistenza in Memtable
  2. Verificare l'esistenza in tutti i sstables [Se la chiave viene lavata dal Memtable]

Nel caso 2 tutti però, cassandra ha implementato i filtri di fioritura sarà un overhead. Ogni scrittura sta per essere una lettura & scrivere

Ma la richiesta può ridurre merge in testa in Cassandra, perché in qualsiasi momento la chiave sta per essere lì in un solo sstable. Ma l'architettura di cassandra dovrà essere cambiata per questo.

Jus controllare questo video http://blip.tv/datastax/counters-in-cassandra-5497678 o scaricare questa presentazione http://www.datastax.com/wp-content/uploads/2011/07/cassandra_sf_counters.pdf per vedere come i contatori sono entrati nell'esistenza di cassandra.

4

mi sento bene oggi e non voglio downvote tutti gli altri manifesti per dire che non è neanche lontanamente possibile creare una serratura con solo e solo un ammasso di Cassandra. Ho appena implementato l'algoritmo di panificazione di Lamport¹ e funziona perfettamente. Non c'è bisogno di altre strane cose come zoo, gabbie, tavoli di memoria, ecc.

Invece è possibile implementare un meccanismo di blocco multi-processo/multi-computer di un uomo povero purché sia ​​possibile ottenere una lettura e una scrittura con almeno la consistenza QUORUM. Questo è tutto ciò di cui hai veramente bisogno per essere in grado di implementare correttamente questo algoritmo. (il livello QUORUM può variare in base al tipo di blocco necessario: locale, rack, rete completa.)

La mia implementazione verrà visualizzata nella versione 0.4.7 di libQtCassandra (in C++). Ho già provato e si blocca perfettamente. Ci sono alcune altre cose che voglio testare e ti permettono di definire una serie di parametri che al momento sono codificati. Ma il meccanismo funziona perfettamente.

Quando ho trovato questo thread ho pensato che qualcosa non andava. Ho cercato un po 'di più e ho trovato una pagina su Apache che menziono di seguito. La pagina non è molto avanzata ma il loro MoinMoin non offre una pagina di discussione ... Comunque, penso che valga la pena menzionarlo. Speriamo che le persone inizieranno ad implementare quel meccanismo di blocco in tutti i tipi di linguaggi come PHP, Ruby, Java, ecc. In modo che venga utilizzato e noto che funzioni.

Fonte: http://wiki.apache.org/cassandra/Locking

¹ http://en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm

Il seguente è più o meno il modo in cui ho implementato la mia versione. Questa è solo una sinossi semplificata. Potrei aver bisogno di aggiornarlo un po 'di più perché ho apportato alcuni miglioramenti durante il test del codice risultante (anche il codice reale utilizza RAII e include una funzionalità di timeout in cima al TTL.) La versione finale sarà trovata nello libQtCassandra library.

// lock "object_name" 
void lock(QString object_name) 
{ 
    QString locks = context->lockTableName(); 
    QString hosts_key = context->lockHostsKey(); 
    QString host_name = context->lockHostName(); 
    int host = table[locks][hosts_key][host_name]; 
    pid_t pid = getpid(); 

    // get the next available ticket 
    table[locks]["entering::" + object_name][host + "/" + pid] = true; 
    int my_ticket(0); 
    QCassandraCells tickets(table[locks]["tickets::" + object_name]); 
    foreach(tickets as t) 
    { 
     // we assume that t.name is the column name 
     // and t.value is its value 
     if(t.value > my_ticket) 
     { 
      my_ticket = t.value; 
     } 
    } 
    ++my_ticket; // add 1, since we want the next ticket 
    table[locks]["tickets::" + object_name][my_ticket + "/" + host + "/" + pid] = 1; 
    // not entering anymore, by deleting the cell we also release the row 
    // once all the processes are done with that object_name 
    table[locks]["entering::" + object_name].dropCell(host + "/" + pid); 

    // here we wait on all the other processes still entering at this 
    // point; if entering more or less at the same time we cannot 
    // guarantee that their ticket number will be larger, it may instead 
    // be equal; however, anyone entering later will always have a larger 
    // ticket number so we won't have to wait for them they will have to wait 
    // on us instead; note that we load the list of "entering" once; 
    // then we just check whether the column still exists; it is enough 
    QCassandraCells entering(table[locks]["entering::" + object_name]); 
    foreach(entering as e) 
    { 
     while(table[locks]["entering::" + object_name].exists(e)) 
     { 
      sleep(); 
     } 
    } 

    // now check whether any other process was there before us, if 
    // so sleep a bit and try again; in our case we only need to check 
    // for the processes registered for that one lock and not all the 
    // processes (which could be 1 million on a large system!); 
    // like with the entering vector we really only need to read the 
    // list of tickets once and then check when they get deleted 
    // (unfortunately we can only do a poll on this one too...); 
    // we exit the foreach() loop once our ticket is proved to be the 
    // smallest or no more tickets needs to be checked; when ticket 
    // numbers are equal, then we use our host numbers, the smaller 
    // is picked; when host numbers are equal (two processes on the 
    // same host fighting for the lock), then we use the processes 
    // pid since these are unique on a system, again the smallest wins. 
    tickets = table[locks]["tickets::" + object_name]; 
    foreach(tickets as t) 
    { 
     // do we have a smaller ticket? 
     // note: the t.host and t.pid come from the column key 
     if(t.value > my_ticket 
     || (t.value == my_ticket && t.host > host) 
     || (t.value == my_ticket && t.host == host && t.pid >= pid)) 
     { 
      // do not wait on larger tickets, just ignore them 
      continue; 
     } 
     // not smaller, wait for the ticket to go away 
     while(table[locks]["tickets::" + object_name].exists(t.name)) 
     { 
      sleep(); 
     } 
     // that ticket was released, we may have priority now 
     // check the next ticket 
    } 
} 

// unlock "object_name" 
void unlock(QString object_name) 
{ 
    // release our ticket 
    QString locks = context->lockTableName(); 
    QString hosts_key = context->lockHostsKey(); 
    QString host_name = context->lockHostName(); 
    int host = table[locks][hosts_key][host_name]; 
    pid_t pid = getpid(); 
    table[locks]["tickets::" + object_name].dropCell(host + "/" + pid); 
} 

// sample process using the lock/unlock 
void SomeProcess(QString object_name) 
{ 
    while(true) 
    { 
     [...] 
     // non-critical section... 
     lock(object_name); 
     // The critical section code goes here... 
     unlock(object_name); 
     // non-critical section... 
     [...] 
    } 
} 
Problemi correlati