Для начала о позитивных моментах. Как я и сказал раньше, с основными своими задачами C* справляется на Ура. Она была создана для быстрой записи, масштабируемости и отказоустойчивости, и в этом она, наверное, лучшая: я ещё не встречал более простого управления кластером, и за всё это время она меня не подвела ни разу. Однако, самая большая проблема C* в том, что её область применения гораздо уже, чем может показаться из документации. И далее я по пунктам расскажу почему.
CQL — это не SQL
Честно говоря, я вообще не понимаю, зачем разработчики C* решили создать CQL. Он запутывает и дезориентирует, создаёт ложные впечатления о специфике работы C*. Вот вам несколько фактов:
- Главное заблуждение, в которое вводит CQL всех новичков, — это иллюзии о том, что вы сможете делать какие-то выборки. Это не так. С* — это key-value хранилище. Вы не сможете получить подмножество строк в таблице. Либо одну (по ключу), либо все. Для обхода этого ограничения в C* есть «wide rows» — возможность писать в строку любые колонки (независимость от схемы, до 2 млрд уникальных колонок в строке). Но и это спасает только при особом подходе к планированию модели данных.
- CQL вводит понятия partition key и cluster key внутри PRIMARY KEY. Ещё одно крупное заблуждение в принципе работы этой БД. На самом деле строка в C* определяется только через partition-часть. Все «записи», отличающиеся по cluster key, будут просто укладываться друг за другом внутри одной и той же строки.
- Нет никакого compound partition key. Проще всего понять поведение compound key — это представить, что значения полей ключа конкатенируются перед сохранением. И строку можно получить только по полному значению ключа (как в redis, к примеру).
- INSERT и UPDATE в C* — это одно и то же. Отныне и во веки веков.
- Коллекции в CQL — это лишь синтаксический сахар, и записи в них хранятся в отдельных колонках.
- Вторичные индексы — тоже лишь синтаксический сахар. C* создаёт новое семейство ключей (таблицу) для каждого вторичного индекса и дублирует туда записи.
Судя по всему, CQL был создан, чтобы популяризовать эту БД среди новичков, пытаясь скрыть самые важные фундаментальные понятия в работе этой БД.
Особенности проектирования данных
Главный принцип при проектировании данных в C* — «SELECT-driven model». Вы проектируете данные так, чтобы вы их смогли потом получить с помощью SELECT. В рамках key-value wide-row хранилища это подразумевает очень сильную денормализацию. Вы не просто денормализуете данные, как вы раньше привыкли это делать в реляционных БД, вы фактически создаёте отдельную таблицу под каждый запрос. И во многих проектах (там, где очень много данных по объёму) это даёт либо огромный overhead во время map/reduce и агрегации, либо огромный overhead по объёму хранимых данных.
И да, вам сразу стоит быть готовым к тому, что без распределённого агрегирования (Hadoop, Spark, Hive и т.д.) эта БД бесполезна. Разработчики обещают в следующей версии операторы для агрегации данных в CQL, но можете особо не надеяться на них. По архитектуре этой БД ясно, что они будут работать только внутри одной строки.
Каунтеры
Отвожу этому типу данных в этой БД свой собственный раздел, и вот почему: изначально, когда я начал внедрять C* в свой проект, я очень обрадовался: для веб-аналитики очень круто иметь атомарные каунтеры, они очень сильно упрощают систему. Но потом я понял простую истину: никогда, слышите? НИКОГДА не используйте каунтеры в C* (по крайней мере в версии до 2.1.* включительно). Дело в том, что этот тип данных противоречит всей идеологии этой БД, и из-за этого у него огромное количество проблем. Если вы спросите у любого специалиста по C* про каунтеры, он в ответ начнёт лишь злобно хихикать.
Короче:
- Вы не можете класть в строку с каунтерами никаких других значений, кроме каунтеров
- Вы не можете класть каунтеры в коллекции (по причине выше)
- Вы не можете сделать каунтер cluster и partition key (по причине выше), сортировать по ним и ставить на них вторичные индексы, само собой, тоже
- Вы не можете установить каунтеру Time To Live
- Каунтеры имеют проблемы с репликацией (редко, но метко)
- Если вы удалите каунтер, то вы ещё долго не сможете создать его заново
Суть расхождения идеологии каунтеров и самой БД в том, что все операции в C* — идемпотентны (т.е. повторное применении операции не изменяет результата). Именно это даёт безотказную архитектуру при падении узлов, датацентров, проблемах со связью и т.п. Каунтеры же нарушают этот принцип, и внутри сделаны через «light transactions» — сначала считать значение, потом увеличить его. И это вызывает много проблем. В общем, если вам нужны счётчики, лучше воспользуйтесь Redis-прослойкой, а в С* уже складывайте окончательное значение.
На этом пока всё. Выбирайте инструменты с умом. Если что-то кажется слишком хорошим, чтобы быть правдой, то возможно, так оно и есть.
Комментарии (19)
slayerhabr
23.05.2015 02:11Подтверждаю, со многим столкнулся. И в моем случае postgresql показал лучшие результаты по скорости выборки. И вместо mysql+C* просто перешел на postgres.
GearHead Автор
23.05.2015 02:36+4Ну тут вы уж загнули. если задачу можно решить с помощью реляционных БД, то её нужно решить с помощью реляционных БД.
Я вообще не вижу смысла в инструментах типа C*, если ваш набор данных может обработать одна машина.slayerhabr
23.05.2015 02:55Дело в том, что задача — накопление данных скажем по датчикам, чтото типа timeseries (что как раз хорошо ложится в идею К). Можно решить реляционными БД? Конечно, но хотелось использовать чтото заточенное под накопление данных. После сравнительных тестов мускль, К (4 ноды в кластере) и простой постгрес. Победил постгрес по всем показателям.
grossws
23.05.2015 03:29Для накопления данных неплохи всякие решения типа rrd, influx, carbon/whisper, если data flow понятен на этапе проектирования
slayerhabr
23.05.2015 03:53Да, но rrd/carbon не подходил — выборки не только по time индексу, в К держал двойную-тройную копию данных под разные запросы. influxdb на тот момент был еще слишком молод.
slayerhabr
23.05.2015 03:58influxdb не подходит по тем же причинам (кастомные индексы насколько я знаю должны появится в 0.9).
pg справился хорошо — одна большая таблица и несколько индексов.
GearHead Автор
23.05.2015 04:56+2так ведь о том и речь. C* не заточена под абстрактное «накопление данных», она заточена под задачи, в которых вам 100% не хватит одной машины (с PG, не с PG, не важно). просто если рано или поздно PG начнёт захлёбываться, и вам придётся выписывать кульбиты с шардингом и репликациями (и множеством сопутствующих проблем), C* позволит линейно и безотказно расширяться с помощью лишь одной команды в консоли.
slayerhabr
23.05.2015 14:00В ближайшие пару-тройку лет сможем расширятся вертиально. Память и диск сейчас достаточно дешевые. Потом будем думать.
2ANikulin
23.05.2015 09:35Если речь заходит об аналитике на больших данных, map-reduce и всё такое, то надо смотреть в сторону HBase. Это хранилище спроектированно именно для этих целей. Кассандра больше подходит для географически распределенных систем, с повышенной доступностью.
AlexeyShurygin
23.05.2015 16:30Вопрос по п.5.
Почему update и insert — это одно и то же? Afaik в C* это разные вещи.andrewnester
23.05.2015 16:33Отличается только тем, что Insert не работает с каунтерами, в остальном — одно и тоже
AlexeyShurygin
23.05.2015 16:53По поводу для чего придумали CQL.
Подход мне тоже не очень нравится — там внутри неочевидная магия делается, которую чтобы понять надо знать как физически хранятся данные.
Однако программировать через альтернативный Thrift API — это застрелиться, достаточно тяжело, т.ч. SQL-like язык нужен и полезен.
andrewnester
Спасибо за статью, жаль ее не было, когда я так в ней нуждался :)
Как раз недавно делал аналитику и БД была Cassandra
Вкратце — задача была хранить логи действий пользователей в системе и потом на основе этих данных выводить статистику
Так вот
1) действительно столкнулся с проблемой каунтеров (но без них тяжеловато было бы вести подсчет)
2) данные действительно очень денормализуются, вместо 1 коассической реляционной таблицы получилось 12 «кассандровских»
Правда потом уменьшилось до 4, так как часть работы по аггрегации данных вынеслась в логику приложения
В остальном пока все отлично, вставка действительно очень быстрая, выборка при правильно аггрегированных данных тоже работает хорошо
grossws
Ещё кассандра хороша, как движок для хорошего параллельного hash-merge join'а =)
Jabher
аэээээмм, elasticsearch?