среда, 28 июля 2010 г.

Ticket сервер на flickr.com

Просматривая различные решения создания уникальных записей натолкнулся на интересную статью на блоге разработчиков Flickr. В данной статье достаточно интересно освящена проблема генерации уникальных ключей для случая использования нескольких серверов баз данных.

Хоть Келлан и упомянул, что ticket серверы не являются чем-то особенно интересным, но учитывая тот факт, что они являются основными для генерации глобальных уникальных ключей, думаю узнать как они работают будет все-таки очень интересно.

Базовой технологией масштабирования баз данных Flickr является шардинг. Вместо хранения данных в одной большой базе данных, данные разделяются между серверами. Соответственно распределяется между ними и нагрузка. В данной ситуации появляется необходимость, чтобы первичные ключи были уникальны глобально для всего кластера во избежании коллизий ключей.

Можно задать вполне резонный вопрос: а почему бы не использовать GUID для этих целей? Основная причина заключается в том, что GIUD достаточно большой по размеру и индексация его в MySQL не самое лучшее решение. Размер ключа играет ключевое значение в больших системах хранения данных. Если нет возможности хранить индекс в памяти, соответственно возможности сохранения и увеличения быстродействие базы данных существенно снижаются.

Одно из самых эффективных методов генерации уникальных ключей - это авто инкремент первичных ключей, но проблема в том, что MySQL не обеспечивает уникальности ключа для физически и логически разнесенных баз данных. Основная идея решения данного вопроса, которое нашло применение в Flickr заключается в использовании централизованного сервера, обеспечивающего и контролирующего авто инкремент значений первичных ключей для всех баз данных (фотографий, комментариев, групп, пользователей, тегов и т.д.)

Рассмотрим детали решения. В MySQL есть нестандартное расширение ANSI SQL REPLACE INTO. Оно позволяет автоматически обновить строку в таблице базы данных и получить инкремент первичного ключа:
REPLACE INTO ticket (stub) VALUES (‘a’);
RETURN LAST_INSERT_ID();

Flickr ticket сервер представляет собой выделенный сервер базы данных, в котором хранятся таблицы для генерации ключей
CREATE TABLE `tickets` (
`id` bigint unsigned NOT NULL auto_increment,
`stub` char(1) NOT NULL default '',
PRIMARY KEY  (`id`),
UNIQUE KEY `stub` (`stub`)
) ENGINE=MyISAM;

SQL запрос SELECT * FROM tickets; возвращает одну единственную строку
mysql> select * from  tickets;
+--------+------+
| id     | stub |
+--------+------+
| 325688 | a    |
+--------+------+

Для упрощения генерации нового ключа можно воспользоваться следующей функцией
CREATE FUNCTION ticket() RETURNS BIGINT()
BEGIN
REPLACE INTO ticket (stub) VALUES (‘a’);
RETURN LAST_INSERT_ID();
END

после этого получение ключа будет выглядеть
mysql> SELECT ticket();
+----------+
| ticket() |
+----------+
|   325689 |
+----------+

Конечно же наличие такого сервера приводит к появлению single point of failure. Для того чтобы избежать нарушение функционирования работы системы в целом в случае выхода ticket сервера, используется несколько ticket серверов. Для того, чтобы не возникало коллизий ключей, их пространство разделяют на части. Для случая Flickr, ticket серверов два, следовательно и пространство разделено на две части, один сервер генерирует четные ключи, другой нечетные.
TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

Используя балансировку round robin между ticket серверами можно добиться разделения нагрузки и обеспечить отказоустойчивость системы в случае выхода из строя одного из серверов.

В качестве примера можно воспользоваться простым HTTP ticket сервером, который возвращает значение ticket в JSON формате

Зависимости: python, MySQLdb, asynchttpsrv
Запустить сервер: python ticketserver.py
Выполнить запрос на получение нового ключа: curl http://localhost:8080/
Пример возвращаемого результата: {"ticket":"62395"}

0 комментариев:

Отправить комментарий