Статьи

Бесплатная производительность с табличными типами MySQL

На прошлой неделе SitePoint выпустил свой второй Twitaway , раздав бесплатный PDF-файл «Принципов успешного фриланса» всем, кто достаточно любопытен , чтобы подписаться на нас в Twitter . Как и ожидалось, сайт, который мы создали для читателей, чтобы заявить, что PDF был забит трафиком в первые несколько часов раздачи, и сервер MySQL за кулисами быстро остановился.

Во-первых, немного предыстории: во время нашего первого Twitaway в 2008 году, Twitter HQ был достаточно любезен, чтобы ослабить ограничение, накладываемое на максимальное количество прямых сообщений Twitter (DM), которые он позволил бы нам отправлять за один день. , Чтобы распространять бесплатные PDF-файлы, мы просто написали PHP-скрипт, который бы просматривал наших подписчиков в Twitter каждые несколько минут, выявлял новых и отправлял им DM со ссылкой для скачивания.

На этот раз, когда мы сели планировать Twitaway 2009, было ясно, что наша работа будет более сложной. Намного менее охотно, чем раньше, штаб-квартира Twitter озвучила наши просьбы об уменьшении дневного лимита DM — они даже не сказали нам, каков был наш дневной лимит. Некоторое время мы не были уверены, как мы сможем доставить ссылку на скачивание в частном порядке нашим подписчикам в Twitter.

Решение, наконец, пришло от Мала, нового разработчика в команде здесь, в SitePoint HQ. Мы будем использовать новую поддержку OAuth в Твиттере, чтобы запросить временный доступ к вашей учетной записи в Твиттере, использовать его для проверки того, что вы действительно следите за нами, а затем просто отобразить ссылку для скачивания PDF в своем браузере. DM не требуется!

Конечно, написание PHP-приложения, поддерживающего этот рабочий процесс на основе OAuth, было намного сложнее, чем написание статической веб-страницы со ссылкой «следуйте за нами в Twitter». Мало того, что было бы труднее писать, но также было бы сложнее обрабатывать большие объемы трафика, попадающие в него.

Итак, мы были: только за полночь в день запуска Twitaway, и приложение перестало отвечать. Наши системы мониторинга дали понять, что, хотя наши PHP-серверы хорошо переносили шторм, сервер баз данных MySQL увеличил загрузку ЦП до 100%, засунул пальцы в уши и начал раскачиваться взад-вперед, повторяя «на-на-на» -п-на-я-не-слушать!»

Как оказалось, это был важный урок для нашей команды разработчиков о важности выбора подходящих типов таблиц MySQL. Читайте дальше о том, как мы решили проблему и почему мы должны были ее предсказать.

Выбор правильного типа стола

Когда мы вошли на заболоченный сервер MySQL, мы увидели (используя команду SHOW PROCESSLIST ), что подавляющее большинство запросов, которые он получал, относились к таблице базы данных, которую мы настроили для хранения данных сеанса PHP.

По умолчанию PHP хранит переменные сеанса ваших пользователей в файлах на жестком диске веб-сервера. В нашем случае, однако, у нас было несколько веб-серверов, которые должны были использовать одни и те же данные сеанса, поэтому мы настроили PHP для хранения своих сеансов на сервере базы данных MySQL.

К сожалению, нам не удалось указать тип таблицы при создании таблицы сеанса в базе данных:

CREATE TABLE sessions (
  sesskey VARCHAR(32) NOT NULL DEFAULT '' PRIMARY KEY,
  expiry INT(11),
  expireref VARCHAR(64),
  sessdata LONGTEXT
);

MySQL поддерживает ряд различных механизмов хранения . При создании таблицы MySQL по умолчанию будет использовать механизм хранения MyISAM для создания таблицы MyISAM. MyISAM — это относительно простой табличный тип, который отказывается от нескольких расширенных функций для повышения производительности. В первые дни MySQL тип таблицы MyISAM способствовал тому, что он стал одной из самых эффективных баз данных.

Эти характеристики, казалось бы, делают MyISAM естественным выбором для чего-то вроде таблицы данных сеанса. К сожалению, у MyISAM есть ахиллесова пята: блокировка на уровне таблицы . Чтобы записать изменение в таблицу MyISAM, MySQL должен заблокировать всю таблицу, не позволяя любому другому запросу читать таблицу до тех пор, пока изменение не будет завершено.

Когда тысячи и тысячи читателей SitePoint стекались на наш сайт Twitaway, чтобы получить свою книгу, несколько PHP-процессов конкурировали за создание, обновление и чтение данных сеанса в этой единой таблице. А из-за блокировки на уровне таблиц MyISAM им приходилось делать это по одному, ожидая своей очереди на все более резервном сервере базы данных.

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

Но там, где у MyISAM есть ахиллесова пята, у InnoDB есть секретное оружие: блокировка на уровне строк . При записи данных в таблицу InnoDB блокируются только затронутые строки, а не вся таблица. Запросы на доступ к другим частям одной и той же таблицы могут выполняться одновременно. В ситуации, подобной данным сеанса, когда каждый обрабатываемый запрос браузера может относиться к другому сеансу, блокировка на уровне строк — это то, что доктор прописал!

Имея это в виду, вы можете догадаться, как мы вылечили наш заболоченный сервер баз данных. Мы просто преобразовали таблицу сеанса в таблицу InnoDB:

 ALTER TABLE sessions ENGINE = InnoDB;

Мгновенно нагрузка на процессор на сервере базы данных упала до более приемлемого уровня, сайт Twitaway вернулся в оперативный режим, и было много радости. Мы могли бы избавить себя от многих неприятностей, просто указав тип таблицы при первом создании таблицы:

 CREATE TABLE sessions (
  sesskey VARCHAR(32) NOT NULL DEFAULT '' PRIMARY KEY,
  expiry INT(11),
  expireref VARCHAR(64),
  sessdata LONGTEXT
) ENGINE = InnoDB;

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

Если MyISAM жертвует функциями ради производительности, тип таблицы Память принимает это до крайности. Таблицы памяти никогда не записываются на диск, поэтому при перезапуске сервера базы данных данные, хранящиеся в любых таблицах памяти, будут потеряны. Однако тот факт, что таблицы памяти не требуют доступа к диску, делает их быстрыми! Поскольку данные сеанса в любом случае должны быть нестабильными, мы не особо возражали, если бы эта таблица не выдержала перезапуск сервера.

Согласно документации, таблицы памяти имеют ту же слабость блокировки на уровне таблиц, что и MyISAM. Тем не менее, когда мы переключили нашу таблицу сеансов на механизм хранения памяти, загрузка ЦП нашего сервера баз данных практически упала до нуля!

Таблицы памяти имеют некоторые жесткие ограничения, которые означают, что они не являются панацеей от проблем с производительностью. Если не учитывать данные, таблицы памяти не могут содержать большие типы столбцов, такие как TEXT или BLOB. Действительно, самая большая часть данных, которую вы можете сохранить в строке таблицы памяти, это 255 символов CHAR или VARCHAR. В нашем случае нам пришлось сократить пару имен переменных сеанса, чтобы убедиться, что они подходят!

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