NoSQL - подходы к решению типичных задач

Врядли сегодня найдется тот, кто еще не слышал про key-value базы данных, или, как их называют в народе, NoSQL. Те, кто успел попробовать продукты вроде Redis, MemcacheDB и других, обнаружили некторые сложности при решении привычных задач - выборка списков и поиск.
В этой статье мы рассмотрим принципы решения типичных задач в key-value базах данных.
Основная сложность
Восновном, все key-value базы данных представляют из себя распределенные хеш-таблицы. Это самая сильная их сторона, т.к. предоставляет высокую производительность и крайне упрощает масштабирование. Тем не менее, это свойство всплывает на поверхность, когда Вам необходимо работать со списками. В MySQL следующие задачи решаются в два счета:
- Выбрать 10 последних пользователей
- Выбрать Самые популярные посты в блоге
- Найти продукты, для которых оставлено более 3х комментариев
- Полнотекстовый поиск
- Поиск по любому свойству (найти пользователя, email которого такой-то)
Но в key-value подобные задачи вызывают удивление и большие трудности. В двух словах, key-value базы данных совсем не предназначены для таких задач. Но это только на первый взгляд, ведь формулировать задачи можно по-разному. Стратегия решения будет зависеть от конкретной ситуации.
И так, по порядку:
Поиск по ключу
Существует класс задач, когда Вам необходимо делать выборку одного объекта не по первичному, а вторичному ключу (например, поиск пользователя по email’у, поиск автора по никнейму т.п.). Принцип решения этой задачи следующий: после создания нового объекта, Вам необходимо создать ссылки на его первичный ключ для всех его свойств, по которым придется делать выборку. Например мы создаем пользователя:
user_134: {
name: Den,
email: golotyuk@gmail.com
}
И дублируем ключ email:
user_email_golotyuk@gmail.com: {
id: 134
}
Теперь, Вы сможете сделать выборку данных пользователя по его почтовому адресу в два этапа.
Использование РСУБД
Если Вам нужен MySQL - используйте его!
Существует класс задач, для которых можно и нужно использовать РСУБД, такие как MySQL и Postgres. Если Вам необходимо делать выборки списков с фильтрами и сортировками, то Вам следует использовать более подходящую для этого РСУБД. Хороший пример - это посты в блоге. (выборки, которые скорее всего нужно будет делать - самые новые, самые популярные, самые комментируемые записи и т.п.).
Подход довольно простой, но на практике очень часто задачи сильно переплетены, и в итоге Вы можете прийти к тому, что РСУБД возьмет все задачи обратно на себя. В этом случае следует подумать о гибридном решении:
Гибридное решение - дополнительная РСУБД
Это решение представляет из себя смесь key-value и РСУБД. Все данные Вы храните в key-value, но те свойства, по которым понадобиться делать агрегатные выборки дублируете в РСУБД (с указателем на ключ). Т.о. Вы сохраните производительность key-value базы данных и сможете решить свою задачу. Сложность тут заключается в том, что Вам придется следить за синхронизацией данных в обоих базах данных, что может сильно усложнить логику приложения.
Полнотекстовый поиск и выборки с задержками
Одна из частых задач - полнотекстовый поиск. Существуют отличные решения - Sphinxsearch, Solr и другие. К сожалению, ни одно из них еще не поддерживает индексацию key-value баз данных, но зато все предоставляют интерфейсы для ручной индексации. Вам нужно будет только описать реализацию для конкретного набора данных (например, с помощью XmlPipe в Sphinx’e).
Наряду с полнотекстовым поиском часто стоят и задачи, связанные с агрегатными выборками, которые не критичны к текущему состоянию данных. Например, когда Вы строите рейтинг пользователей (или постов в блоге, или продуктов в магазине или …), Вы можете спокойно использовать данные по состоянию на прошлый час(день/неделю/…). В качестве решения Вы можете использовать полнотекстовые серверы по их непрямому назначению. Многие из них поддерживают разнообразные фильтры и сортировки, которых достаточно для решения 90% задач подобного рода.
Распределенные выборки
Некоторые key-value базы данных позволяют делать выборки списков встроенными средствами. Масштабирование этого решения будет выглядеть как запросы ко всем узлам сети и агрегирование результатов на бекенде. В крупных масштабах это может быть очень затратная операция, поэтому это решени слеудет оптимизировать. Во многих случаях Вы можете обойтись без запросов ко всем узлам, а сделать выборку только на одном из них (например - выбрать случайное фото).
Что еще сюда можно добавить?


Спасибо за статью.
В Redis’е можно делать аналог SQL’ных индексов через списки и упорядоченные множества. Как заявляет автор, они эффективно работают и на миллионах элементов.
Вам случаем не доводилось тестировать Redis-списки на больших размерах?
в начале статьи Вы говорите о списках, но в статье, хоть и приведены решения некоторых задач, так и не сказано, как решить проблему списков средствами nosql.
лично я не имею, на данный момент, значимого опыта по nosql.
но пердставляю такие решения этой проблемы(ТЕ проблемы списков):
1. хранить список в одном из элементов:
user_NNN:
{
name: Vasya,
incoming_msg: [ 'text_1', 'text_2' ]
}
решение чревато проблемами с одновременным добавлением в список,
ограничением на длинну списка.
2. поиск по префиксу, например
user_NNN_incoming_SOMEUNIQID1: { … }
user_NNN_incoming_SOMEUNIQID2: { … }
…
чтобы полчить все сообщения, достаточно искать по префиксу “user_NNN_incoming_”.
но работает только для btree, для хеш-таблиц - не работает.
3. элементы с одинаковыми именами.
ну вродибы все понятно.
это решение имеет ограничение на количество таких элементов.
4.1. первое-улучшенное
хранить список по частям в отдельном ключе
user_NNN_incoming_K1: { … }
user_NNN_incoming_K2: { … }
…
ограничить количество ссыллок в каждом из ключей,
при чтении - читать следующий (1,2,3…) пока не появится ошибка “нет ключа”
4.2. первое-улучшенное вариант два
ввести мультиверсионность(MVCC). при конфликте версий - объединить.
ограничение на размер остается.
все это выглядит както костыльно-неуниверсально.
так как же всетаки быть?
как эти проблемы решены в том-же гугле?
понятно, что получив список ключей можно сделать с ним все что угодно (map-reduce и Ко)
но как получить такой список?
noSQL БД иногда характеризуют как “ненадежные”, “плохо реплецируемые”, “молодые(нестабильные)” поэтому использовать их чучуть страшно
(хотя в одномиз своих проектов я отказался от mysql в пользу redis и вот уже 6 месяцев ни разу не пожалел … хотя у меня не большая БД (~200Мб)).
Читал я еще про возможность использовать mysql в как key-value БД - говорят она показывает неплохие результаты по производительности по primary key (у меня получилось провести тест на локальном сервере SELECT/INSERT ~5K/сек, redis на этой же машине показал GET/SET ~20K/сек, такой же тест на похожих данных с обычной реляционной моделью на mysql SELECT/INSERT ~400/сек) так что отбрасывать mysql можно не сразу
Думаю, кто заботится о “надежности”, “реплецируемости”, “стабильности” может попробовать просто изменить логику работы с mysql.
Хотелось бы узнать мнение авто на этот счет, т.е. использования mysql (или др. РСУБД) в качестве key-value store, есть ли в этом смысл, и какие применяют техники?
@vasa_c
Нет, буду благодарен, если поделитесь опытом
@tx2
Спасибо!
Все варинаты, которые Вы описали, так или иначе подходят под разного рода задачи. Внутренный список (вложенный массив) подходит под задачи, когда Вам нужно организовать много небольших списков (например, личные сообщения). Список по префиксу - только если поддерживается b+tree индекс (чаще всего поддерживается). Не убирайте RDBMS из рассматриваемых вариантов. Например, хранить список в MysQL куда лучше и надежнее чем делать то, что Вы описали в пункте 4 (пытаться поверх функциональности БД реализовать механизм выборок руками).
@boom
По поводу, того что многие считают key-value “нехорошими”. Многие просто не любят думать, а обвиняют в этом технологии. Конечно, если пытаться молотком просверлить дыру, то он как бы тоже “плохой” и “неудобный”
По поводу MySQL, как key-value хранилища. Зачем? Вы же сами проверяли и сравнивали различие в производительности. Если Вам нужна key-value БД, используйте ее. Почти все они поддерживают репликацию, бекапы, многие позволяют настраивать fail-over режим. Поэтому вопрос доступности и надежности - это вопрос правильной настройки и управления. А вот использовать технологию, которая для этого не создана, куда хуже и опаснее.
Насколько я понимаю, NoSQL это более широкое понятие, нежели key-value.
http://www.jprogers.info/2009/12/nosql.html
очень интересный проект (лично для меня) уже неделю жду нового поста)