我想知道Cassandra何时可以在行键上指定唯一约束.类似于SQL Server的东西ADD CONSTRAINT myConstrain UNIQUE (ROW_PK)
如果插入已存在的行键,则不会覆盖现有数据,但是我收到一些异常或响应,由于约束违规而无法执行更新.
也许这个问题有一个解决方法 - 有一些计数器可以将接缝更新为原子
谢谢,
马切伊
Van*_*yen 14
轻量级交易?
http://www.datastax.com/documentation/cassandra/2.0/cassandra/dml/dml_ltwt_transaction_c.html
INSERT INTO customer_account (customerID, customer_email)
VALUES (‘LauraS’, ‘lauras@gmail.com’)
IF NOT EXISTS;
不幸的是,不,因为Cassandra不会对写入执行任何检查.为了实现类似的功能,Cassandra必须在每次写入之前进行读取,以检查是否允许写入.这会大大减慢写入速度.(重点是写入顺序流出而不需要进行任何磁盘搜索 - 读取中断此模式并强制寻求发生.)
我也想不出计数器会有所帮助的方式.计数器不是使用原子测试和设置实现的.相反,它们基本上存储了许多增量,当您读取计数器值时,这些增量会相加.
Cassandra - 可以借助主键约束来实现唯一约束。您需要将所有想要唯一的列作为主键。Cassandra 将自行解决其余问题。
CREATE TABLE users (firstname text, lastname text, age int,
email text, city text, PRIMARY KEY (firstname, lastname));
Run Code Online (Sandbox Code Playgroud)
这意味着当和相同时,Cassandra 不会在此users
表中插入两个不同的行。firstname
lastname
我今天感觉很好,我不会对所有其他张贴者表示不满,因为即使使用Cassandra集群也无法远程创建锁。我刚刚实现了Lamport的面包店算法¹,它工作得很好。不需要任何其他奇怪的东西,例如动物园,笼子,存储表等。
相反,您可以实现穷人的多进程/多计算机锁定机制,只要您可以获得至少QUORUM一致性的读写即可。这是您能够正确实现此算法的真正所需。(QUARUM级别可以根据所需的锁类型而变化:本地,机架,整个网络。)
我的实现将出现在libQtCassandra的0.4.7版中(在C ++中)。我已经测试过,并且可以完美锁定。我还要测试几件事,并让您定义一组现在已进行硬编码的参数。但是该机制运行良好。
当我找到该线程时,我认为出了点问题。我搜索了更多内容,并在Apache上找到了我下面提到的页面。该页面不是很高级,但是他们的MoinMoin没有提供讨论页面...无论如何,我认为这值得一提。希望人们将开始以各种语言(例如PHP,Ruby,Java等)实现该锁定机制,以便它得到使用并知道它可以工作。
来源:http://wiki.apache.org/cassandra/Locking
¹http: //en.wikipedia.org/wiki/Lamport%27s_bakery_algorithm
以下是我实现我的版本的方式。这只是一个简化的提要。我可能需要进行更多更新,因为我在测试结果代码时做了一些改进(实际代码也使用RAII,并且在TTL之上包括超时功能。)最终版本将在libQtCassandra库中找到。
// 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...
[...]
}
}
Run Code Online (Sandbox Code Playgroud)
重要说明(2019/05/05):尽管使用Cassandra实现Lamport's Bakery是一项很棒的练习,但它是Cassandra数据库的反模式。这意味着在重负载下性能可能很差。从那时起,我创建了一个新的锁系统,仍然使用Lamport算法,但是将所有数据保留在内存中(非常小),并且仍然允许多台计算机参与锁,因此如果其中一台出现故障,锁系统将继续工作符合预期(许多其他锁定系统不具备该功能。当主服务器出现故障时,您将失去锁定能力,直到另一台计算机决定自己成为新的主计算机为止。)
归档时间: |
|
查看次数: |
17552 次 |
最近记录: |