ГлавнаяРегистрацияВходВ закладки

Главная » Статьи » PHP, MySQL » MySQL
Как FriendFeed использует MySQL для хранения данных без схемы
Автор: admin  Раздел: MySQL
Мы употребляем MySQL для сохранения других этих . Наша база данных вырастает совместно с числом юзеров. Теперь около нас больше 250 миллионов записей, сие записи пользователей (post'ы), комменты, балла («likes»)
В течении всего времени как росла информационная база, мы время от времени сталкивались с неувязками масштабируемости. Мы улаживали проблемки типовыми маршрутами: slave-сервера, применяемые лишь для чтения, memcache для увеличения пропускной таланты чтения и секционирование для роста пропускной способности записи. Хотя, по мере роста, израсходованные способы масштабируемости дали почву затруднению прибавлению новоиспеченной функциональности.
А именно, модифицирование схемы базы данных либо прибавление индексов к имеющийся 10-20 миллионов записей приносили к целой блокировке сервера на немного часов. Устранение престарелых индексов спрашивало времени, но не устранение било по производительности, поскольку база данных возобновляла употреблять их на любом INSERT. Есть сложноватые процедуры с помощью каких возможно обойти эти проблемы (пример творение новоиспеченного индекса на slave-сервере, и следующий размен местами master'a и slave), хотя данные процедуры так тяжкие и страшные, что они конечно потеряли нас хотения подливать нововведение, требующее изменение схемы или индекса. А так как наши базы сильно распределены, реляционные багаж MySQL как пример JOIN ни в какое время не трудили нам. Тогда мы разрешили порыскать заключение заморочек, возлежащее за пределами реляционных баз данных.
Есть много планов, призванных исправить ситуацию хранения данных с упругой схемой и построением индексов на лету (например
). Однако, по-видимому буква какой-то из них не употребляется большими веб-сайтами. В исследованиях об которых мы декламировали и изгоняли самочки, ни один из планов не показал себе прочным, довольно спелым для наших целей (см.
, пример). А все это время MySQL трудил. Он не калечил исходные. Репликация трудила. Мы теснее в довольной мере соображали все его тесные места. Нам влюбился MySQL только как помещение, вне реляционных шаблонов.
Все взвесив, мы решили до создать систему хранения данных в отсутствие схемы поверх MySQL, заместо применения абсолютно нового решения. В этой статье я попробую обрисовать главные подробности системы. Тоже самое нам интересно как иные сайтики решили эти проблемы. Ну и мы мыслим, что наша служба станет может быть полезна иным разрабам.
Наша база данных бережёт данные без схемы как большого количества полей (например JSON объекты или словари (dictionary) в Python). Один-единственное непременное поле — это id, 16-байтный UUID. Прочее обязано быть не важно для нашего хранилища, именно за данным оно формируется. Мы «переменяем» схему, элементарно добавив новоиспеченные поля.
Станем регистрировать данные записей и хранить индекс в раздельной MySQL таблице. Если мы пожелаем проиндексировать 3 поля любой записи, мы приобретаем три MySQL таблицы — по одной на любой индекс. Если более не нуждаемся в индексе, мы прекращаем строчить в таблицу индекса, и можем выслать таблицу если захотеть. Если спрашивается новоиспеченный индекс, мы основываем новоиспеченную таблицу MySQL для него и бросим асинхроичный процесс для наполнения индекса, не прерывая прочие задачки.
Как итог, мы приобретаем огромное количество таблиц нежели было ранее, однако добавление и удаление индексов упростилось. Мы обрезаны улучшали процесс для заполнения индексов (какой мы сказали «The Cleaner»), так дабы он основывал индексы скоро без нарушения работы веб-сайта. Теперь же мы можем добавлять новые качества и индексировать за дни, а не недельки. Так же теперь же не требуется обмен мастер на slave или другие страшные операции.
| ,
,
,
body MEDIUMBLOB,
(id),
Столбец added_id необходим лишь потому, что InnoDB физиологически хранит данные в порядке основного ключа. AUTO_INCREMENT Шлюзы обеспечивают, что новые записи записаны на винчестер вслед за престарелыми, что подсобляет и чтению, и записи (доступ к новоиспеченным записи как правило выходит почаще, чем к престарелым записям, по этой причине странички FriendFeed отсортированы в возвратном хронологическом распорядке). Тело записи хранится как плотный (zlib) Python-
словарь.
Индексы сохраняются в раздельных таблицах. Для нового индекса мы создаём таблицу с свойствами, по каким мы желаем реализовывать розыск. Для образца, запись в FriendFeed смотрится приблизительно так:
| ,
,
,
"We just launched a new backend system for FriendFeed!& quot;
,
"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c"
,
,
,
Мы хотим проиндексировать по полю user_id для отражения абсолютно всех записей, что делал user. Наша индекс-таблица выглядит так:
| ,
,
(user_id, entity_id)
Наша книгохранилище автоматом делает индексы. Для старта нашего хранилища, какое сохраняет сходственные записи с индексом, изображенным выше, мы строчим (на Python):
| user_id_index = friendfeed.datastore.Index(
, properties=[
], shard_on=
datastore = friendfeed.datastore.DataStore(
"127.0.0.1:3306"
,
"127.0.0.1:3307"
],
: binascii .a2b_hex(
),
: binascii .a2b_hex(
),
: binascii .a2b_hex(
),
"We just launched a new backend system for FriendFeed!& quot;
,
"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c"
,
,
,
datastore.put(new_entity)
entity = datastore.get(
binascii .a2b_hex(
entity = user_id_index.get_all(datastore, user_id=
binascii .a2b_hex(
Index класс видит на поле user_id во всех записях и автоматически создаёт индекс в таблице index_user_id. Так как наша база данных секционирована (sharding), довод shard_on используется для определения в какой области будет держаться индекс (в данном случае entity[«user_id»] % num_shards)
Для исполнения запроса с использованием сделанного индекса используется предмет класса Index (см. user_id_index.get_all). Метод «хранилища» поделает «join» таблиц index_user_id и таблицы с записями, для начала пробегаясь по всем таблицам index_user_id на всех секторах базы данных для извлечения перечня ID записей и потом приобретает эти записи из таблицы entities.
Для творения нового индекса, например по атрибуту link, мы создадим таблицу:
| ,
,
(link, entity_id)
| user_id_index = friendfeed.datastore.Index(
, properties=[
], shard_on=
link_index = friendfeed.datastore.Index(
, properties=[
], shard_on=
datastore = friendfeed.datastore.DataStore(
"127.0.0.1:3306"
,
"127.0.0.1:3307"
],
indexes=[user_id_index, link_index])
./rundatastorecleaner.py --index=index_link
Из-за того что база данных сегментирована, индекс для точной записи смогут искаться на различных сегментах. Что случится, если процесс внезапно увенчается до того, как запишет все индексы по таблицам?
Наиболее гонористые FriendFeed инженеры почитали, что транзакции нужны в исходной ситуации. Хотя мы желали сберечь нашу с тобой систему как можно более несложной. Мы решили обессилить ограничения:
Индекс сможет вернуть неуместные записи.
В результате, мы создаём новую запись в последующем распорядке:
Храним запись в главную таблицу, воспользовавшись ACID гарантиями InnoDB (Atomicity, Consistency, Isolation, Durability).
Когда мы читаем из индекс таблиц, мы видим что итог может быть неточен (т. е. результат может иметь излишние объекты, если запись не была окончена на шаге 2). Дабы удостовериться, что мы отдаваем верные записи, мы вторично фильтруем результат, приобретенный из индекс таблиц:
Читаем entity_id из всех индекс-таблиц, участвующих в запросе
Фильтруем (в Python) все записи, что не то что надо по аспектам запроса.
Для исправления индексов сотворен процесс «Cleaner» (чистильщик), какой упоминался раньше. Он бросится по таблице записей, записывая упущенные индексы, устраняя престарелые и поправляя не неизменные. Он затевает с новоиспеченных записей, практически все неточности поправляются весьма быстро (в течение многих сек.).
Мы чуть-чуть оптимизировали наши основные ключи в новой системе и довольны итогом. Ниже диаграмма задержек пред эффективностью страницы FriendFeed в последнее время (мы швырнули новый backend несколько суток обратно):
В частности, заминка нашей системы стабильна, и на протяжении вершин в середине дня. Ниже диаграмма за последние 24 hours:
С новой системой стало на множество легче трудиться. Мы уже видоизменили индексы несколько раз на пролетарой системе, и теперь затеваем миграцию наших основных таблиц, чтобы передвигаться далее.
, by Bret Taylor • February 27, 2009
Так же советую к чтению на известном
Просмотров: 1400
Дата: 2011-09-03 23:29:25
Комментариев: 0
Источник: