Aleksey Yeschenko "Моделирование данных с помощью CQL3". Выступление на Cassandra Conf 2013

1,395 views

Published on

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,395
On SlideShare
0
From Embeds
0
Number of Embeds
414
Actions
Shares
0
Downloads
22
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Aleksey Yeschenko "Моделирование данных с помощью CQL3". Выступление на Cassandra Conf 2013

  1. 1. Моделирование данных с CQL3: типичные паттерны и анти-паттерны Aleksey Yeschenko
 Apache Cassandra Committer, Engineer @DataStax @AYeschenko
  2. 2. Кто я такой • Backend Ruby web-dev (давно и неправда) • Telecom Erlang dev (недавно и правда) • Принуждают использовать Java+Python (cqlsh) в DataStax с июля 2012 • Apache Cassandra committer с ноября 2012 • @AYeschenko
  3. 3. Когда использовать C* • Необходима Active-Active репликация между несколькими датацентрами • Необходим (около) 100% аптайм • Данные больше не умещаются в PostgreSQL/MySQL/etc. • PostgreSQL/MySQL/etc. больше не справляются с объёмом записи • Модель Cassandra изначально идеально подходит для приложения • Нет необходимости в ad-hoc запросах • Любое другое решение стоит $$$$$ * 1 и более пунктов из списка == true
  4. 4. Когда не использовать C* • Во всех остальных случаях
  5. 5. Как использовать, если использовать
  6. 6. Корни С* Dynamo BigTable • http://www.allthingsdistributed.com/2007/10/amazons_dynamo.html • http://research.google.com/archive/bigtable.html
  7. 7. Как хранятся данные + терминология • Partition ключ и множество ячеек (до 2 миллиардов) • Хэш partition ключа определяет ноды, к которым принадлежит partition • Быстрое чтение по partition ключу • На диске и в памяти, ячейки всегда отсортированы по имени • Компаратор для сортировки задаётся пользователем 1 Имя ячейки 2 Миллиарда ... Имя ячейки Значение ячейки Значение ячейки Timestamp Timestamp TTL TTL Partition ключ
  8. 8. Идеальная нагрузка • Запись доминирует • Перезапись/удаление ячеек отсутствуют или не очень часты • Чтение по ключу + (имени ячейки или небольшим отрезкам ячеек в порядке компаратора)
  9. 9. Идеальная нагрузка • Логи • Данные с сенсоров • Финансовые данные (ticks) • Ads - клики и отображения, аналитика • Необязательно именно временные ряды (в порядке timestamp); любые данные, естественно упорядоченные по последовательному значению • Нужны последние N значений
  10. 10. Паттерн #1: временной ряд
  11. 11. Паттерн #1: временной ряд CREATE TABLE temperature ( weatherstation_id text, partition ключ event_time timestamp, кластеринговая колонка temp int, PRIMARY KEY (weatherstation_id, event_time) ) WITH CLUSTERING ORDER BY (event_time DESC); составной ключ обратный порядок сортировки ячеек • Каждая станция регулярно собирает показания • Один partition == одна станция • Одно показание == одна ячейка (здесь две, технически) • Потенциально очень широкий partition
  12. 12. Паттерн #1: временной ряд CREATE TABLE temperature ( weatherstation_id text, partition ключ event_time timestamp, кластеринговая колонка temp int, PRIMARY KEY (weatherstation_id, event_time) составной ключ обратный порядок сортировки ячеек ) WITH CLUSTERING ORDER BY (event_time DESC); SVO weatherstation_id 1386475781 1386475781:temp 1386475780 1386475780:temp 1386475779 1386475779:temp … * -6 * event_time -6 * temp -5 … * маркеры CQL3 ряда
  13. 13. Паттерн #1: временной ряд CREATE TABLE temperature ( weatherstation_id text, date text, составной partition ключ event_time timestamp, temp int, PRIMARY KEY ((weatherstation_id, date), event_time) ) WITH COMPACT STORAGE AND CLUSTERING ORDER BY (event_time DESC); • Один partition для пары {weatherstation_id, date} • Отсутствие оверхеда маркера ряда и имени колонки
  14. 14. Паттерн #1: временной ряд CREATE TABLE temperature ( weatherstation_id text, date text, составной partition ключ event_time timestamp, temp int, PRIMARY KEY ((weatherstation_id, date), event_time) ) WITH COMPACT STORAGE AND CLUSTERING ORDER BY (event_time DESC); SVO:2013-12-09 1386475781 1386475780 1386475779 … -6 -6 -5 … date weatherstation_id timestamp temp
  15. 15. Анти-паттерн #1: неуместное использование встроенных индексов • RDBMS опыт с индексацией НЕ переносится в C*
  16. 16. Анти-паттерн #1: неуместное использование встроенных индексов • RDBMS опыт с индексацией НЕ переносится в C* • Встроенные индексы в C* != RDBMS индексы
  17. 17. Анти-паттерн #1: неуместное использование встроенных индексов • RDBMS опыт с индексацией НЕ переносится в C* • Встроенные индексы в C* != RDBMS индексы • Встроенные индексы в C* != RDBMS индексы
  18. 18. Анти-паттерн #1: неуместное использование встроенных индексов • RDBMS опыт с индексацией НЕ переносится в C* • Встроенные индексы в C* != RDBMS индексы • Встроенные индексы в C* != RDBMS индексы • Встроенные индексы в C* != RDBMS индексы
  19. 19. Анти-паттерн #1: неуместное использование встроенных индексов • RDBMS опыт с индексацией НЕ переносится в C* • Встроенные индексы в C* != RDBMS индексы • Встроенные индексы в C* != RDBMS индексы • Встроенные индексы в C* != RDBMS индексы • Для удобства, не для улучшения производительности • Достаточно ограничены в плане удобства • Следует избегать, по большей части
  20. 20. Анти-паттерн #1: неуместное использование встроенных индексов cqlsh:nope> CREATE TABLE users ( ... username varchar, ... email varchar, ... fullname varchar, ... last_login timestamp, ... PRIMARY KEY (username) ... ); ! cqlsh:nope> INSERT INTO users (username, email, fullname) VALUES ('ay', 'aleksey@datastax.com', 'Aleksey Yeschenko'); ! cqlsh:nope> SELECT * FROM users WHERE email = 'aleksey@datastax.com'; Bad Request: No indexed columns present in by-columns clause with Equal operator
  21. 21. Анти-паттерн #1: неуместное использование встроенных индексов
  22. 22. Анти-паттерн #1: неуместное использование встроенных индексов cqlsh:nope> CREATE INDEX ON users (email); ! cqlsh:nope> SELECT * FROM users WHERE email = 'aleksey@datastax.com'; username | email | fullname | last_login ----------+----------------------+-------------------+------------ ay | aleksey@datastax.com | Aleksey Yeschenko | ! (1 rows) null
  23. 23. Анти-паттерн #1: неуместное использование встроенных индексов • При высокой и средней кардинальности колонки решается ‘ручным’ вторичным индексом • Отдельная таблица для обратного лукапа
  24. 24. Анти-паттерн #1: неуместное использование встроенных индексов cqlsh:yep> CREATE TABLE users ( ... username varchar, ... email varchar, ... fullname varchar, ... last_login timestamp, ... PRIMARY KEY (username) ... ); cqlsh:yep> CREATE TABLE users_by_email_idx ( ... email varchar, ... username varchar, ... PRIMARY KEY (email, username) ... );
  25. 25. Анти-паттерн #1: неуместное использование встроенных индексов cqlsh:yep> BEGIN BATCH ... INSERT INTO users (username, email, fullname) VALUES ('ay', 'aleksey@datastax.com', 'Aleksey Yeschenko'); ... INSERT INTO users_by_email_idx (email, username) VALUES ('aleksey@datastax.com', 'ay'); ... APPLY BATCH; ! cqlsh:yep> SELECT username FROM users_by_email_idx WHERE email = 'aleksey@datastax.com'; username ---------- ay ! cqlsh:yep> SELECT * FROM users WHERE username = 'ay'; username | email | fullname | last_login ----------+----------------------+-------------------+------------ ay | aleksey@datastax.com | Aleksey Yeschenko | null
  26. 26. Анти-паттерн #1: неуместное использование встроенных индексов • При средней/низкой кардинальности свой, отдельный набор проблем • Главная - большое количество random i/o • Решается правильной моделью/денормализацией вместо использования встроенных индексов
  27. 27. Анти-паттерн #1: неуместное использование встроенных индексов • Q: Когда вообще следует использовать встроенные индексы? • A: Для поиска рядов с 1+ кластеринговыми колонками внутри определённого partition (зная partition ключ)
  28. 28. Анти-паттерн #1: неуместное использование встроенных индексов CREATE TABLE example ( partition_key varchar, clustering_col0 varchar, clustering_col1 varchar, val varchar ); ! CREATE INDEX ON example (val); ! SELECT * FROM example WHERE partition_key = ‘some_pk’ AND val = ‘some_val’;
  29. 29. Анти-паттерн #1: неуместное использование встроенных индексов • Всё вышесказанное распространяется на индексы коллекций в 2.1 • Обращайтесь со встроенными индексами как с expert-level feature
  30. 30. Паттерн #2: денормализация • Отношения без реляционности • Без использования foreign key CREATE TABLE users (! username varchar,! firstname varchar,! lastname varchar,! email varchar, ! PRIMARY KEY(username)! ); • Users 1-M Videos Users username firstname lastname email tcodd rboyce Edgar Raymond Codd Boyce videoid 99051fe9 videoname My funny cat b3a76c6b Math Videos tcodd@relational.com rboyce@relational.com username description tcodd My cat plays the pianodog Now my tcodd plays tags cats,piano,lol dogs,piano,lol CREATE TABLE videos (! videoid uuid,! videoname varchar,! username varchar,! description varchar, ! tags varchar,! upload_date timestamp,! PRIMARY KEY(videoid)! );
  31. 31. Паттерн #2: денормализация • Лукап видео по username • Запись в две таблицы сразу • Без встроенных индексов CREATE TABLE username_video_index (! username varchar,! videoid uuid,! upload_date timestamp,! video_name varchar,! PRIMARY KEY (username, videoid)! ); SELECT video_name! FROM username_video_index! WHERE username = ‘ctodd’! AND videoid = ‘99051fe9’
  32. 32. Паттерн #2: денормализация • Users 1-M Comments • Videos 1-M Comments • Без встроенных индексов Users username firstname lastname email tcodd rboyce Codd Boyce videoid 99051fe9 Edgar Raymond videoname My funny cat b3a76c6b Math Videos tcodd@relational.com rboyce@relational.com username description tcodd My cat plays the pianodog Now my tcodd plays tags cats,piano,lol dogs,piano,lol Comments username tcodd videoid 99051fe9 comment Sweet! rboyce b3a76c6b Boring :(
  33. 33. Паттерн #2: денормализация • Две таблицы для выборки по видео и по пользователям • Запись в три таблицы сразу (comments, comments_by_video, comments_by_user) • Не жалеть запись CREATE TABLE comments_by_video (! videoid uuid,! username varchar,! comment_ts timestamp,! comment varchar,! PRIMARY KEY (videoid,username)! ); CREATE TABLE comments_by_user (! username varchar,! videoid uuid,! comment_ts timestamp,! comment varchar,! PRIMARY KEY (username,videoid)! );
  34. 34. Анти-паттерн #2: очереди и очередеподобные нагрузки
  35. 35. Анти-паттерн #2: очереди и очередеподобные нагрузки • Cassandra не лучшее решение для очередей • Можно, но требует гимнастики • Лучше использовать что-нибудь вроде Kafka/RabbitMQ • Любой паттерн, где в partition удаляются ячейки и выполняются slice сканы, включающие удалённые ячейки • Пример - нарочно упрощён, ради иллюстрации
  36. 36. Анти-паттерн #2: очереди и очередеподобные нагрузки —— naive путь CREATE TABLE queues ( name text, enqueued_at timeuuid, payload blob, PRIMARY KEY (name, enqueued_at) —— ячейки упорядочены по времени вставки, ASC ) WITH COMPACT STORAGE;
  37. 37. Анти-паттерн #2: очереди и очередеподобные нагрузки —— 1000x добавить в очередь: INSERT INTO queues (name, enqueued_at, payload) VALUES (‘queue-1’, now(), 0x…); ! —— 999x убрать из очереди, индивидуально: DELETE FROM queues WHERE name = ‘queue-1’ AND enqueued_at = ?; ! —— достать последний элемент из очереди: SELECT enqueued_at, payload FROM queues WHERE name = 'queue-1' LIMIT 1;
  38. 38. Анти-паттерн #2: очереди и очередеподобные нагрузки queue-1 51c220e0-60… 58940a00-60.. 61163b30-60.. 995 x … X X X X 6e1ac030-60… ab36fa10-60.. X <some blob>
  39. 39. Анти-паттерн #2: очереди и очередеподобные нагрузки activity | source | elapsed -------------------------------------------+-----------+-------- execute_cql3_query | 127.0.0.3 | 0 Message received from /127.0.0.3 | 127.0.0.1 | 42 Sending message to /127.0.0.1 | 127.0.0.3 | 718 Executing single-partition query on queues | 127.0.0.1 | 145 Acquiring sstable references | 127.0.0.1 | 158 Merging memtable contents | 127.0.0.1 | 189 Merging data from memtables and 0 sstables | 127.0.0.1 | 235 Read 1 live and 9999 tombstoned cells | 127.0.0.1 | 251102 Enqueuing response to /127.0.0.3 | 127.0.0.1 | 252976 Sending message to /127.0.0.3 | 127.0.0.1 | 253052 Message received from /127.0.0.1 | 127.0.0.3 | 324314 Processing response from /127.0.0.1 | 127.0.0.3 | 324535 Request complete | 127.0.0.3 | 324812
  40. 40. Анти-паттерн #2: очереди и очередеподобные нагрузки • partition сканируется до тех пор пока LIMIT неудалённых ячеек не будет собран, или • пока не закончились ячейки в partition, или • пока не встретится ячейка вне заданных границ (если указано в where)
  41. 41. Анти-паттерн #2: очереди и очередеподобные нагрузки —— зная последний удалённый элемент SELECT enqueued_at, payload FROM queues WHERE name = 'queue-1' AND enqueued_at > 6e1ac030-60b9-11e3-949a-0800200c9a66 LIMIT 1;
  42. 42. Анти-паттерн #2: очереди и очередеподобные нагрузки queue-1 51c220e0-60… 58940a00-60.. 61163b30-60.. 995 x … X X X X 6e1ac030-60… ab36fa10-60.. X <some blob>
  43. 43. Анти-паттерн #2: очереди и очередеподобные нагрузки activity | source | elapsed -------------------------------------------+-----------+-------- execute_cql3_query | 127.0.0.3 | 0 Sending message to /127.0.0.1 | 127.0.0.3 | 965 Message received from /127.0.0.3 | 127.0.0.1 | 34 Executing single-partition query on queues | 127.0.0.1 | 339 Acquiring sstable references | 127.0.0.1 | 355 Merging memtable contents | 127.0.0.1 | 461 Partition index lookup over for sstable 3 | 127.0.0.1 | 1122 Merging data from memtables and 1 sstables | 127.0.0.1 | 2268 Read 1 live and 0 tombstoned cells | 127.0.0.1 | 4404 Message received from /127.0.0.1 | 127.0.0.3 | 6109 Enqueuing response to /127.0.0.3 | 127.0.0.1 | 4492 Sending message to /127.0.0.3 | 127.0.0.1 | 4606 Processing response from /127.0.0.1 | 127.0.0.3 | 6608 Request complete | 127.0.0.3 | 6901
  44. 44. Анти-паттерн #2: очереди и очередеподобные нагрузки • partitioning по дате • хинты для чтения • то же относится к TTL • худший сценарий - death by OOM • https://issues.apache.org/jira/browse/CASSANDRA-6117 (Avoid death-by-tombstone by default) 10k warning, 50k error • http://www.datastax.com/dev/blog/cassandra-anti-patternsqueues-and-queue-like-datasets
  45. 45. Что дальше - вебинары по моделированию • http://youtu.be/px6U2n74q3g - The Data Model is Dead, Long Live the Data Model • http://youtu.be/qphhxujn5Es - Become a Super Modeler • http://youtu.be/T_WRC_GjRd0 - The World's Next Top Data Model • http://youtu.be/UP74jC1kM3w - Understanding How CQL3 Maps to Cassandra's Internal Data Structure
  46. 46. Вопросы?

×