UDF в MySQL, json или то, как забрать обновления данных из БД 3

Posted by Андрей on Октябрь 30, 2010

Иногда необходимо забирать данные из БД MySQL в режиме реального времени во внешнюю систему, которая никак не связана с MySQL. Существует множество возможных решений, например, можно реализовать «слейва» MySQL, который бы хранил полученные данные во внешней системе.

Одно из возможных решений — сделать «выгрузку» данных из MySQL с помощью UDF (User Defined Functions) и триггеров. Для этого необходимо поставить слейв MySQL, на котором уже повесить на интересующие таблицы триггеры, которые с помощью UDF будут выгружать поток изменений таблиц во внешнюю систему. Слейв необходим, т.к. если триггеры поставить на мастере, то в случае отката транзакции действия, уже сделанные триггерами, откатить не получится, а на слейв попадают только зафиксированные транзакции. Второе,чтобы триггеры работали на слейве, тип репликации должен быть выставлен на STATEMENT-based.

Порывшись в одном интересном архиве UDF для MySQL я нашел несколько функций, которые мне подошли:

  • преобразование строки MySQL в json;
  • интерфейс с memcached.

В результате получился следующий план действий: данные модифицируются на мастере, реплицируются на слейв с помощью STATEMENT-репликации. В процессе репликации на слейве запускаются триггеры, формируют с помощью UDF пакет обновлений в JSON, и передают его во внешнюю очередь (memcacheq) по memcached-протоколу. Конечно, это не единственный возможный способ, но все UDF уже были почти готовы. После доделывания напильником UDF получился вполне стабильно работающий вариант.

Триггеры выглядят примерно следующим образом:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE FUNCTION kick_photos (row_id INT) RETURNS INT 
BEGIN 
SELECT memc_set('queue_db', (json_object('insert' AS action, 'photos' AS table_name, photos.id AS id, json_members('data', json_object(photos.user_id AS `user_id`,photos.width AS `width`,photos.created_at AS `created_at`,photos.filename AS `filename`,photos.parent_id AS `parent_id`,photos.content_type AS `content_type`,photos.height AS `height`,photos.thumbnail AS `thumbnail`,photos.size AS `size`))))) INTO @dummy FROM photos WHERE id = row_id; 
RETURN @dummy; 
END
 
CREATE TRIGGER photos_INSERT AFTER INSERT ON photos FOR EACH ROW 
SET @dummy = memc_set('queue_db', (json_object('insert' AS action, 'photos' AS table_name, NEW.id AS id, json_members('data', json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`)))));
 
CREATE TRIGGER photos_DELETE BEFORE DELETE ON photos FOR EACH ROW 
SET @dummy = memc_set('queue_db', (json_object('delete' AS action, 'photos' AS table_name, OLD.id AS id, json_members('data', json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`)))));
 
CREATE TRIGGER photos_UPDATE AFTER UPDATE ON photos FOR EACH ROW 
BEGIN 
IF json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`) <> json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`) THEN 
  SET @dummy = memc_set('queue_db', (json_object('update' AS action, 'photos' AS table_name, OLD.id AS id, json_members('new', json_object(NEW.user_id AS `user_id`,NEW.parent_id AS `parent_id`,NEW.created_at AS `created_at`,NEW.filename AS `filename`,NEW.width AS `width`,NEW.content_type AS `content_type`,NEW.height AS `height`,NEW.thumbnail AS `thumbnail`,NEW.size AS `size`)), json_members('old', json_object(OLD.user_id AS `user_id`,OLD.parent_id AS `parent_id`,OLD.created_at AS `created_at`,OLD.filename AS `filename`,OLD.width AS `width`,OLD.content_type AS `content_type`,OLD.height AS `height`,OLD.thumbnail AS `thumbnail`,OLD.size AS `size`))))); 
END IF; 
END;

Комментарии:

  • функция kick_photos позволяет скопировать строчку таблицы в очередь как пакет обновления типа «вставка», может использоваться для начального наполнения внешней системы;
  • триггеры на удаление и вставку просто формируют соответствующие пакеты;
  • триггер на обновление проверяет, действительно ли в пакете произошли изменения (например, мы можем использовать не все поля в пакете);
  • необходимо учесть, что работе FOREIGN KEY CONSTRAINT триггеры не вызываются (очередной прикол MySQL), т.е., например, при если при выполнении запроса на удаление из таблицы A будут по FOREIGN KEY удалятся записи из таблицы B, то в триггере на удаление из A необходимо отработать этот случай, т.к. триггеры на таблице B не будут вызваны.

Код UDF доступен на github, это — «подпиленный» код из репозитория UDF или собственные разработки:

Структуры данных в memcached/MemcacheDB 4

Posted by Андрей on Январь 21, 2009

Достаточно часто нам приходится хранить данные в memcached или MemcacheDB. Это могут быть относительно простые данные, например, закэшированные выборки из базы данных, а иногда необходимо хранить и обрабатывать более сложные структуры данных, которые обновляются одновременно из нескольких процессов, обеспечивать быстрое чтение данных и т.п. Реализация таких структур данных уже не укладывается в комбинацию команд memcached get/set. В данной статье будут описаны способы хранения некоторых структур данных в memcached с примерами кода и описанием основных идей.

Memcached и MemcacheDB в данной статье рассматриваются вместе, потому что имеют общий интерфейс доступа и логика работы большей части структур данных будет одинаковой, далее будем называть их просто «memcached». Зачем нам нужно хранить структуры данных в memcached? Чаще всего для распределенного доступа к данным из разных процессов, с разных серверов и т.п. А иногда для решения задачи хранения данных достаточно интерфейса, предоставляемого MemcacheDB, и необходимость в использовании СУБД отпадает.

Иногда проект разрабатывается изначально для нераспределенного случая (работа в рамках одного сервера), однако предполагая будущую необходимость масштабирования, лучше использовать сразу такие алгоритмы и структуры данных, которые могут обеспечить легкое масштабирование. Например, даже если данные будут храниться просто в памяти процесса, но интерфейс к доступа к ним повторяет семантику memcached, то при переходе к распределенной и масштабируемой архитектуре достаточно будет заменить обращения к внутреннему хранилищу на обращения к серверу (или кластеру серверов) memcached.

Continue reading…

Memcached: статистика, отладка и RPC 2

Posted by Андрей on Октябрь 31, 2008

Серия постов про «Web, кэширование и memcached» продолжается. Начало здесь: 1, 2, 3, 4 и 5. В этих постах мы поговорили о memcached, его архитектуре, возможном применении, выборе ключа кэширования, кластеризации, атомарных операциях и реализации счетчиков в memcached, а также о проблеме одновременного перестроения кэшей и тэгировании кэшей.

Сегодняшний пост завершает эту серию, в нём обзорно мы поговорим о технических «мелочах»:

  • анализ статистики memcached;
  • отладка memcached;
  • «RPC» с помощью memcached.

Полный текст всех разделов в виде одной большой PDF-ки можно скачать и посмотреть здесь (в разделе «Материалы»).

Continue reading…

Сброс группы кэшей и тэгирование в memcached 7

Posted by Андрей on Октябрь 29, 2008

Серия постов про «Web, кэширование и memcached» продолжается. Начало здесь: 1, 2, 3 и 4. В этих постах мы поговорили о memcached, его архитектуре, возможном применении, выборе ключа кэширования, кластеризации, атомарных операциях и реализации счетчиков в memcached, а также о проблеме одновременного перестроения кэшей.

Сегодня мы поговорим о тэгировании кэшей и о возможности сброса сразу группы кэшей в memcached.

Тэгирование

Последний, шестой пост, будет посвящен различным техническим вопросам работы с memcached: анализу статистике, отладке и т.п.

Continue reading…

Проблема одновременного перестроения кэшей 7

Posted by Андрей on Октябрь 28, 2008

Серия постов про «Web, кэширование и memcached» продолжается. Начало здесь: 1, 2 и 3. В этих постах мы поговорили о memcached, его архитектуре, возможном применении, выборе ключа кэширования, кластеризации, атомарных операциях и реализации счетчиков в memcached.

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

Перегрузка backend

Следующий пост будет посвящен тэгированию кэшей.

Continue reading…

Атомарность операций и счетчики в memcached 8

Posted by Андрей on Октябрь 24, 2008

Серия постов про «Web, кэширование и memcached» продолжается. В первом и втором постах мы поговорили о memcached, его архитектуре, возможном применении, выборе ключа кэширования и кластеризации memcached.

Сегодня речь пойдет о:

  • атомарных операциях в memcached;
  • реализации счетчиков просмотров и онлайнеров.

Атом

Следующий пост будет посвящен проблеме одновременного перестроения кэшей.

Continue reading…

Кластеризация memcached и выбор ключа кэширования 4

Posted by Андрей on Октябрь 21, 2008

Серия постов про «Web, кэширование и memcached» продолжается. В первом посте мы поговорили о memcached, его архитектуре и возможном применении.

Сегодня речь пойдет о:

  • выборе ключа кэширования;
  • кластеризации memcached и алгоритмах распределения ключей.

Ключи

Следующий пост будет посвящен атомарности операций и счетчикам в memcached.

Continue reading…

Кэширование и memcached 14

Posted by Андрей on Октябрь 16, 2008

Этим постом хочу открыть небольшую серию постов по материалам доклада на HighLoad++-2008. Впоследствии весь текст будет опубликован в виде одной большой PDF-ки.

Введение

Для начала, о названии серии постов: посты будут и о кэшировании в Web’е (в высоконагруженных Web-проектах), и о применении memcached для кэширования, и о других применениях memcached в Web-проектах. То есть все три составляющие названия в различных комбинациях будут освещены в этой серии постов.

Continue reading…

Web, кэширование и memcached (выступление на HighLoad++ 2008) 11

Posted by Андрей on Октябрь 08, 2008

Итак, HighLoad++ состоялся. Если говорить кратко, конференция мне понравилась. Ниже мои личные впечатления о конференции, краткие тезисы доклада и презентация.

Текст доклада дописываю, есть мечта к концу недели это доделать (сейчас готова ровно половина). Тогда же текст опубликую, возможно в серии отдельных постов и в виде одной большой PDF-ки тут.

Мои впечатления о конференции

Итак, мне понравилось. Интересные доклады — много интересных докладов. Жалко, что не было Яндекса — они делают хорошие доклады. В первый день была проблема поесть и попить, но ко второму дню ситуация как-то улучшилась. Народу чуть-чуть больше, чем хотелось бы (иногда в аудиторию к докладчику не пролезть через тела тех, которые устроили «пробку» на входе в зал). Но интересные или очень интересные доклады, много обсуждений, новых идей. Встретил старых знакомых, это всегда приятно :)

Огранизационно всё было четко, понравился дизайн мелочей — бейджиков, шаблона презентаций и прочего — просто и со вкусом. В общем и целом — так держать, Олег ;)

Continue reading…

Анонс: выступление на HighLoad++ (2008) 9

Posted by Андрей on Сентябрь 30, 2008

6-7 октября в Москве пройдет конференция HighLoad++. На этой конференции я представлю доклад на тему «Web, кеширование и memcached» (текущая программа конференции).

Краткие тезисы доклада привожу ниже:

Цель доклада – рассказать о проблемах кеширования в распределенных высоконагруженных проектах и о возможных путях решения этой проблемы. Предполагаемый уровень подготовки аудитории – начинающий++.

UPD: доклад будет во второй день, 7 октября, 17:20-18:10, второй зал.

Continue reading…