Управление версиями данных в Cassandra с помощью CQL3

Я довольно n00b в Cassandra (в основном я работаю с РСУБД с некоторыми NoSQL здесь и там, такими как Google BigTable и MongoDB), и я борюсь с моделированием данных для вариантов использования, которые я пытаюсь удовлетворить. Я просмотрел это и это и даже это, но это не совсем то, что мне нужно.

У меня есть эта основная таблица:

CREATE TABLE documents (
    itemid_version text,       
    xml_payload text,
    insert_time timestamp,
    PRIMARY KEY (itemid_version)
); 

itemid на самом деле является UUID (и уникальным для всех документов), а version — это int (версия 0 — это «первая» версия). xml_payload — это полный XML-документ, который может стать довольно большим. Да, по сути, я создаю хранилище документов с версиями.

Как видите, я объединил их для создания первичного ключа, и я объясню, почему я это сделал, позже, когда объясню требования и/или варианты использования:

  1. пользователю нужно получить один (1) документ, который он хочет, он знает идентификатор элемента и версию (не обязательно самую последнюю)
  2. пользователю нужно получить один (1) документ, который он хочет, он знает идентификатор элемента, но не знает последнюю версию
  3. пользователю нужна история версий одного (1) документа.
  4. пользователю нужно получить список (1 или более) документов, которые он хочет, он знает идентификатор элемента И версию (не обязательно самую последнюю)

Я буду писать клиентский код, который будет выполнять варианты использования, извините за синтаксис, поскольку я пытаюсь быть независимым от языка.

первый простой:

$itemid_version = concat($itemid, $version)
$doc = csql("select * from documents where itemid_version = {0};" 
    -f $itemid_version)

теперь, чтобы удовлетворить 2-й и 3-й варианты использования, я добавляю следующую таблицу:

CREATE TABLE document_versions (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
) WITH clustering order by (version DESC);

новые записи будут добавляться по мере создания новых документов и новых версий существующих документов

теперь у нас есть это (вариант использования № 2):

$latest_itemid, $latest_version = csql("select itemid, 
    version from document_versions where item_id = {0} 
    order by version DESC limit 1;" -f $itemid)
$itemid_version = concat($latest_itemid, $latest_version)
$doc = csql("select * from documents where itemid_version = {0};" 
    -f $itemid_version)

и это (вариант использования № 3):

$versions = csql("select version from document_versions where item_id = {0}" 
    -f $itemid)

для третьего требования я добавляю еще одну таблицу:

CREATE TABLE latest_documents (
    itemid uuid,
    version int,
    PRIMARY KEY (itemid, version)
)

записи вставляются для новых документов, записи обновляются для существующих документов

и теперь у нас есть это:

$latest_itemids, $latest_versions = csql("select itemid, version 
    from latest_documents where item_id in ({0})" -f $itemid_list.toCSV())

foreach ($one_itemid in $latest_itemids, $one_version in $latest_versions)
    $itemid_version = concat($latest_itemid, $latest_version)
    $latest_docs.append(
        cql("select * from documents where itemid_version = {0};" 
        -f $itemid_version))        

Теперь, надеюсь, понятно, почему я объединил itemid и version для создания индекса для documents, а не для создания составного ключа: у меня не может быть OR в предложении WHERE в SELECT

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

Я на правильном пути здесь? есть довольно много вещей, которые меня не устраивают... но в основном потому, что я еще не понимаю Кассандру:

  • Я чувствую, что первичный ключ для documents должен быть составным из (itemid, version), но я не могу удовлетворить вариант использования № 4 (возврат списка из запроса)... Я не могу использовать отдельный оператор SELECT для каждый документ из-за падения производительности (накладные расходы сети)... или можно (должно)?
  • 2 поездки за документом, если версия неизвестна заранее. возможно, это компромисс, с которым мне приходится жить, или, может быть, есть лучший способ.

person Dexter Legaspi    schedule 21.05.2014    source источник


Ответы (2)


Как это сработает, Декстер?

На самом деле это очень похоже на ваше решение, за исключением того, что вы можете хранить все версии и получать «последнюю» версию только из одной таблицы (document_versions).

Я думаю, что в большинстве случаев вы можете получить то, что хотите, в одном SELECT, за исключением варианта использования № 2, где сначала извлекается самая последняя версия документа, где сначала требуется предварительный SELECT для document_versions.

ВТОРАЯ ПОПЫТКА

(Код удалил с первой попытки, извиняюсь всем, кто следил в комментариях).

CREATE TABLE documents (
        itemid_version text,
        xml_payload text,
        insert_time timestamp,
        PRIMARY KEY (itemid_version)
);

CREATE TABLE document_versions (
        itemid text,
        version int,
        PRIMARY KEY (itemid, version)
) WITH CLUSTERING ORDER BY (version DESC);


INSERT INTO documents (itemid_version, xml_payload, insert_time) VALUES ('doc1-1', '<?xml>1st</xml>', '2014-05-21 18:00:00');
INSERT INTO documents (itemid_version, xml_payload, insert_time) VALUES ('doc1-2', '<?xml>2nd</xml>', '2014-05-21 18:00:00');
INSERT INTO documents (itemid_version, xml_payload, insert_time) VALUES ('doc2-1', '<?xml>1st</xml>', '2014-05-21 18:00:00');
INSERT INTO documents (itemid_version, xml_payload, insert_time) VALUES ('doc2-2', '<?xml>2nd</xml>', '2014-05-21 18:00:00');

INSERT INTO document_versions (itemid, version) VALUES ('doc1', 1);
INSERT INTO document_versions (itemid, version) VALUES ('doc1', 2);
INSERT INTO document_versions (itemid, version) VALUES ('doc2', 1);
INSERT INTO document_versions (itemid, version) VALUES ('doc2', 2);
  1. пользователю нужно получить один (1) документ, который он хочет, он знает идентификатор элемента и версию (не обязательно самую последнюю)

    ВЫБЕРИТЕ * ИЗ документов, ГДЕ itemid_version = 'doc1-2';

  2. пользователю нужно получить один (1) документ, который он хочет, он знает идентификатор элемента, но не знает последнюю версию (вы должны передать конкатенированный идентификатор элемента + версию в результате первого запроса во второй запрос)

    SELECT * FROM document_versions WHERE itemid = 'doc2' LIMIT 1;

    ВЫБЕРИТЕ * ИЗ документов, ГДЕ itemid_version = 'doc2-2';

  3. пользователю нужна история версий одного (1) документа.

    SELECT * FROM document_versions WHERE itemid = 'doc2';

  4. пользователю нужно получить список (1 или более) документов, которые он хочет, он знает идентификатор элемента И версию (не обязательно самую последнюю)

    ВЫБЕРИТЕ * ИЗ документов, ГДЕ itemid_version IN ('doc1-2', 'doc2-1');

Ваше здоровье,

person reggoodwin    schedule 21.05.2014
comment
спасибо @reggoodwin ... что касается варианта использования № 4, я знаю, что это вариант использования, который бросает здесь гаечный ключ, поскольку ваше решение сработало бы для меня, если бы у меня не было этого требования. в основном, для варианта использования № 4 это сводится к правам, т. е. некоторые пользователи имеют право только на определенную версию документа (опять же, определенные пользователи могут не иметь право на последнюю версию). - person Dexter Legaspi; 21.05.2014
comment
на самом деле, для этого подхода SELECT * FROM documents where itemid IN ('doc1', 'doc2'); возвращает ВСЕ версии документов, а не только последние. - person Dexter Legaspi; 21.05.2014
comment
Декстер, спасибо, что указали на проблему с моим примером использования № 4. Я не тестировал этот последний запрос перед добавлением некоторых окончательных INSERT. Теперь я повторно представил другой пример кода, который, я надеюсь, работает лучше. На самом деле это очень похоже на ваше решение. - person reggoodwin; 21.05.2014
comment
На самом деле этот подход является антипаттерном Cassandra, поскольку вы пытаетесь минимизировать количество чтений. Первое эмпирическое правило состоит в том, что каждая таблица должна строиться для обслуживания определенного запроса. - person Igor Zelaya; 14.11.2017
comment
Есть ли шанс, что я могу использовать систему ключ-значение в табличных данных для ее версии (данных) с помощью Cassandra? Моя цель - предоставить пользователю патч для вставки, удаления и обновления каждый раз, когда поступают данные и должна быть сформирована версия. Примечание: эти исправления являются версией-кандидатом по сравнению с текущей последней версией. - person aviral sanjay; 15.01.2019

Давайте посмотрим, сможем ли мы придумать модель сверху вниз, начиная с ваших запросов:

CREATE TABLE document_versions (
  itemid uuid,
  name text STATIC,
  vewrsion int,   
  xml_payload text,
  insert_time timestamp,
  PRIMARY KEY ((itemid), version)
) WITH CLUSTERING ORDER BY (version DESC);
  1. Вариант использования 1: пользователю нужно получить один (1) документ, который он хочет, он знает идентификатор элемента и версию (не обязательно самую последнюю)

    SELECT * FROM document_versions 
      WHERE itemid = ? and version = ?;
    
  2. Вариант использования 2: пользователю нужно получить единственный (1) документ, который он хочет, он знает идентификатор элемента, но не знает последнюю версию

    SELECT * FROM document_versions
      WHERE itemid = ? limit 1;
    
  3. Вариант использования 3: пользователю нужна история версий одного (1) документа.

    SELECT * FROM document_versions 
      WHERE itemid = ?
    
  4. Вариант использования 4: пользователю нужно получить список (1 или более) документов, которые он хочет, он знает идентификатор элемента И версию (не обязательно самую последнюю)

    SELECT * FROM documents 
      WHERE itemid = 'doc1' and version IN ('1', '2');
    

    Одна таблица для всех этих запросов — правильный подход. Я бы посоветовал пройти бесплатный онлайн-курс Datastax: Моделирование данных DS220

person Igor Zelaya    schedule 14.11.2017
comment
действительно, это, вероятно, лучший ответ для Cassandra v2.2 и выше поскольку добавлена ​​поддержка предложения IN для любого поля раздела. обратите внимание, что я задал вопрос более чем за год до выпуска v2.2. - person Dexter Legaspi; 29.05.2019