2014-10-24 5 views
8

Выполнение двух идентичных запросов, но ключевое слово DISTINCT дает неожиданные результаты. Без ключевого слова результат будет одобрен, но с DISTINCT, похоже, что предложение where игнорируется. Зачем ? версияSELECT DISTINCT cql игнорирует предложение WHERE

Cqlsh:

Connected to Test Cluster at localhost:9160. 
[cqlsh 4.1.1 | Cassandra 2.0.6 | CQL spec 3.1.1 | Thrift protocol 19.39.0] 

Таблица считается:

DESCRIBE TABLE events; 

CREATE TABLE events (
    userid uuid, 
    "timestamp" timestamp, 
    event_type text, 
    data text, 
    PRIMARY KEY (userid, "timestamp", event_type) 
) WITH 
    bloom_filter_fp_chance=0.010000 AND 
    caching='KEYS_ONLY' AND 
    comment='' AND 
    dclocal_read_repair_chance=0.000000 AND 
    gc_grace_seconds=864000 AND 
    index_interval=128 AND 
    read_repair_chance=0.100000 AND 
    replicate_on_write='true' AND 
    populate_io_cache_on_flush='false' AND 
    default_time_to_live=0 AND 
    speculative_retry='99.0PERCENTILE' AND 
    memtable_flush_period_in_ms=0 AND 
    compaction={'class': 'SizeTieredCompactionStrategy'} AND 
    compression={'sstable_compression': 'LZ4Compressor'}; 

содержание таблицы:

SELECT * FROM events; 

userid        | timestamp    | event_type | data 
--------------------------------------+--------------------------+------------+------ 
aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 |  toto | null 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:06:17+0100 |  toto | null 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:07:17+0100 |  toto | null 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:08:17+0100 |  toto | null 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:09:17+0100 |  toto | null 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e | 1970-01-17 09:10:17+0100 |  toto | null 

(6 rows) 

Request1: Запрос без DISTINCT

SELECT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING; 

userid 
-------------------------------------- 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e 

(3 rows) 

Request2: То же запрос с DISTINCT

SELECT DISTINCT userid FROM events WHERE timestamp > '1970-01-17 09:07:17+0100' ALLOW FILTERING; 

userid 
-------------------------------------- 
aaaaaaaa-be1c-44ab-a0e8-f25cf6064b0e 
4271a78f-be1c-44ab-a0e8-f25cf6064b0e 

(2 rows) 

EDIT 1
здесь некоторый контекст.
Эта таблица «событий» подвержена множеству записей, она получает около ~ 1k вставки в секунду, и у меня есть пакетный скрипт, который проверяет эти события каждые 5 минут.
Этот пакетный сценарий имеет 2 потребности:
1- получить все UserIds, которые были активны в течение последних 5 минут (то есть каждый идентификатор пользователя присутствует в событиях за последние 5 минут)
2- получить все события, связанные с этими идентификаторами пользователей (не только последние 5 минут)

Раньше у меня были две разные таблицы. Одна таблица «активных пользователей» для первого запроса и таблица «событий», как я описал здесь для второго запроса. Моя проблема заключается только в том, что от моего сервера требуется писать в двух разных таблицах, когда он получает событие. Поэтому я попробовал это, используя только таблицу событий.

+1

Просто наблюдение, но вы используете 'timestamp' как часть составного ключа. Я бы предложил использовать 'timeuuid' здесь, чтобы предотвратить коллизии и перезаписать записи. 'timestamp', если он отлично расположен вне первичного ключа. – dtoux

+1

Является ли ваш столбец timestamp DESC? Похоже, вам нужно, чтобы это было заказано именно так. –

ответ

15

Это происходит потому, что в Cassandra CQL DISTINCT предназначен для возврата только к разделам (строке) ключей вашей таблицы (семейства столбцов) ... которые должны быть уникальными. Следовательно, предложение WHERE может работать только с ключами раздела при использовании с DISTINCT (что в вашем случае не очень полезно). Если вы берете DISTINCT, то WHERE может быть использован для оценки ключей кластеризации (столбца) внутри каждой клавиши раздела (хотя, с ALLOW FILTERING).

Я чувствую себя вынужденным упомянуть, что ALLOW FILTERING не то, что вы должны делать много ... и определенно не в производстве. Если этот запрос является один вам нужно часто запускать (запрашивая события для userids после определенного timestamp), то я хотел бы предложить разделение данных по event_type вместо:

PRIMARY KEY (event_type, "timestamp", userid) 

Тогда вы сможете запустить этот запрос без ALLOW FILTERING ,

SELECT userid FROM events WHERE event_type='toto' AND timestamp > '1970-01-17 09:07:17+0100' 

Не зная ничего о вашей заявке или прецеденте, который может быть или не быть полезным для вас. Но рассмотрите это как пример и как указание на то, что может быть лучший способ построить вашу модель для удовлетворения ваших шаблонов запросов. Ознакомьтесь с Patrick McFadin's article on timeseries data modeling, чтобы узнать больше о том, как моделировать эту проблему.

+1

Спасибо за ваш ответ, я добавил некоторый контекст для моего вопроса. Я немного удивлен тем, что cqlsh не говорит мне, что я делаю что-то неправильное, фильтруя ключ без раздела, хотя я использую ключевое слово DISTINCT. О разрешении фильтрации, я думаю, что два ожидания, которые у меня есть из моей таблицы событий (что я описал в моем недавнем редактировании), то есть получение события по идентификаторам пользователей и по метке времени скомпрометировано. – Diplow

+2

Верный ответ! –

3

Как объясняется Аароном, при использовании ключевого слова DISTINCT вы можете фильтровать только ключи раздела. Причиной этого является алгоритм запросов DISTINCT и способ, которым Cassandra хранит данные на диск/память.

Чтобы понять это, я буду проводить аналогию:

Cassandra хранит информацию, похожую на индекс книги. Если вы ищете раздел под названием «Моя третья глава», вам нужно только посмотреть на первый уровень индекса для него, поэтому вам нужно всего лишь выполнить итеративный поиск в относительно небольшом наборе. Однако, если вы ищете подглаву под названием «Моя четвертая подглавная глава», принадлежащая «Моя вторая глава», вам нужно будет выполнить 2 итерационных поиска в 2 разных наборах, как малых, при условии, что индекс имеет не менее 2 уровни. Чем глубже вам нужно идти, тем дольше может потребоваться (вам все еще может быть повезло и найти ее очень быстро, если она находится в начале индекса, но в таких алгоритмах вы должны протестировать средний и худший сценарий) и чем сложнее этот индекс, тем лучше.

Cassandra делает что-то похожее: Keyspace -> Table -> Partition Key -> Clustering Key -> Column Чем глубже вам нужно идти, тем больше наборов вам нужно иметь в памяти, и для поиска потребуется больше времени. Индекс, используемый для выполнения запросов DISTINCT, может даже содержать множества до уровня ключа раздела, таким образом, только для поиска ключей раздела.

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

Если они решат использовать DISTINCT для кластеризации, то ваш запрос будет в порядке. Между тем вам придется фильтровать их в приложении, возможно, используя встроенный тип, называемый set или что-то подобное, которое обрабатывает повторяющиеся значения самостоятельно.

Не решение, предлагаемое Aaron (с использованием идентификатора пользователя как ключа кластеризации после отметки времени), ни этот (фильтрация на стороне клиента) не использует быстрый механизм DISTINCT. Его предложение не требует фильтрации на стороне клиента, поскольку оно уже обрабатывает это для вас, но предлагает два основных недостатка: он не предлагает обратной совместимости, поскольку вам придется воссоздать таблицу и использовать постоянный ключ раздела и, таким образом, не разрешать Cassandra, чтобы распространять эти данные среди своих узлов. Помните, что каждое значение одного и того же ключа разделов хранится в одном узле.

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