2011-11-16 13 views
8

Me gustaría saber cuándo es posible en Cassandra especificar una restricción única en la clave de fila. Algo parecido a SQL Server de ADD CONSTRAINT myConstrain UNIQUE (ROW_PK)Cassandra - restricción única en la clave de fila

En caso de inserción con clave de fila ya existente, los datos existentes no se sobrescribe, pero reciben clase de excepción o respuesta que la actualización no se puede realizar debido a restringir violación.

tal vez hay una solución para este problema - hay contadores que pone al día las costuras sean atómica

Gracias,

Maciej

Respuesta

7

Por desgracia, no, porque Cassandra no realiza ningún control de las escrituras . Para implementar algo así, Cassandra tendría que leer antes de cada escritura, para verificar si la escritura está permitida. Esto ralentizaría mucho las escrituras. (El punto es que las escrituras se transmiten de forma secuencial sin necesidad de hacer ningún disco busca - lee interrumpir este patrón y la fuerza pretende que se produzca.)

No puedo pensar en una manera que contrarresta ayudaría, tampoco. Los contadores no se implementan utilizando un atomic test-and-set. En cambio, básicamente almacenan muchos deltas, que se suman cuando lee el valor del contador.

+0

gracias - esta es la forma en que he esperaba. ¿Alguien más podría confirmarlo? Me gustaría estar 101% seguro :) –

+0

Por supuesto, podría hacer un montón de escrituras y luego verificar luego si su restricción fue violada. Aunque no sé si eso sería útil para ti. –

+0

Theodore es correcto. Normalmente, cuando desea exclusividad, debe usar un UUID o una combinación de detalles que garantice la exclusividad. –

2

Obviamente no se puede En Cassandra todas sus escrituras se reflejan en

  1. registro de confirmación
  2. Memtable

a escala millones escribe & durabilidad

Si tenemos en cuenta su caso. Antes de hacer esto Cassandra necesitar

  1. Comprobar por la existencia en Memtable
  2. Comprobar por la existencia en todos los sstables [Si la clave se añade desde Memtable]

En el caso 2 todo, sin embargo, Cassandra tiene filtros de floración implementados va a ser una sobrecarga. Cada escritura va a ser una lectura & escribir

Pero su solicitud puede reducir la sobrecarga de mezcla en Cassandra, porque en cualquier momento la clave va a estar allí en una sola sstable. Pero la arquitectura de cassandra tendrá que cambiarse por eso.

Jus mira este video http://blip.tv/datastax/counters-in-cassandra-5497678 o descarga esta presentación http://www.datastax.com/wp-content/uploads/2011/07/cassandra_sf_counters.pdf para ver cómo han llegado los contadores a la existencia de cassandra.

4

Hoy me siento bien y no voy a downvote todos los otros carteles por decir que no es ni remotamente posible crear una cerradura con sólo y solo un grupo Cassandra. Acabo de implementar el algoritmo de panadería Lamport¹ y funciona muy bien. No hay necesidad de otra cosa extraña como zoológicos, jaulas, mesas de memoria, etc.

En su lugar, puede implementar un mecanismo de bloqueo multi-proceso/multi-computadora de hombre pobre siempre que pueda obtener una lectura y escritura con al menos la consistencia QUORUM. Eso es todo lo que necesita para poder implementar correctamente este algoritmo. (el nivel QUORUM puede cambiar dependiendo del tipo de bloqueo que necesite: local, rack, red completa.)

Mi implementación aparecerá en la versión 0.4.7 de libQtCassandra (en C++). Ya probé y se bloquea perfectamente. Hay algunas cosas más que quiero probar y te permiten definir un conjunto de parámetros que en este momento están codificados. Pero el mecanismo funciona perfectamente.

Cuando encontré este hilo pensé que algo andaba mal. Busqué más y encontré una página en Apache que mencioné a continuación. La página no es muy avanzada, pero su MoinMoin no ofrece una página de discusión ... De todos modos, creo que valió la pena mencionarla. Afortunadamente, las personas comenzarán a implementar ese mecanismo de bloqueo en todo tipo de idiomas, como PHP, Ruby, Java, etc. para que se acostumbre y se sepa que funciona.

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

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

La siguiente es más o menos la forma en que he implementado mi versión. Esto es solo una sinopsis simplificada. Quizás necesite actualizarlo un poco más porque realicé algunas mejoras mientras probaba el código resultante (también el código real usa RAII e incluye una capacidad de tiempo de espera en la parte superior del TTL). La versión final se encontrará en el 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... 
     [...] 
    } 
} 
Cuestiones relacionadas