Конечно же это право разработчиков, и они поступают так, как считают будет правильнее и лучше для них. В любом случае, я очень благодарен им хотя бы за то, что они выложили в свободный доступ описание своих решений:
- Google BigTable, BigTable: A Distributed Storage System for Structured Data
- Amazon Dynamo, Dynamo: Amazon’s Highly Available Key-Value Store
- FriendFeed, как они используют MySQL для schema-less баз данных. Оригинал статьи. Ее перевод на Хабре.
Я остановлюсь на последнем решении, как наиболее простом и лучше описанном, не требующем какого-либо специального окружения. Для его воплощения в жизнь подойдет любой компьютер на базе Windows, Linux, MacOS с установленным python и MySQL.
Хочу сразу заметить, что приведенный ниже пример несколько упрощен от оригинала. Основная идея - это разобраться, как это работает, а не повторить один в один реализацию, описанную в статье. Следовательно, я допустил ряд вольностей со своей стороны по упрощению схемы хранения данных, изменению полей. В данном посте не рассматриваются вопросы индексации данных, а также вопросы масштабирования. Очень надеюсь, что данный пост станет первым в серии постов, посвященных применению MySQL для хранения key-value баз данных. Как говорится, от простого к более сложному. В любом случае, ссылки на оригиналы статей вы можете найти выше и вернуться к ним в случае необходимости.
Небольшая вводная часть. Хранилище данных позволяет хранить наборы свойств объекта/сущности, которые представляют собой либо JSON объекты, либо Python словари. Единственным обязательным полем является поле id - 16 байтовый UUID.
Объекты хранятся в таблице
CREATE TABLE entity ( __key__ INT NOT NULL AUTO_INCREMENT PRIMARY KEY, id BINARY(16) NOT NULL, body MEDIUMBLOB, UNIQUE KEY (id));Поле __key__ используется для выборки данных, о котором я расскажу несколько позже. Про id уже упоминалось - это 16 байтовый уникальный id (uuid). Для его формирования используется стандартная функция mysql uuid() с единственным отличием, что расположение полей uuid изменено для упрощения индексации, выполнена реверсия полей:
стандартный вывод uuid(): 694518e3-a2a3-11df-957f-d4c66194c009
для id используется: d4c66194c009957f11dfa2a3694518e3
О причине данного решения я уже вскользь упоминал в предыдущем посте MySQL: как добавить микро/миллисекунды в current_timestmap. В MySQL для генерации uuid используется функция uuid версии 1, т.е. комбинация MAC адреса и значение 100 наносекундных интервалов с 00:00:00.00 15 Октября 1582 года. В стандартном выводе uuid() первые поля используются для хранения интервалов времени, а последнее для MAC адреса. Следовательно, первые поля чаще изменяются и их имеет смысл переставить в конец последовательности.
Поле body используется для хранения свойств объекта. Перед сохранением выполняется сериализация данных cPickle и сжатие zlib.
Исходный код можно найти на sources-ownport
Предположу, что база данных MySQL уже создана и перейду сразу к работе с данными.
ds = Datastore('mysql://entitydb:entitydb@localhost/entitydb')
if not ds.is_table_exist('entity01'):
ds.create_entity('entity01')Код, представленный выше, показывает, каким образом можно подключиться к базе данных. Формат URI для подключения к базе данных: mysql://<username>:<password>@<host or IP address>[:port]/<database>
Объект Datastore использует три метода для работы с данными: get, put, delete.
Метод get() используется для выборки объекта по его id или выборки набора данных.
Если id объекта известно, то выборка осуществляется get(<entity>, <id>).
Если необходимо сделать выборку для всех объектов из базы данных, можно воспользоваться полем __key__. Например:
data = ds.get('entity01')
while len(data) > 0:
print data
data = ds.get('entity01', __key__=data[-1][0]+1)Метод put() используется для добавления нового или обновления данных существующего объекта.
Добавление нового объекта
entity = {
'title': 'Test entity',
'description': 'Test entity description',
}
ds.put('entity01', entity)Для обновления данных существующего объекта достаточно указать его id
ds.put('entity01', entity, id)Метод delete() используется для удаления объекта.
ds.delete('entity01', id)Конечно же данное решение еще достаточно сырое, чтобы сразу использовать его в продуктиве. Есть не решенные вопросы:
- индексация данных
- полнотекстовый поиск
- разрешение конфликтов в случае одновременного сохранения данных
- версионность данных
Но как площадка для экспериментов на мой взгляд очень интересна.



Пример реализации NoSQL With MySQL in Ruby http://jamesgolick.com/2009/12/16/introducing-friendly-nosql-with-mysql-in-ruby.html
ОтветитьУдалить