Квест → Как хакнуть форму
Прошли: 77
MySQL всегда был основной базой данных для работы с PHP, так сложилось исторически, практически с самого начала. Конечно, некоторые используют PostgreSQL, SQL Server, или Oracle, но для интернет-проектов MySQL обычно является предпочтительной реляционной БД.
Это было связано, в основном, с легкостью внедрения и применения MySQL. Libmysqlclient шел в комплекте с PHP до тех пор, пока эта библиотека не была повторно лицензирована под GPL. После чего она была удалена из-за невозможности распространения вместе с PHP.
Это сделало процесс сборки для PHP немного более сложным, потому что теперь Libmysqlclient должна была быть доступна на хостинге.
Учитывая широкую распространенность PHP, а также тот факт, что PHP был самым распространенным языком, использующим MySQL, Oracle (а позже и Sun), понимая неприятность ситуации, пришли к соглашению: они создали MySQL Native Driver - лицензированное дополнение для PHP, которое позволяет получить доступ к MySQL без libmysqlclient.
MySQL Native Driver (mysqlnd) был добавлен в PHP 5.3 и стал библиотекой по умолчанию с версии 5.4 (хотя вы все еще можете скомпилировать и использовать libmysqlclient). У него есть некоторые новые возможности, большая производительность и лучшее использование памяти, чем libmysqlclient.
Из руководства MySQL:
Библиотека mysqlnd
написана на C в виде расширения PHP. Она использует возможности PHP: управление памятью, потоки (ввод-вывод) и процедуры для работы со строками. Mysqlnd использует PHP-шное управление памятью, что дает возможность использовать ограничения памяти вдобавок с её экономией с помощью read-only переменных (копирование при записи).
Дополнительно библиотека может использовать плагины, которые можно найти в сети.
Чтобы установить, просто скомпилируйте одно из трех расширений MySQL. В каждом конкретном случае не указывайте явный путь до libmysqlclient.
Три расширения:
ext/pdo_mysql
(начиная с PHP 5.1)ext/mysqli
(начиная с PHP 5.0)ext/mysql
(начиная с PHP 2.0, объявлена устаревшей (deprecated) начиная с PHP 5.6)
Примечание: если вы установите ext/mysql
или ext/mysqli
, ext/pdo_mysql
станет доступно автоматически.
Вы можете указать расширение, выбрав один или несколько следующих опций конфигурации:
--with-mysql
--with-mysqli
--with-pdo-mysql
Если вы используете Debian или Ubuntu, то можете легко установить пакет php5-mysqlnd
с помощью командной строки:
$ sudo apt-get install php5-mysqlnd
Таким образом вы удалите пакет php5-mysql
, в основе которого лежит libmysqlclient, взамен получите предлагаемый со всеми тремя расширениями.
Помимо преимуществ в производительности, большим преимуществом mysqlnd
являются плагины. Эти плагины доступны через PECL, и могут быть легко установлены с помощью:
$ pecl install mysqlnd_< name >
Доступные (и стабильные) плагины:
mysqlnd_memcache
: переводит SQL для использования memcache-protocol MySQL 5.6, который поддерживает демона NoSQLmysqlnd_ms
: выполняет разделение чтения и записи между ведущим (master) и ведомым (slave) серверами с простой балансировкой нагрузкиmysqlnd_qc
: Добавляет простой кэш запросов в PHPmysqlnd_uh
: Позволяет писать mysqlnd плагины в PHP
Поскольку эти плагины для самого mysqlnd
, они заработают для всех трех расширений.
Наиболее полезный плагин - это mysqlnd_ms
, от master/slave - ведущий/ведомый. Этот плагин позволяет прозрачно, хотя и несколько примитивно, разделить потоки чтения и записи между разными серверами.
После установки плагина через PECL, необходимо настроить php.ini
и конфигурационный файл mysqlnd_ms
.
В файле php.ini
(или в mysqlnd_ms.ini
в Debian-подобных системах):
extension=mysqlnd_ms.so mysqlnd_ms.enable=1 mysqlnd_ms.config_file=/путь/к/mysqlnd_ms.json
Затем вам надо создать файл и назвать его mysqlnd_ms.json
. Этот файл определяет ведущий (master) и ведомый (slave) сервера, а также стратегии разделения чтения, записи и балансировки нагрузки.
Какие настройки выставлять зависит от вашей топологии репликации.
Простейшая конфигурация файла с одним ведущим и одним ведомым:
{ "appname": { "master": { "master_0": { "host": "master.mysql.host", "port": "3306", "user": "dbuser", "password": "dbpassword", "db": "dbname" } }, "slave": { "slave_0": { "host": "slave.mysql.host", "port": "3306" "user": "dbuser", "password": "dbpassword", "db": "dbname" }, } } }
Единственная обязательная настройка - это host
. Все остальные опциональны.
Дополнительно mysqlnd_ms
может сделать простое распределение нагрузки одним из нескольких путей:
random
(случайный) — выбирается случайный ведомый для каждого запроса на чтениеrandom once
(случайный единственный) — выбирается случайный ведомый для первого запроса на чтение и он же продолжает использоваться для всех последующихround robin
(циклический алгоритм) — в предопределенном порядке выбирается ведомый для каждого запроса на чтениеuser
(пользовательский) — вызов ведомого для каждого запроса определяется пользовательскими настройками
Важно понять, что пока вы не выбирете последний вариант и сами его не настроите, каждое request-обращение будет распределять нагрузку закрыто. Так, циклический алгоритм (round robin
) применяется к каждому запросу в пределах одного и того же обращения и это не значит, что в последовательной цепочке обращений на каждое будет выделен один сервер, как можно было бы ожидать от фактических настроек по распределению у железа или софта.
Имея это в виду, я бы рекомендовал не использовать этот плагин в качестве распределения нагрузки. Вместо этого для ваших ведомых используйте балансировщик, например, haproxy, и просто укажите путь к его конфигу в качестве единственного ведомого.
По умолчанию mysqlnd_ms
будет распределять запросы так: начинающиеся с SELECT
на ведомые сервера, а все остальные - на ведущий.
Это одновременно и хорошо, и плохо. Такое прозрачное распределение означает, что вам не придется делать никаких изменений в коде. Но в тоже время это примитивный подход, ибо не происходит никакого анализа, является ли запрос на самом деле запросом на чтение.
Обычный SELECT
-запрос не попадет к ведущему, но и запрос вида SELECT ... INTO
(то есть на запись) также к нему не попадет, а полетит прямиком на ведомый сервер, что может обернуться катастрофой.
К счастью, плагин включает в себя возможность указывать, что какому серверу послать. Это делается путем добавления к запросу одной из трех констант:
MYSQLND_MS_MASTER_SWITCH
— запрос уйдет ведущему серверуMYSQLND_MS_SLAVE_SWITCH
— запрос уйдет ведомому серверуMYSQLND_MS_LAST_USED_SWITCH
— запрос уйдет серверу, который использовался последним
Эти три константы всего лишь представления строк: "ms=master"
, "ms=slave"
, и "ms=last_used"
соответсвенно. Однако, в будущем строки могут быть изменены, поэтому следует использовать именно константы.
Чтобы использовать указатель, добавьте перед запросом комментарий, куда именно следует его отправить дальше. Самый простой способ сделать это - использовать sprintf(), которая использует описатели преобразования (в данном случае, %s - строковый описатель).
Для примера, пришлем SELECT-запрос ведущему серверу:
$sql = sprintf("/*%s*/ SELECT * FROM table_name;", MYSQLND_MS_MASTER_SWITCH);
Или, пошлем не-SELECT
-запрос ведомому:
$sql = sprintf("/*%s*/ CREATE TEMPORARY TABLE `temp_table_name` SELECT * FROM table_name;", MYSQLND_MS_SLAVE_SWITCH);
Последний указатель позволяет вам быть уверенным, что будет использовано то же подключение, что и для предыдущего запроса. Это особенно полезно, когда нужно убедиться, что вы переключились на ведущий и будете читать с него после того, как информация была изменена, но, потенциально, репликация еще не сработала. Другой случай: транзакции, в которых есть запросы и на чтение, и на запись.
if ($request->isPost() && $form->isValid()) { $user>setValues($form->getValues()); $user->save(); } $sql = sprintf("/*%s*/ SELECT * FROM user_session WHERE user_id = :user_id", MYSQLND_LAST_USED_SWITCH);
Ведущему будет уходить запрос тогда (и только тогда!), когда он был использован в предыдущий раз. В данном конкретном примере это случится, если произошло обновление данных пользователя (с помощью $user->save()
).
Плагин mysqlnd_ms
невероятно полезен, особенно, когда вы хотите добавить распределение чтения и записи на большие написанные поколениями программистов проекты. Не идеал, но для большинства проектов 80-90% работы будет сделано без изменения хотя бы одной строчки кода.
Во второй части мы рассмотрим более продвинутое использование плагина mysqlnd_ms
.