Cassandra - уникальное ограничение на строку

Я хотел бы знать, когда это возможно в Кассандре, чтобы указать уникальное ограничение на ключ строки. Нечто похожее на SQL Server ADD CONSTRAINT myConstrain UNIQUE (ROW_PK)

В случае вставки с уже существующим ключом строки, существующие данные не будут перезаписаны, но я получаю вид исключения или ответ, что обновление не может быть выполнено из-за нарушения ограничения.

Может быть, есть обходной путь для этой проблемы - есть счетчики, которые обновляют швы, чтобы они были атомарными.

Ответ 1

Кассандра - уникальное ограничение может быть реализовано с помощью ограничения первичного ключа. Вы должны поместить все столбцы в качестве первичного ключа, те, которые вы хотите быть уникальными. Кассандра будет заниматься остальным самостоятельно.

CREATE TABLE users (firstname text, lastname text, age int, 
email text, city text, PRIMARY KEY (firstname, lastname));

Это означает, что Cassandra не будет вставлять две различные строки в этом users таблицы, когда firstname и lastname совпадают.

Ответ 3

К сожалению, нет, потому что Cassandra не выполняет никаких проверок при записи. Чтобы реализовать что-то подобное, Кассандре нужно было бы прочитать перед каждой записью, чтобы проверить, разрешена ли запись. Это значительно замедлит запись. (Все дело в том, что записи транслируются последовательно, без необходимости делать какие-либо образы дисков - читает прерывание этого шаблона и принудительное выполнение.)

Я не могу думать о том, как помогут счетчики. Счетчики не выполняются с использованием атомного теста и набора. Вместо этого они по существу хранят множество дельт, которые складываются вместе, когда вы читаете значение счетчика.

Ответ 4

Сегодня я чувствую себя хорошо, и я не буду опровергать все другие постеры за то, что они говорят, что даже дистанционно невозможно создать замок только с одним кластером Кассандры. Я только что реализовал алгоритм пекарни Lamportort, и он работает просто отлично. Не нужно никаких других странных вещей, таких как зоопарки, клетки, таблицы памяти и т.д.

Вместо этого вы можете реализовать механизм блокировки многопроцессорных/многопользовательских компьютеров для бедного человека, если вы можете получить чтение и запись с минимальной согласованностью QUORUM. Это все, что вам действительно нужно, чтобы правильно реализовать этот алгоритм. (Уровень QUORUM может меняться в зависимости от типа блокировки, которая вам нужна: локальная, стойка, полная сеть.)

Моя реализация появится в версии 0.4.7 libQtCassandra (в C++). Я уже проверил и он отлично блокирует. Есть еще несколько вещей, которые я хочу протестировать, и позволить вам определить набор параметров, которые сейчас жестко запрограммированы. Но механизм работает отлично.

Когда я нашел эту ветку, я подумал, что что-то не так. Я искал еще немного и нашел страницу на Apache, о которой я упоминаю ниже. Страница не очень продвинутая, но их MoinMoin не предлагает страницу для обсуждения... Во всяком случае, я думаю, что это стоило упомянуть. Надеемся, что люди начнут реализовывать этот механизм блокировки во всех видах языков, таких как PHP, Ruby, Java и т.д., Чтобы он привык и знал, что он работает.

Источник: http://wiki.apache.org/cassandra/Locking

En 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...
        [...]
    }
}

ВАЖНОЕ ЗАМЕЧАНИЕ (2019/05/05): Хотя было реализовано отличное упражнение для реализации пекарни Lamport с использованием Cassandra, это анти-шаблон для базы данных Cassandra. Это означает, что он может плохо работать при большой нагрузке. С тех пор я создал новую систему блокировки, все еще используя алгоритм Лампорта, но сохраняя все данные в памяти (она очень мала) и по-прежнему позволяя нескольким компьютерам участвовать в блокировке, поэтому, если один из них выйдет из строя, система блокировки продолжит работать как и ожидалось (многие другие системы блокировки не имеют такой возможности. Когда мастер отключается, вы теряете возможность блокировки, пока другой компьютер не решит сам стать новым мастером...)

Ответ 5

Очевидно, вы не можете В кассандре все ваши записи отражены в

  • Журнал фиксации
  • Memtable

для масштабирования миллионов записей и долговечности

Если мы рассмотрим ваш случай. Прежде чем делать это, вам необходимо

  • Проверить наличие в Memtable
  • Проверить наличие во всех sstables [Если ваш ключ сброшен с Memtable]

В случае 2 все, хотя, cassandra реализовала фильтры цветения, это будет накладные расходы. Каждая запись будет читать и писать

Но ваш запрос может уменьшить накладные расходы на слияние в cassandra, потому что в любой момент ключ будет находиться только в одном sstable. Но для этого потребуется изменить архитектуру cassandra.

Jus проверить это видео http://blip.tv/datastax/counters-in-cassandra-5497678 или загрузить эту презентацию http://www.datastax.com/wp-content/uploads/2011/07/cassandra_sf_counters.pdf, чтобы увидеть, как счетчики пришли к существованию кассандры.