Созданная в 2008-м году ребятами из Facebook, на сегодняшний день Cassandra (название иногда сокращают до «C*») является одним из наиболее популярных NoSQL решений. Проект полностью написан на Java и успешно используется в IBM, Apple, Netflix, Twitter, Яндексе, CERN, SoundCloud, Rackspace, а также множестве других компаний . Сегодня мы поднимем наш собственный маленький Cassandra-кластер в облаке Амазона, но сначала, как обычно, немного теории.
Теоретическая часть
Главное, что нужно знать о Cassandra:
- Cassandra считается гибридом key-value и column-oriented баз данных . Как и в РСУБД, есть базы данных, в них таблицы ( column families ), в них строки. Значения в строках хранятся в виде троек (имя столбца, значение, таймстамп). Лично мне всегда больше нравилось думать о Cassandra, как о базе данных, где каждая строка представляет собой хэш-таблицу . Возможно, вам такое представление покажется более понятным. В строках одной и той же таблицы могут быть определены разные столбцы. Таким образом, Cassandra эффективно хранит разряженные таблицы. Притом, в одной таблице может быть до двух миллионов столбцов;
- C точки зрения пользователя Cassandra ведет себя как урезанная РСУБД с поддержкой своего диалекта SQL без джоинов (Сassandra Query Language, CQL). Можно считать Сassandra в какой-то степени NewSQL решением. Хотя при этом на Cassandra и графы неплохо моделируются. Если столбец в строке не определен, пользователь видит его, как null;
- Помимо обычных значений, вроде целых чисел, строк и дат, в столбцах могут храниться коллекции, например, map<T1, T2>, list<T> и set<T>. Также поддерживаются counter columns и static columns;
- Есть поддержка TTL на уровне отдельных столбцов в конкретной строке:
insert into ... using ttl [SEC]
. Есть поддержка TTL по умолчанию на уровне таблицы; - Cassandra является распределенной и отказоустойчивой базой данных. Одна строка может храниться на нескольких физических машинах. Каком количестве — определяется фактором репликации конкретной таблицы. Если у вас фактор репликации N и вы погасили N-1 машин, данные будут и дальше доступны. Внутри это хозяйство реализовано во многом так же, как в Riak , с хэш-кольцом, AP и вот этим всем;
- Только, в отличие от Riak, все конфликты разрешаются автоматически , благодаря хранению таймстампов для каждого столбца в конкретной строке. Например, вы почти одновременно делаете два запроса —
update users set email = 'test@example.ru' where uid = 123
иupdate users set birthday = 586787696 where uid = 123
, создавая тем самым конфликт на узлах, отвечающих за хранение этой строки. В этом случае конфликт будет успешно разрешен по времени последнего изменения столбцов, и вы получите пользователя с адресом test@example.ru и днем рождения 586787696; - Кроме того, есть поддержка CAS, реализованная внутри на основе алгоритма Paxos. Понятное дело, в этом случае таймстампы для разрешения конфликтов не используются;
- При каждом чтении и записи клиент может указать желаемый уровень консистентности — ANY, ONE, TWO, THREE, QUORUM, SERIAL, ALL и другие. Например, уровень ONE (используется по умолчанию) говорит, что запрос должен дойти хотя бы до одного узла, отвечающего за хранение строки, а уровень QUORUM — что запрос должно получить большинство узлов, например, 2 из 3. Таким образом, можно всегда выбирать между скоростью выполнения запросов и надежностью. По умолчанию в фоне ноды Cassandra крутят read repair процесс, приводящий все ноды в консистентное состояние, поэтому для многих задач ONE является вполне подходящим уровнем;
- Данные хранятся в LSM tree со всеми вытекающими последствиями. Например, использовать Cassandra в режиме вроде message queue очень плохо, потому что вы будете постоянно писать данные, затем tombstone’ы, затем выполнять сжатие данных, из-за чего время ответа станет прыгать в широких пределах. За счет использования LSM tree Сassandra обеспечивает очень высокую скорость записи и при этом хорошую скорость чтения;
- Несмотря на то, что memtable в Cassandra используется только для буферизации записи, а не для кэширования чтения, в Cassandra предусмотрен row cache, да и кэш файловой системы никто не отменял. Поэтому, в принципе, есть шанс обойтись в проекте одной только Cassandra, не поднимая дополнительно никаких Memcached , Redis или Couchbase . Хотя, конечно, каждый конкретный случай нужно мерить отдельно. А еще в Enterprise версии есть in-memory таблицы;
- У каждой таблицы есть primary key, который может быть составным. Primary key при этом делится на две части — partition key и clustering key. Первый используется для шардирования данных по нодам, в то время, как второй — для упорядочивания данных внутри конкретной ноды. Правильно подобрав clustering key можно делать эффективные выборки по диапазонам значений;
- Поддерживаются несколько стратегий шардированния данных в кластере. Например, Murmur3Partitioner распределяет данные между узлами по хэшу от partition key и используется по умолчанию. Но также доступен и ByteOrderedPartitioner, название которого говорит само за себя;
- Помимо clustering key есть еще и вторичные индексы. Являются довольно эффективными. В смысле, что не приводят к посылке запросов всем узлам кластера, если в запросе указан partition key. Поддержка вторичных индексов делает запись медленнее, поэтому по возможности лучше их избегать. В Cassandra версии 2.1 была добавлена возможность строить вторичные индексы по коллекциям. При этом Cassandra не поддерживают выборку при помощи вторичных индексов по диапазону, видимо, из-за разряженности таблиц;
И еще парочка интересных моментов. Начиная с Cassandra 2.1 появились user defined types. А в Cassandra 2.2 можно будет писать хранимые процедуры и агрегаты. Есть поддержка аутентификации и работы с клиентом по SSL. В Cassandra 2.2 обещают добавить роли. Есть репликация между дата-центрами, rack awareness, и даже MapReduce (понятно, что не нужно пытаться использовать Cassandra вместо Hadoop!). Предусмотрено удобное резервное копирование. Команда nodetool snapshot
буквально моментально создает снапшот базы данных, после чего его можно заливать в S3 . Запускаем по крону на каждой ноде, и профит! Подробности о резервном копировании можно найти здесь . Наконец, Cassandra можно использовать в качестве распределенной файловой системы — см Cassandra File System (CFS) .
Звучит интересно? Так давайте же поднимем в облаке Амазона наш собственный Cassandra кластер!
Установка Cassandra в AWS
Устанавливать будем на инстансы m3.large c Ubuntu 14.04 LTS. В Security Group разрешаем ходить на порт 22 откуда угодно, а на порты 1024-65535 только изнутри VPC (172.16.0.0/12). На инстансах m3.large своп выключен (проверяем командой swapon -s
), у вас должно быть так же.
Время на машинах внутри AWS может сильно разъезжаться, совсем недавно я видел разницу в несколько минут. Поэтому первым делом устанавливаем ntpd:
ntpq -pn
Затем ставим Java:
sudo apt-get update
sudo apt-get install oracle-java8-installer
sudo apt-get install oracle-java8-unlimited-jce-policy
Наконец, устанавливаем Cassandra:
echo «deb http://debian.datastax.com/community stable main» |
sudo tee -a / etc / apt / sources.list.d / cassandra.sources.list
sudo apt-get update
sudo apt-get install dsc21 cassandra-tools
Важные файлы и каталоги:
- /etc/cassandra/cassandra.yaml — основные настройки;
- /etc/cassandra/cassandra-env.sh — все параметры JVM;
- /var/log/cassandra/system.log — смотрим сюда, если что-то сломалось;
- /var/lib/cassandra/ — все данные;
У меня Cassandra крутится еще и локально, в Vagrant , для тестов. В файле /etc/cassandra/cassandra-env.sh я прописал:
HEAP_NEWSIZE = «150M»
Пока Cassandra вроде не жалуется. В реальном, боевом, окружении, понятно, эти параметры лучше вообще не трогать без явной нужды, так как скрипт вычисляет их автоматически. Кроме того, можно настроить JMX:
JVM_OPTS = » $JVM_OPTS -Dcom.sun.management.jmxremote.port=9999″
JVM_OPTS = » $JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false»
JVM_OPTS = » $JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false»
И подключиться к Cassandra с помощью jconsole или jvisualvm:
В jconsole во вкладке MBeans можно посмотреть разные интересные метрики, например, количество попаданий в кэш и такого рода вещи.
Создание кластера
Работать с одной нодой как-то неинтересно, поэтому поднимем-ка мы кластер из трех нод.
Останавливаем Cassandra:
Сносим все данные:
Правим /etc/cassandra/cassandra.yaml. Поменять нужно следующее.
А то по умолчанию там написано «Test Claster». Если у вас больше одного кластера, убедитесь, что они имеют уникальные названия.
parameters:
— seeds: «172.1.2.3»
Это наша первая нода, поэтому в seeds пишем только ее адрес внутри VPC.
Этот адрес также передается по Gossip другим нодам, поэтому тут нельзя написать «слушай все интерфейсы».
А вот rpc address используется только для взаимодействия с клиентом, поэтому здесь можно.
Но только если указать при этом конкретный broadcast rpc addess.
По умолчанию используется SimpleSnitch. В случае с Амазоном имеет смысл рассмотреть опции Ec2Snitch и Ec2MultiRegionSnitch. В двух словах, эта опция позволяет разместить реплики данных как можно дальше друг от друга, чтобы все реплики не находились в одной стойке или датаценте. За подробностями обращайтесь к комментариям в конфиге.
Запускаем Cassandra:
Проверяем, что нода поднялась:
Поднимаем вторую ноду аналогично. В консоли Амазона можно создать AMI, а из этого AMI — готовую машину, после чего перейти к шагу с остановкой сервера и удалением данных. Список seeds указываем такой же, listen_address меняем. Затем поднимаем и третью ноду. В seeds указываем адрес первой и второй ноды. Для всех остальных нод можно использовать такой же список из трех нод. Все узлы добавлять в seeds не рекомендуется. В результате nodetool status
должен показать столько нод, сколько мы добавили.
Поздравляю, кластер поднят! Просто, не правда ли?
Основы работы с cqlsh и языком CQL
Cassandra поддерживает несколько интерфейсов. С ней можно работать по протоколу Thrift , который считается устаревшим и который в Cassandra 3.0 обещают выкинуть, а также по новому протоколу CQL , название которого совпадает с названием языка запросов. Для работы с Cassandra по трифту можно воспользоваться утилитой cassandra-cli. Но нам, к счастью, незачем беспокоиться об обратной совместимости, поэтому сразу воспользуемся более актуальной утилитой cqlsh.
Создадим новую базу данных, или, в терминологии Cassandra, кейспейс:
‘replication_factor’:2};
cqlsh> use test;
Создадим новую таблицу:
description text );
Заполним ее какими-нибудь данными:
cqlsh:test> insert into todo_list (id,description) values (2,’Item 2′);
cqlsh:test> insert into todo_list (id,description) values (3,’Item 3′);
Выборка значений:
id | description
——+————-
1 | Item 1
2 | Item 2
3 | Item 3
(3 rows)
Нельзя просто так взять и выбрать строку по description, это вам не MySQL:
InvalidRequest: code=2200 [Invalid query] message=»No secondary
indexes on the restricted columns support the provided operators: »
Поэтому построим вторичный индекс:
Вот теперь можно:
id
——
1
(1 rows)
В этом месте можно на время выключить одну из нод и проверить, что все данные все еще доступны. После обратного включения ноды она какой-то время висит в nodetool status
, как down, но через минуту где-то все становится ОК.
Переключение в «вертикальный вывод», аналог x
из PostgreSQL:
Now Expanded output is enabled
cqlsh:test> expand off
Disabled Expanded output.
Включение-выключение трассировки запросов:
Now Tracing is enabled
cqlsh:test> tracing off;
Disabled Tracing.
Получение информации о таблице или кейспейсе:
… skiped …
cqlsh:test> desc keyspace test;
… skiped …
Удаление строки:
Удаление таблицы:
Удаление кейспейса:
Выход из cqlsh:
Работа с составными primary keys, counter columns, коллекциями и прочим, к сожалению, выходит за рамки данного поста. Что касается составных primary keys и определения, где у него partition key, а где clustering key, синтаксис там примерно такой:
table1 (field1, field2, field3, field4, field5)
primary key ((field1, field2), field3, field4))
with clustering order by (field3 desc, field4 asc);
Здесь (field1, field2) будет partition key, (field3, field4) — clustering key. Придумывание правдоподобного примера и проверку эффективности range scan’ов по clustering key при помощи трассировки можете считать своим домашним заданием.
Заключение
В первом приближении, впечатления от Cassandra у меня только положительные. Документация отличная, много книг, активное сообщество, радует похожесть по семантике на РСУБД. Падение ноды ни на чем не сказывается, ну и так далее. Следует только иметь в виду, что в мире Cassandra есть много устаревших книг и статей. Как уже отмечалось, Thrift устарел, и сейчас рулит CQL3. Если вы слышали про supercolumns, то их тоже использовать уже не рекомендуют.
Ссылки по теме:
- Официальная документация очень годна и доступна в PDF ;
- Список изменений в новых версиях, начиная с версии 0.3 ;
- Рассылка user@cassandra.apache.org, на вопросы отвечают быстро ;
- DataStax Developer Blog, обязательно подписываемся ;
- Хорошая статья «Basic Rules of Cassandra Data Modeling»
- Хорошая серия статей о Cassandra: часть 1 , часть 2 , часть 3 ;
Ну и, как обычно, про Reddit и StackOverflow не забывайте.
А используете ли вы Cassandra и если да, то для каких задач?
Дополнение: Простая программа на Scala, работающая с Cassandra