2015-03-04 3 views
2

У меня есть простая таблица, распространяемая userId:Cassandra предотвращающих дубликаты

create table test (
    userId uuid, 
    placeId uuid, 
    visitTime timestamp, 
    primary key(userId, placeId, visitTime) 
) with clustering order by (placeId asc, visitTime desc); 

Каждая пара (userId, placeId) может иметь 1 или NONE посещений. visitTime - это всего лишь некоторые связанные с ним данные, используемые для сортировки в таких запросах, как select * from test where userId = ? order by visitTime desc.

Как я могу потребовать (userId, placeId) быть уникальным? Мне нужно, чтобы убедиться, что

insert into test (userId, placeId, timeVisit) values (?, ?, ?) 

не вставит 2-й визит в (userId, placeId) с различным временем. Проверка на существование до вставки не является атомарной, есть ли лучший способ?

+0

Вы пытаетесь отслеживать самое последнее время посещения для отдельного пользователя и места? Или какая часть таблицы должна быть уникальной? Почему вы не хотите добавлять visitTime на свой ПК? –

+0

Ваш вопрос непонятен - отредактируйте его, объясняя, что вам нужно и что вы пытаетесь сделать (кстати, ваш оператор неверен в C *) –

+0

Порядок кластеризации применяется только к строкам с одним и тем же ключом раздела (который будет использоваться с идентификатором пользователя в твоем случае). Это не относится к строкам с разными идентификаторами пользователей. См. Больше информации здесь: http://www.datastax.com/documentation/cql/3.0/cql/ddl/ddl_compound_keys_c.html – jny

ответ

3

Позвольте мне понять - если пара (userId, placeId) должна быть уникальной (означает, что вам не нужно ставить две строки с этой парой данных), что такое timeVisit, полезный для первичного ключа? Зачем вам выполнять запрос с использованием order by visitTime desc, если у него будет только одна строка?

Если вам необходимо предотвратить дублирование, у вас есть 2 способа.

1 - Легкая сделка - это, используя IF NOT EXISTS, сделает то, что вы хотите. Но, как я объяснил, here легкие транзакции действительно медленные из-за особой обработки cassandra

2 - USING TIMESTAMP Writetime enforcing - (будьте осторожны!***) 'трюк', чтобы заставить убывающую TIMESTAMP

Позвольте мне привести пример:

INSERT INTO users (uid, placeid , visittime , otherstuffs) VALUES (1, 2, 1000, 'PLEASE DO NOT OVERWRITE ME') using TIMESTAMP 100; 

Это производит этот выход

select * from users; 

uid | placeid | otherstuffs    | visittime 
-----+---------+----------------------------+----------- 
    1 |  2 | PLEASE DO NOT OVERWRITE ME |  1000 

Давайте теперь уменьшить timestamp

INSERT INTO users (uid, placeid , visittime , otherstuffs) VALUES (1, 2, 2000, 'I WANT OVERWRITE YOU') using TIMESTAMP 90; 

Теперь в таблице данные не были обновлены, так как существует высокая операция TS (100) для пары (uid, placeid) - на самом деле здесь выход не изменился

select * from users; 

uid | placeid | otherstuffs    | visittime 
-----+---------+----------------------------+----------- 
    1 |  2 | PLEASE DO NOT OVERWRITE ME |  1000 

Если производительность имеет значение, то использовать решение 2, если производительность не имеет значения, а затем использовать раствор 1. Для решения 2 можно вычислить убывающую метку времени для каждой записи с использованием фиксированного числа минус системное время Миллис

например:

Long decreasingTimestamp = 2_000_000_000_000L - System.currentTimeMillis(); 

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

HTH,
Carlo

+0

Пила этот второй подход http://blog.codezuki.com/blog/2015/01/20/unique-set-with-cassandra. Я просто думал, что произойдет, если временная метка всегда сохраняется одинаково? Будет ли это помогать этому делу (т. Е. Отслеживать, если это не так?) –

1

С помощью Cassandra каждая комбинация первичных ключей (комбинация клавиш + кластеризация) уникальна. Поэтому, если у вас есть запись с первичным ключом (A, B, C), и вы вставляете другой, новый, с теми же (A, B, C) значениями, старый будет перезаписан.

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

+0

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

+0

Извините, я так не понял вас. Я попытаюсь отредактировать. –

0

Если я правильно понял ваше требование, вам не нужно, чтобы visitTime являлся частью первичного ключа. В вашем запросе вам также не нужно сортировать по visitTime, так как всегда будет только одно вхождение комбинации /placeId. Вам не нужно вставлять «запись» без visitTime, потому что вы можете с уверенностью предположить, что если ваш запрос возвращает 0 результатов, пользователь никогда не посещал это место.

Так что, если вы сделаете ваш PRIMARY KEY быть только userId, placeId, то вы можете использовать lightweight transactions для достижения своей цели.

В таком случае вы можете использовать простой insert into test (userId, placeId, timeVisit) values (?, ?, ?) IF NOT EXISTS, который не будет перезаписываться, если уже есть запись с предоставленной комбинацией userId/placeId.

Смежные вопросы