Файл настроек плагина (>=1.1.x)

Следующая документация относится к PECL/mysqlnd_ms >= 1.1.0-beta. Она не подходит для более ранних версий. Документация для более ранних версий содержится в разделе mysqlnd_ms 1.0.x и ниже.

Введение

Замечание: Список изменений: Функционал добавленный в PECL/mysqlnd_ms 1.1.0-beta

Следующая документация относится к PECL/mysqlnd_ms >= 1.1.0-beta. Она не подходит для более ранних версий.

Плагин использует собственный файл конфигурации. Файл конфигурации содержит информацию о главном сервере репликации MySQL, подчиненных серверах репликации MySQL, политике выбора сервера (балансировка нагрузки), стратегии обработки отказов и использовании ленивых соединений.

Плагин загружает свой файл конфигурации в начале веб-запроса. Затем он кешируется в памяти и используется в течение всей обработки запроса. Таким образом, нет необходимости перезапускать PHP после изменения файла конфигурации. Изменения в конфигурационном файле станут активными почти мгновенно.

Для определения конфигурационного файла используется директива конфигурации PHP mysqlnd_ms.config_file. Обратите внимание, что эта директива не будет повторно прочитана для обработки каждого нового веб-запроса. Таким образом, при изменении имени или местоположения файла конфигурации потребуется перезагрузка PHP. Однако, если имя и путь файла остались прежними, то все изменения в нем будут применены.

Использование и разбор JSON достаточно эффективно, и использование JSON позволяет создать иерархические структуры данных намного легче, в отличие от стандартного файла php.ini.

Пример #1 Преобразование массива PHP в формат JSON

Или в качестве альтернативы, если есть желание, можно использовать синтаксис массивов PHP. В этом примере показан процесс преобразования массива в формат JSON.

<?php
$config 
= array(
  
"myapp" => array(
    
"master" => array(
      
"master_0" => array(
        
"host"   => "localhost",
        
"socket" => "/tmp/mysql.sock",
      ),
    ),
    
"slave" => array(),
  ),
);

file_put_contents("mysqlnd_ms.ini"json_encode($configJSON_PRETTY_PRINT));
printf("Файл mysqlnd_ms.ini создан...\n");
printf("Сброс содержимого файла...\n");
printf("%s\n"str_repeat("-"80));
echo 
file_get_contents("mysqlnd_ms.ini");
printf("\n%s\n"str_repeat("-"80));
?>

Результат выполнения данного примера:

Файл mysqlnd_ms.ini создан...
Сброс содержимого файла...
--------------------------------------------------------------------------------
{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost",
                "socket": "\/tmp\/mysql.sock"
            }
        },
        "slave": [

        ]
    }
}
--------------------------------------------------------------------------------

Конфигурационный файл плагина состоит из одной или более секций. Секции представлены свойствами самого верхнего уровня в дереве JSON. Секции также можно назвать именами конфигураций.

Приложения ссылаются на секции по их имени. Приложения используют имена секций в качестве параметра хоста (сервера) в методах создания соединения расширений mysqli, mysql и PDO_MYSQL. После подключения плагин mysqlnd сравнивает имя хоста со всеми именами секций из файла конфигурации плагина. Если совпадение найдено, то будут загружены настройки для этой секции.

Пример #2 Пример использования имен секций

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.2.27"
            },
            "slave_1": {
                "host": "192.168.2.27",
                "port": 3306
            }
        }
    },
    "localhost": {
        "master": [
            {
                "host": "localhost",
                "socket": "\/path\/to\/mysql.sock"
            }
        ],
        "slave": [
            {
                "host": "192.168.3.24",
                "port": "3305"
            },
            {
                "host": "192.168.3.65",
                "port": "3309"
            }
        ]
    }
}
<?php
/* Все эти соединения будут использовать распределение нагрузки */
$mysqli = new mysqli("myapp""username""password""database");
$pdo = new PDO('mysql:host=myapp;dbname=database''username''password');
$mysql mysql_connect("myapp""username""password");

$mysqli = new mysqli("localhost""username""password""database");
?>

Имена секций - это строки. Допустимо использовать такие значения: 192.168.2.1, 127.0.0.1 или localhost. Если, к примеру, приложение соединяется к localhost и присутствует секция localhost, то семантика операции соединения будет изменена. Приложение будет использовать не только сервер MySQL, расположенный на localhost, но плагин также начнет балансировку нагрузки MySQL-запрсов, согласно правилам из секции localhost. Таким образом, вы можете настроить балансировку нагрузки без изменения исходного кода приложения. Имейте в виду, что такая конфигурация может не способствовать общей читаемости исходного кода вашего приложения. Использование имен секций, совпадающих с реальными именами хостов, следует рассматривать как последнее средство.

Каждая секция конфигурации, как минимум, должна содержать список основных (master) серверов и список подчиненных (slave) серверов. Список основных серверов содержится в секции master, а список подчиненных в секции slave. Если не задать список подчиненных серверов, то будет выдана фатальная ошибка уровня E_ERROR, поэтому просто оставляйте ее пустой. Допустимо не использовать подчиненные сервера, но рекомендуется это делать только в случае синхронных кластеров. Более подробно читайте в разделе поддерживаемые кластеры. Основная часть документации фокусируется на использовании асинхронных кластеров репликации MySQL.

Списки подчиненных и основных серверов могут быть индексированы символьными именами. Либо же можно использовать массив анонимных записей.

Пример #3 Список анонимных подчиненных серверов

"slave": [
    {
        "host": "192.168.3.24",
        "port": "3305"
    },
    {
        "host": "192.168.3.65",
        "port": "3309"
    }
]

Список анонимных серверов задается массивом JSON. Список индексированных серверов задается объектом JSON.

Пример #4 Список основных серверов с символьными именами

"master": {
    "master_0": {
        "host": "localhost"
    }
}

Рекомендуется все же использовать имена для серверов, так как они будут показаны в сообщениях об ошибках.

Порядок серверов сохраняется и учитывается mysqlnd_ms. Если, например, вы сконфигурируете стратегию балансировки round robin, то первый запрос SELECT будет запущен на первом из списка сервере.

Сервер описывается с помощью полей host, port, socket, db, user, password и connect_flags. Обязательным из них является только host, а остальные опциональны.

Пример #5 Конфигурация сервера

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "db_server_host",
                "port": "db_server_port",
                "socket": "db_server_socket",
                "db": "database_resp_schema",
                "user": "user",
                "password": "password",
                "connect_flags": 0
            }
        },
        "slave": {
            "slave_0": {
                "host": "db_server_host",
                "port": "db_server_port",
                "socket": "db_server_socket"
            }
        }
    }
}

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

Формат конфигурационного файла был изменен в версии 1.1.0-beta для возможности задания связанных фильтров. Фильтры нужны для фильтрации списка серверов для выбора того, на котором будет выполняться запрос. Фильтры настраиваются в разделе filter. Фильтры запускаются mysqlnd_ms в том порядке, как они описаны в конфигурационном файле. Определение фильтров не является обязательным, так что, если они вам не нужны, то можете не указывать секцию filters.

Фильтры заменяют использовавшуюся в более ранних версиях настройку pick[] Новые random и roundrobin предоставляют ту-же функциональность.

Пример #6 Новый фильтр roundrobin, старая функциональность

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            },
            "slave_1": {
                "host": "192.168.78.137",
                "port": "3306"
            }
        },
        "filters": {
            "roundrobin": [

            ]
        }
    }
}

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

Пример #7 Фильтр user заменяет mysqlnd_ms_set_user_pick_server()

"filters": {
    "user": {
        "callback": "pick_server"
    }
}

Корректность конфигурационного файла проверяется на двух этапах, при его первоначальном чтении и при создании соединения. Чтение конфигурационного файла происходит при старте обработки веб-запроса PHP. На ранних стадиях загрузки расширения, ошибки могут отображаться некорректно. В самом худшем случае ошибки вообще не будут показаны и соединения не будут устанавливаться, возвращая неадекватные сообщения. Эта проблема была решена вверсии 1.5.0.

Пример #8 Обычные сообщения об ошибках в случае некорректности конфигурационного файла (до версии 1.5.0)

<?php
$mysqli 
= new mysqli("myapp""username""password""database");
?>

Результат выполнения данного примера:

Warning: mysqli::mysqli(): (mysqlnd_ms) (mysqlnd_ms) Failed to parse config file [s1.json]. Please, verify the JSON in Command line code

Warning: mysqli::mysqli(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Name or service not known in Command line code on line 1

Warning: mysqli::query(): Couldn't fetch mysqli in Command line code on line 1

Fatal error: Call to a member function fetch_assoc() on a non-object in Command line code on line 1

Начиная с версии 1.5.0, ошибки этапа чтения конфигурационного файла сохраняются в буфере и выводятся при попытке установки соединения. Для установки уровня ошибок, который будет использоваться для вывода забуферизованных ошибок, используйте директиву mysqlnd_ms.force_config_usage. По умолчанию будет использоваться уровень E_WARNING.

Пример #9 Улучшеная проверка конфигурационного файла, начиная с версии 1.5.0

<?php
$mysqli 
= new mysqli("myapp""username""password""database");
?>

Результат выполнения данного примера:

Warning: mysqli::mysqli(): (mysqlnd_ms) (mysqlnd_ms) Failed to parse config file [s1.json]. Please, verify the JSON in Command line code on line 1

Для отладки потенциальных ошибок конфигурационного файла может быть полезным установить mysqlnd_ms.force_config_usage = 1. Это не только переключит уровень ошибок на E_RECOVERABLE_ERROR, но и позволит отловить опечатки в именах секций.

Пример #10 Возможно более точная ошибка из-за mysqlnd_ms.force_config_usage=1

mysqlnd_ms.force_config_usage=1
<?php
$mysqli 
= new mysqli("invalid_section""username""password""database");
?>

Результат выполнения данного примера:

Warning: mysqli::mysqli(): (mysqlnd_ms) Exclusive usage of configuration enforced but did not find the correct INI file section (invalid_section) in Command line code on line 1 line 1

Конфигурационные директивы

Ниже приведено краткое описание доступных директив конфигурации.

master массив или объект

Списков основных серверов MySQL. Список может быть как типа массив JSON для указания списка анонимных серверов, так и типа объект JSON. Примеры смотрите выше.

Обязательно должен быть определен хотя бы один основной сервер. Если в конфигурационном файле будет отсутствовать эта директива, то плагин вызовет ошибку уровня E_ERROR. Фатальная ошибка может выглядить так (mysqlnd_ms) Section [master] doesn't exist for host [name_of_a_config_section] in %s on line %d.

Сервер описывается с моиощью host, port, socket, db, user, password и connect_flags. Обязательно должно присутствовать значение host. Остальные настройки по желанию. Если их не задавать, то плагин возьмет их из параметров, указанных пользователем в функции соединения с БД. Также см.: пример использование имен секций.

Таблица параметров настройки сервера.

Ключ Описание Версия
host

Хост, на котором развернута база данных. Это обязательный параметр. В случае его отсутствия, при попытке соединения будет выдана ошибка уровня E_RECOVERABLE_ERROR. Ошибка будет примерно такая (mysqlnd_ms) Cannot find [host] in [%s] section in config in %s on line %d.

Начиная с 1.1.0.
port

Порт TCP/IP, на котором слушает БД.

Начиная с 1.1.0.
socket

Сокет Unix сервера баз данных.

Начиная с 1.1.0.
db

База данных (схема).

Начиная с 1.1.0.
user

Пользователь.

Начиная с 1.1.0.
password

Пароль.

Начиная с 1.1.0.
connect_flags

Флаги соединения.

Начиная с 1.1.0.

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

slave массив или объект

Список подчиненных серверов репликации MySQL. Синтаксис аналогичен списку основных серверов. См. master.

Плагин поддерживает работу с одним или более подчиненными серверами.

Список подчиненных серверов должен присутствовать в обязательном порядке. Плагин вызовет ошибку уровня E_ERROR, если в конфигурационном файле будет отсутствовать директива slave. Сообщение об ошибке будет примерно таким (mysqlnd_ms) Section [slave] doesn't exist for host [%s] in %s on line %d. Обратите внимание, что допустимо указывать пустой список. Ошибка была введена только для контроля, что секция slave в принципе присутствует. Если у вас используются только основные сервера, то допустимо оставлять этот список пустым.

При пустом списке, если будет предпринята попытка запустить запрос на подчиненном сервере, плагин может выдать предупреждение типа mysqlnd_ms) Couldn't find the appropriate slave connection. 0 slaves to choose from.. Также возможно у другое предупреждение, такое как (mysqlnd_ms) No connection selected by the last filter.

global_transaction_id_injection массив или объект

Настройка глобального идентификатора транзакции относится как к встроеному серверному механизму глобальных идентификаторов транзакций, так и к эмуляции на стороне клиента.

Ключ Описание Версия
fetch_last_gtid

SQL-запрос для извлечения последнего глобального идентификатора транзакций. Этот запрос нужен тогда, когда плагину необходимо узнать самый свежий идентификатор глобальной транзакции. Это нужно, например, при проверке статуса подчиненного сервера. Также используется функцией mysqlnd_ms_get_last_gtid().

Начиная с 1.2.0.
check_for_gtid

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

Начиная с 1.2.0.
report_errors

Выдавать ли предупреждение в случае, если выполнение какого либо из этих запросов завершилось ошибкой.

Начиная с 1.2.0.
on_commit

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

Начиная с 1.2.0.
wait_for_gtid_timeout

Заставляет плагин ждать wait_for_gtid_timeout секунд, пока подчиненный сервер не придет в согласованное состояние, при поиске подчиненного сервера. Эта настройка ограничивает время выделяемое на опрос статуса подчиненного сервера. Если опрос занимает слишком много времени, то общее затраченное время может привысить wait_for_gtid_timeout. Плагин вызывает sleep(1) для паузы в одну секунду между двумя опросами.

Эта настройка может использоваться как с функионалом MySQL 5.6., так и с эмуляцией глобального идентификатора транзакций на стороне клиента.

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

Начиная с 1.4.0.
fabric объект

Настройки, относящиеся к MySQL Fabric. Если плагин используется вместе с MySQL Fabric, то файл конфигурации плагина не содержит секций со списками серверов. Вместо этого плагин запрашивает эти списки у MySQL Fabric.

Минимальная конфигурация для использования плагина с MySQL Fabric должна содержать список из одного или более хостов MySQL Fabric, к которым он будет обращаться. Если задано более одного хоста, то плагин будет использовать стратегию roundrobin для выбора одного из них. Другие стратегии пока недоступны.

Пример #11 Минимальная конфигурация плагина для использования с MySQL Fabric

{
    "myapp": {
        "fabric": {
            "hosts": [
                {
                    "host" : "127.0.0.1",
                    "port" : 8080
                }
            ]
        }
    }
}

Каждый хост MySQL Fabric описывается объектом JSON со следующими параметрами.

Ключ Описание Версия
host

Хост MySQL Fabric.

Начиная с 1.6.0.
port

Порт TCP/IP, на котором MySQL Fabric принимает запросы от удаленных клиентов, типа нашего плагина.

Начиная с 1.6.0.

Плагин использует потоки PHP для общения с MySQL Fabric через XML RPC поверх HTTP. По умолчанию дл сетевого взаимодействия не заданы какие0либо тайм-ауты. Таким образом, по умолчанию используются тайм-ауты потоков PHP, которые не контролируются плагином.

Можно задать собственные тайм-ауты. Настройка тайм-аутов в конфигурационном файле плагина будет иметь тот же эффект, что и настройка тайм-аутов для HTTP-соединения через потоки PHP.

Тайм-аут задается в секундах. Допустимые значения лежат в диапазоне от 0 до 65535. Эта настройка доступна с версии 1.6.

Пример #12 Таймаут соединения с Fabric

{
    "myapp": {
        "fabric": {
            "hosts": [
                {
                    "host" : "127.0.0.1",
                    "port" : 8080
                }
            ],
            "timeout": 2
        }
    }
}

Закрепление транзакции за сервером (Transaction stickiness) и логика MySQL Fabric могут противоречить друг другу. Закрепление транзакции запрещает переключение между серверами на протяжении выполнения транзакции. При использовании Fabric и шардинга, пользователь ошибочно может запустить локальную транзакцию на одном сервере и затем попытаться переключиться на другой, используя функции mysqlnd_ms_fabric_select_shard() или mysqlnd_ms_fabric_select_global(). В этом случае плагин не отвергнет запрос на переключение посреди транзакции, а позволит пользователю переключиться независимо от того, включено ли закрепление. Это будет исключительно ошибкой пользователя, написавшего такой код.

Если закрепление транзакции включено и вы хотите получать предупреждения, используя функции mysqlnd_ms_fabric_select_shard() или mysqlnd_ms_fabric_select_global(), то установите логический флаг trx_warn_server_list_changes.

Пример #13 Предупреждение о нарушении границ транзакции

{
    "myapp": {
        "fabric": {
            "hosts": [
                {
                    "host" : "127.0.0.1",
                    "port" : 8080
                }
            ],
            "trx_warn_serverlist_changes": 1
        },
        "trx_stickiness": "on"
    }
}
<?php
$link 
= new mysqli("myapp""root""""test");
/*
  Этот запрос скорее всего завершится с ошибкой.
  Но в любом случае мы перейдем в режим
  необходимый для демонстрации.
*/
@mysqlnd_ms_fabric_select_global($link1);
$link->begin_transaction();
@
$link->query("DROP TABLE IF EXISTS test");
/*
  Переключение серверов/шардов является ошибкой
  при открытии локальной транзакции!
*/
mysqlnd_ms_select_global($link1);
?>

Результат выполнения данного примера:

PHP Warning: mysqlnd_ms_fabric_select_global(): (mysqlnd_ms) Fabric server exchange in the middle of a transaction in %s on line %d

Обратите внимание, что эта особенность эксперементальная и в будущем синтаксис и семантика могут поменяться.

filters объект

Список фильтров. Фильтры нужны для фильтрации списка серверов доступных для выполнения заданного запроса. Фильтры можно выстраивать цепочкой. Фильтры random и roundrobin заменяют директиву pick[], ранее использовавшуюся для политик балансировки. Фильтр user заменяет функцию mysqlnd_ms_set_user_pick_server().

Фильтры могут принимать параметры для уточнения своих действий.

Если политики балансировки нагрузки не заданы, то плагин будет использовать random_once. Политика random_once берет случайный подчиненный сервер в момент запуска первого запроса на чтение. Для любых запросов, производящих только чтение, будет использоваться подчиненный сервер до момента завершения обработки PHP-скрипта. В случае, если в этой секции не заданы random или roundrobin, то балансировка нагрузки не будет использоваться.

Если цепочка фильтров настроена таким образом, что фильтр возвращающий не более одного сервера является входом фильтра, которому необходимо получить более одного сервера, то плагин может выдать предупреждение при создании подключения. Предупреждение будет примерно такое: (mysqlnd_ms) Error while creating filter '%s' . Non-multi filter '%s' already created. Более того, для соединения может быть установлена ошибка с номером 2000, SQL-состоянием HY000 и сообщением, аналогичным предупреждению.

Пример #14 Некорректная последовательность фильтров

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "filters": [
            "roundrobin",
            "random"
        ]
    }
}
<?php
$link 
= new mysqli("myapp""root""""test");
printf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error());
$link->query("SELECT 1 FROM DUAL");
?>

Результат выполнения данного примера:

PHP Warning:  mysqli::mysqli(): (HY000/2000): (mysqlnd_ms) Error while creating filter 'random' . Non-multi filter 'roundrobin' already created. Stopping in filter_warning.php on line 1
[2000] (mysqlnd_ms) Error while creating filter 'random' . Non-multi filter 'roundrobin' already created. Stopping
PHP Warning:  mysqli::query(): Couldn't fetch mysqli in filter_warning.php on line 3

Фильтр: random объект

Фильтр random работает как политики балансировки нагрузки, как random и random once, устанавливаемые ранее с помощью pick[].

Политика random берет случайный сервер всякий раз, когда надо выполнить запрос на чтение. Политика random once выбирает один подчиненный сервер и использует его для запросов на чтение на протяжении выполнения PHP-скрипта. Если политики балансировки нагрузки не определены, то, по умолчанию, будет использоваться random once.

Если фильтру random не передать ни одного аргумента, то будет выбрана политика балансировки random.

Пример #15 Стратегия random с помощью фильтра random

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            },
            "slave_1": {
                "host": "192.168.78.137",
                "port": "3306"
            }
        },
        "filters": [
            "random"
        ]
    }
}

Фильтру можно передать необязательный аргумент sticky. Если параметр sticky будет иметь значение 1 (типа строка), то фильтр будет определять стратегию random once.

Пример #16 Стратегия random once с помощью фильтра random

{
    "filters": {
        "random": {
            "sticky": "1"
        }
    }
}

Оба фильтра, random и roundrobin, поддерживают установку приоритета, веса сервера, начиная с PECL/mysqlnd_ms 1.4.0. Если в фильтре определен аргумент weight, он должен определять вес для всех серверов. При этом список подчиненных серверов в секции slave должен быть не анонимным, а именованным, как и в секции master. Имена должны использоваться для определения весов конкретных серверов с помощью weight.

Пример #17 Ошибка ссылки

[E_RECOVERABLE_ERROR] mysqli_real_connect(): (mysqlnd_ms) Unknown server 'slave3' in 'random' filter configuration. Stopping in %s on line %d

Использование ошибочного имени в секции weight может привести к ошибке, показанной выше.

Если секция weight отсутствует, то вес всех серверов считается за единицу.

Пример #18 Задание весов для балансировки нагрузки

{
   "myapp": {
       "master": {
           "master1":{
               "host":"localhost",
               "socket":"\/var\/run\/mysql\/mysql.sock"
           }
       },
       "slave": {
           "slave1": {
               "host":"192.168.2.28",
               "port":3306
           },
           "slave2": {
               "host":"192.168.2.29",
               "port":3306
           },
           "slave3": {
               "host":"192.0.43.10",
               "port":3306
           },
       },
       "filters": {
           "random": {
               "weights": {
                   "slave1":8,
                   "slave2":4,
                   "slave3":1,
                   "master1":1
               }
           }
       }
   }
}

В среднем, сервер с весом два будет выбираться в два раза чаще, чем сервер с весом один. Различные веса могут отражать производительность разных серверов, для задания предпочтения серверам с меньшими сетевыми задержками, или для настройки резервного сервера, который будет использоваться в случае отказа основного. В последнем случае имеет смысл задать резервному серверу очень маленький вес относительно остальных. К примеру, в приведенной выше конфигурации, сервер slave3 будет принимать только 8 процентов всех запросов, и если slave1 и slave2 работают, он будет выбираться крайне редко. В случае отказа slave1 и slave2, использование slave3 возрастет. Пожалуйста, перед тем как начать использовать веса таким образом, почитайте об обеспечении отказоустойчивости.

Корректные значения весов лежат в диапазоне от 1 до 65535.

Неизвестные аргументы будут тихо проигнорированы.

The filter expects one or more servers as input. Outputs one server. A filter sequence such as random, roundrobin may cause a warning and an error message to be set on the connection handle when executing a statement.

Список аргументов фильтра.

Ключ Описание Версия
sticky

Включает или отключает стратегию балансировки random once. См. выше.

Начиная с 1.2.0.
weight

Назначение весов/приоритетов серверам при балансировке нагрузки. Описание см. выше.

Начиная с 1.4.0.
Фильтр: roundrobin объект

При использовании фильтра roundrobin, плагин последовательно перебирает список подчиненных серверов для выбора сервера, на котором будет выполнен запрос. Если плагин доходит до конца списка, то перебор начинается с его начала.

Пример #19 roundrobin filter

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "filters": [
            "roundrobin"
        ]
    }
}

Ожидает один или более серверов на вход. Возвращает один сервер. Последовательность фильтров, такая как roundrobin, random может установить ошибку для обработчика подключения во время выполнения запроса.

Список аргументов фильтра.

Ключ Описание Версия
weight

Назначение весов/приоритетов серверам при балансировке нагрузки. Описание см. выше.

Начиная с 1.4.0.
Фильтр: user объект

Фильтр user заменяет функцию mysqlnd_ms_set_user_pick_server(), которая была удалена в версии 1.1.0-beta. Фильтр устанавливает функцию обратного вызова, которая будет использоваться для выбора серверов и разделения операций записи/чтения.

Встроенный в плагин механизм разделения чтения/записи может быть переопределен двумя способами. Самый простой способ - добавить в начало строки запроса SQL-хинт MYSQLND_MS_MASTER_SWITCH, MYSQLND_MS_SLAVE_SWITCH или MYSQLND_MS_LAST_USED_SWITCH. Использованием SQL-хинтов можно контролировать, например, будет ли запрос выполняться на основном сервере или на одном из подчиненных. С помощью SQL-хинтов невозможно однозначно указать конкретный подчиненный сервер для выполнения запроса.

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

Плагин вызывает функцию для выбора сервера из списков подчиненных и основных серверов. Функция должна проанализировать запрос и вернуть URI наиболее подходящего сервера для его выполнения.

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

Пример #20 Установка callback-функции

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "filters": {
            "user": {
                "callback": "pick_server"
            }
        }
    }
}

Функция обратного вызова должны вернуть хост для выполнения запроса. URI хоста должно браться из списков подчиненных и основных серверов, переданных функции. Если функция вернет значение, отсутствующее в этих списках, то плагин вызовет ошибку уровня E_RECOVERABLE_ERROR. Ошибка будет примерно такая (mysqlnd_ms) User filter callback has returned an unknown server. The server 'server that is not in master or slave list' can neither be found in the master list nor in the slave list. Если приложение перехватит и проигнорирует эту ошибку, то для соединения будет ошибка, например такая: (mysqlnd_ms) No connection selected by the last filter with the error code 2000 and the sqlstate HY000. Также может быть показано предупреждение.

Задание в качестве функции обратного вызова несуществующей функции приведет к ошибке уровня E_RECOVERABLE_ERROR при попытке ее вызвать. Сообщение об ошибке будет примерно такое: (mysqlnd_ms) Specified callback (pick_server) is not a valid callback. Если приложение перехватит и проигнорирует эту ошибку, то для соединения будет ошибка, например такая: (mysqlnd_ms) Specified callback (pick_server) is not a valid callback with the error code 2000 and the sqlstate HY000. Также может быть показано предупреждение.

Следующие параметры плагин передает функции обратного вызова.

Параметр Описание Версия
connected_host

URI текущего соединения.

Начиная с 1.1.0.
query

Строка с SQL-запросом, который необходимо выполнить.

Начиная с 1.1.0.
masters

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

Начиная с 1.1.0.
slaves

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

Начиная с 1.1.0.
last_used_connection

URI сервера, на котором исполнялся последний запрос.

Начиная с 1.1.0.
in_transaction

Логический флаг, определяющий, является ли запрос частью открытой транзакции. Если включен режим autocommit, то этот флаг будет false, иначе true.

Определение транзакции базируется на мониторинге библиотеки mysqlnd на предмет вызовов set_autocommit. Более подробно читайте в разделе пулы соединений и переключение.

Начиная с 1.1.0.

Пример #21 Использование функции обратного вызова

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.2.27",
                "port": "3306"
            },
            "slave_1": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "filters": {
            "user": {
                "callback": "pick_server"
            }
        }
    }
}
<?php
function pick_server($connected$query$masters$slaves$last_used_connection$in_transaction)
{
 static 
$slave_idx 0;
 static 
$num_slaves NULL;
 if (
is_null($num_slaves))
  
$num_slaves count($slaves);

 
/* по умолчанию: откат в встроеной в плагин логике */
 
$ret NULL;

 
printf("Пользователь соединен с '%s'...\n"$connected);
 
printf("... решаем где выполнять '%s'\n"$query);

 
$where mysqlnd_ms_query_is_select($query);
 switch (
$where)
 {
  case 
MYSQLND_MS_QUERY_USE_MASTER:
   
printf("... используем основной сервер\n");
   
$ret $masters[0];
   break;
  case 
MYSQLND_MS_QUERY_USE_SLAVE:
   
/* SELECT или SQL-хинт для использования подчиненного */
   
if (stristr($query"FROM table_on_slave_a_only"))
   {
    
/* таблица, которая находится только на первом подчиненном сервере */
    
printf("... доступ к таблице возможен только на подчиненном сервере A\n");
    
$ret $slaves[0];
   }
   else
   {
    
/* round robin */
    
printf("... выполняем запрос на чтение на подчиненном сервере\n");
    
$ret $slaves[$slave_idx++ % $num_slaves];
   }
   break;
  case 
MYSQLND_MS_QUERY_LAST_USED:
   
printf("... испольщзуем тот же сервер, что и для предыдущего запроса\n");
   
$ret $last_used_connection;
   break;
 }

 
printf("... ret = '%s'\n"$ret);
 return 
$ret;
}

$mysqli = new mysqli("myapp""root""""test");

if (!(
$res $mysqli->query("SELECT 1 FROM DUAL")))
 
printf("[%d] %s\n"$mysqli->errno$mysqli->error);
else
 
$res->close();

if (!(
$res $mysqli->query("SELECT 2 FROM DUAL")))
 
printf("[%d] %s\n"$mysqli->errno$mysqli->error);
else
 
$res->close();


if (!(
$res $mysqli->query("SELECT * FROM table_on_slave_a_only")))
 
printf("[%d] %s\n"$mysqli->errno$mysqli->error);
else
 
$res->close();

$mysqli->close();
?>

Результат выполнения данного примера:

Пользователь соединен с 'myapp'...
... решаем где выполнять 'SELECT 1 FROM DUAL'
... выполняем запрос на чтение на подчиненном сервере
... ret = 'tcp://192.168.2.27:3306'
Пользователь соединен с 'myapp'...
... решаем где выполнять 'SELECT 2 FROM DUAL'
... some read-only query for a slave
... ret = 'tcp://192.168.78.136:3306'
Пользователь соединен с 'myapp'...
... решаем где выполнять 'SELECT * FROM table_on_slave_a_only'
... доступ к таблице возможен только на подчиненном сервере A
... ret = 'tcp://192.168.2.27:3306'

Фильтр: user_multi объект

Фильтр user_multi отличается от user только в одном аспекте. Весь остальной синтаксис аналогичен. Фильтр user может выбрать и вернуть только один сервер. Цепочка фильтров на этом обычно заканчивается, так как она должна сократить список серверов ровно до одного. Соответственно, после user остается ровно один сервер, на котором и запускается запрос.

Фильтр user_multi, напротив, возвращает список подчиненных и основных серверов, который передается следующему фильтру в цепочке. Такой фильтр обычно ставится в начале цепочки фильтров. Другой фильтр, возвращающий более одного сервера - это quality_of_service.

Функция обратного вызова, заданая в user_multi должна вернуть массив с двумя элементами. В первом список выбранных основных серверов, а во втором - подчиненных серверов. Список должен содержать ключи подчиненных и основных серверов, присутствующие в переданных ему ранее списках. Следующий пример демонстрирует получения случайного списка подчиненных и основных серверов.

Пример #22 Получение случайных подчиненных и основных серверов

<?php
function pick_server($connected$query$masters$slaves$last_used_connection$in_transaction)
{
  
$picked_masters = array()
  foreach (
$masters as $key => $value) {
    if (
mt_rand(02) > 1)
      
$picked_masters[] = $key;
  }
  
$picked_slaves = array()
  foreach (
$slaves as $key => $value) {
    if (
mt_rand(02) > 1)
      
$picked_slaves[] = $key;
  }
  return array(
$picked_masters$picked_slaves);
}
?>

В случае, если функция не вернет списка серверов, плагин выдаст ошибку уровня E_RECOVERABLE. Ошибка будет примерно такая: (mysqlnd_ms) User multi filter callback has not returned a list of servers to use. The callback must return an array in %s on line %d. В случае, если списки будут не пустыми, но будут содержать некорретные ключи серверов, то будет выдана ошибка уровня E_RECOVERABLE с примерно таким текстом: (mysqlnd_ms) User multi filter callback has returned an invalid list of servers to use. Server id is negative in %s on line %d, или похожая.

Будет ли выдаваться ошибка в случае возврата пустых списков подчиненных или основных серверов, зависит от настроек. Если возвращен пустой список основных серверов для запроса на запись, то плагин, скорее всего, вызовет ошибку типа (mysqlnd_ms) Couldn't find the appropriate master connection. 0 masters to choose from. Something is wrong in %s on line %d. Обычно именно такая ошибка уровня E_ERROR и произойдет. В случае операции чтения и пустого списка подчиненных серверов, поведение будет определяться конфигурацией механизма защиты от отказов. Если допустим откат к основному серверу, то ошибок не будет. Если же откат к основному серверу запрещен, то будет ошибка типа (mysqlnd_ms) Couldn't find the appropriate slave connection. 0 slaves to choose from. Something is wrong in %s on line %d.

Фильтр: node_groups объект

Фильтр node_groups позволяет группировать узлы кластера и отправлять запрос на эти группы, например для поддержки партиционирования. Партиционирование данных может потребоваться в случае ручного шардирования, в кластерах типа primary copy с несколькими основными серверами или для обхода узких мест в кластерах типа update everywhere, не имеющих собственных механизмов партиционирования. Соответственно, после этого фильтра необходимо применить другой фильтр, который выберет один сервер из возвращенного списка.

Ключ Описание Версия
пользовательские имена групп узлов

Можно определить одну или более групп узлов кластера. Каждая такая группа должна иметь свое имя. Это имя используется в SQL-хинтах для принудительного задания группы серверов, на которых должен выполняться запрос. Для запуска запроса на одном из серверов конкретной группы, SQL-зхапрос должен предворяться хинтом /*имя группы узлов*/. Обратите внимание, что вокруг имя группы узлов не должно быть пробелов. Поскольку имя группы узлов будет использоваться как есть вам качестве части SQl-хинта, вы должны избегать имен некорректных с точки зрения SQL.

Каждое определение группы серверов должно содержать список основных-серверов. Также можно задавать список подчиненных серверов. В случае отсутствия основного-сервера в группе name_of_group может привести к ошибке уровня E_RECOVERABLE_ERROR с текстом (mysqlnd_ms) No masters configured in node group 'name_of_group' for 'node_groups' filter.

Список подчиненных и основных серверов должен содержать сервера, которые присутствуют в глобальном списке основных серверов и глобальном списке подчиненных серверов соответственно. В случае если будет указан неизвестный сервер, то будет выдана ошибка уровня E_RECOVERABLE_ERROR с текстом (mysqlnd_ms) Unknown master 'server_alias_name' (section 'name_of_group') in 'node_groups' filter configuration.

Пример #23 Ручное партиционирование

{
  "myapp": {
       "master": {
            "master_0": {
                "host": "localhost",
                "socket": "\/tmp\/mysql.sock"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.2.28",
                "port": 3306
            },
            "slave_1": {
                "host": "127.0.0.1",
                "port": 3311
            }
        },
        "filters": {
            "node_groups": {
                "Partition_A" : {
                    "master": ["master_0"],
                    "slave": ["slave_0"]
                }
            },
           "roundrobin": []
        }
    }
}

Обратите внимание, что если цепочка фильтров вернула пустой список подчиненных серверов и использована конфигурационная директива mysqlnd_ms.multi_master=0, плагин может выдать предупреждение.

Начиная с 1.5.0.
Фильтр: quality_of_service объект

Фильтр quality_of_service идентифицирует узлы кластера способные предоставить необходимый уровень сервиса. Это фильтр, который может вернуть ноль, один или несколько серверов. За ним обязательно должен следовать фильтр, гарантированно возвращающий ровно один сервер.

Фильтр quality_of_service появился в версии 1.2.0-alpha. В версиях 1.2, этот фильтр фокусируется на таком аспекте качества, как согласованность данных. Разные типы кластеров предоставляют разный уровень согласованности данных. К примеру, асинхронная репликация MySQL предоставляет не гарантированную согласованность. Подчиненный сервер может не отдать запрашиваемые данные, либо данные на нем будут устаревшиви, поскольку они на него еще не среплицировались. Обычно это допустимо. В других случаях, для корректной работы приложения, требуется более высокий уровень согласованности. Для таких случаев quality_of_service может отбросить сервера, которые этого не гарантируют.

Фильтр quality_of_service может быть удален или создан во время исполнения.Успешный вызов mysqlnd_ms_set_qos() удаляет все существующие qos фильтры из списка фильтров и добавляет новый в самое начало. Все настройки, какие можно сделать с помощью mysqlnd_ms_set_qos(), также можно сделать и в конфигурационном файле. Однако использование функции, безусловно, является наиболее распространенным вариантом использования. Вместо установки уровней session_consistency и strong_consistency, рекомендуется просто не указывать подчиненные сервера, а только основные сервера. Использование пустого списка подчиненных серверов короче и более читаемо. Единственный уровень сервиса, который имеет смысл указывать в конфигурационном файле, это комбинация eventual_consistency и контроль максимальной задержки репликации подчиненного сервера.

Ключ Описание Версия
eventual_consistency

Не гарантированная согласованность. Позволяет использовать любые подчиненные и основные сервера. данные могут быть устаревшими.

Данный фильтр может принимать опциональный параметр age. Если параметр age задан, то плагин будет рассматривать достойными чтения только те подчиненные сервера, для которых репликация возвращает задержку подчиненного сервера меньшую или равную age. Задержка репликации измеряется с использованием SHOW SLAVE STATUS. Если плагин не может получить значение задержки репликации, то сервер будет исключен. Детали реализации и использования описаны в секции концепции качества сервиса.

Обратите внимание, что если цепочка фильтров вернула пустой список подчиненных серверов и использована конфигурационная директива mysqlnd_ms.multi_master=0, плагин может выдать предупреждение.

Пример #24 Global limit on slave lag

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.2.27",
                "port": "3306"
            },
            "slave_1": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "filters": {
            "quality_of_service": {
                "eventual_consistency": {
                    "age":123
                }
            }
        }
    }
}

Начиная с 1.2.0.
session_consistency

Согласованность на уровне сессии (гарантия чтения значений, которые вы записали). Позволяет использовать все основные сервера и подчиненные сервера синхронизованные с основным сервером. Если не задано никаких дополнительных параметров, то все подчиненные серверы будут исключены, так как надежно определить то, что подчиненные сервера полностью синхронизированы с основным сервером - невозможно. Обратите внимание, что если цепочка фильтров вернула пустой список подчиненных серверов и использована конфигурационная директива mysqlnd_ms.multi_master=0, плагин может выдать предупреждение.

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

Начиная с 1.1.0.
strong_consistency

Сильная согласованность. Будут использованы только основной сервер.

Начиная с 1.2.0.
failover До версии (включая) 1.3.x: строка. Начиная с 1.4.0: объект.

Политика обработки отказов. Поддерживаемые политики: disabled (по умолчанию), master, loop_before_master (Начиная с 1.4.0).

Если политики обработки отказов не настроены, то по умолчанию никакой автоматической обработки отказов производиться не будет (failover=disabled). Всякий раз, когда плагин не может установить соединение с сервером, он выдает предупреждение и устанавливает для этого соединения код и сообщение об ошибке. После этого он возвращает ошибку приложению на обработку и, например, перепосылает последний запрос в надежде, что будет выбран другой сервер.

Обратите внимание, что логика обработки отказов по умолчанию применяется только при создании нового соединения. Если соединение было открыто, то никаких попыток переподключения, в случае ошибки, производиться не будет. Если, к примеру, сервер, с которым установлено соединение, отключится, то, при попытке выполнить на нем запрос, никаких попыток восстановления предпринято не будет. Вместо этого вернется ошибка.

Если используется failover=master, то плагин будет неявно переключаться на основной сервер, если он доступен. Пожалуйста внимательно почитайте документацию, где описаны все риски и подводные камни использования failover=master.

Пример #25 Откат к основному серверу в случае невозможности соединиться с подчиненным сервером (PECL/mysqlnd_ms < 1.4.0)

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "failover": "master"
    }
}

Начиная с PECL/mysqlnd_ms 1.4.0 ключ конфигурации failover должен содержать объект.

Пример #26 Новый синтаксис, начиная с версии 1.4.0

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "failover": {"strategy": "master" }
    }
}

Ключ Описание Версия
strategy

Политика обработки отказов. Допустимые значения: disabled (default), master, loop_before_master

Значение disabled запрещает автоматическую обработку отказов.

Значение master говорит плагину, что, в случае недоступности подчиненного сервера, необходимо переключиться на основному серверу. Если соединение с основным сервером вернуло ошибку, то плагин завершит цикл обработки ошибок и вернет ошибку пользователю.

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

Начиная с 1.4.0.
remember_failed

Запоминать ошибки в течение всего времени исполнения PHP-скрипта. По умолчанию: false.

Если установить как true, то плагин запомнит хосты, к которым не удалось подключиться, и в течение исполнения текущего скрипта больше не будет пытаться их использовать для балансировки нагрузки.

Начиная с версии 1.4.0. Этот функционал доступен только в сочетании с фильтрами балансировки нагрузки random и roundrobin. Рекомендуется использовать эту опцию.
max_retries

Максимальное количество попыток коннекта перед тем, как отклонить хост. По умолчанию: 0 (нет ограничений).

Эта настройка предназначена для предотвращения исключения хостов из списка используемых после первой ошибки соединения. Если установить n > 0, то плагин не будет убирать сервер из списка пока для него не будет предпринято заданное количество неудачных соединений.

Начиная с 1.4.0. Этот функционал доступен только в сочетании с фильтрами балансировки нагрузки random и roundrobin.

Установка failover любым значением, отличным от disabled, master или loop_before_master, не вызовет ошибок или предупреждений.

lazy_connections логическое

Контролирует использование ленивых подключений. Ленивые подключения - это такие подключения, которые не устанавливаются, пока клиент не отправит первый запрос. Включены по умолчанию.

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

Леничные подключения не представляют опасности, если вы часто меняете состояние подключения. Плагин не отправляет все изменения состояния во все соединения из пула соединений. Небольшое количество изменений посылаются только в открытые соединения. Ленивые соединения, открытые позже, не будут затронуты. Только небольшое количество настроек будут "запоминаться" и применяться к новым открытым ленивым соединениям.

Пример #27 Запрещение ленивых соединений

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "lazy_connections": 0
    }
}

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

server_charset строка

Эта настройка появилась в версии 1.4.0. Крайне рекомендуется ее использовать при включенных ленивых подключениях.

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

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

Приложения использующие ленивые подключения могут попытаться экранировать строки перед отправкой запроса. Фактически это обычное дело, когда строка запроса нуждается в экранировании. Однако, так как ленивое соединение еще не открыто, то такое экранирование завершится с ошибкой. Плагин выдаст ошибку уровня E_WARNING с текстом (mysqlnd_ms) string escaping doesn't work without established connection. Possible solution is to add server_charset to your configuration.

Установка server_charset позволит плагину использовать заданную кодировку для экранирования строк до того, как ленивое соединение будет реально установлено. Мало того, плагин будет принудительно использовать эту кодировку когда соединение установлено.

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

Плагин не запрещает пользователю в любой момент поменять кодировку с помощью функции set_charset() или соответствующего SQL-запроса. Обратите внимание, что использование SQL не рекомендуется, поскольку плагин не сможет его отследить. Пользователь может, к примеру, изменить кодировку для ленивого соединения после экранирования, но до установления соединения. Кодировка, установленная пользователем, может быть использована для любого вложенного экранирования, до установления реального соединения. Соединение будет установлено с использованием заданной в конфигурационном файле кодировки не взирая на кодировку сервера и предшествующие действия пользователя. Если соединение уже было установлено, set_charset больше не имеет смысла.

Пример #28 Экранирование строк для ленивых подключений

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "lazy_connections": 1,
        "server_charset" : "utf8mb4"
    }
}
<?php
$mysqli 
= new mysqli("myapp""username""password""database");
$mysqli->real_escape("это будет экранироваться с помощью настройки server_charset - utf8mb4");
$mysqli->set_charset("latin1");
$mysqli->real_escape("это будет экранироваться с помощью latin1");
/* server_charset implicitly set - utf8mb4 connection */
$mysqli->query("SELECT 'это соединение будет использовать server_charset' AS _msg FROM DUAL");
/* теперь используется latin1*/
$mysqli->set_charset("latin1");
?>

master_on_write логическое значение

Если установлено, то плагин начнет использовать основной сервер только после того, как на нем будет выполнен первый запрос. Приложение также сможет использовать подчиненные сервера, используя SQL-хинты.

Эта настройка может помочь победить задержку репликации. Если, например, будет запущен запрос INSERT, то, после него, все последующие запросы плагин будет отправлять на основной сервер, включая запросы типа SELECT. Это может помочь в случае, когда подчиненные сервера не успеют среплицировать результаты запроса INSERT.

Пример #29 Обеспечение консистентного чтения

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "master_on_write": 1
    }
}

Обратите внимание, что фильтр quality_of_service появился в версии 1.2.0-alpha. Он дает возможность более тонко упровлять уровнем сервиса, который вам нужен.

Все настройки привязки транзакции к серверу, включая trx_stickiness=on, перебиваются master_on_write=1.

trx_stickiness строка

Политики привязки транзакции к серверу. Поддерживаемые политики: disabled (по умолчанию), master.

Эта настройка требует версии PHP 5.4.0 или выше. Если использовать с более ранними версиями PHP, то плагин выдаст предупреждение вида (mysqlnd_ms) trx_stickiness strategy is not supported before PHP 5.3.99.

Если политика не определена, либо установлена как trx_stickiness=disabled, то плагин может балансировать нагрузку и переключаться между серверами в течение транзакции. Плагин не транзакционно-безопасен. Для предотвращения переключения соединения в ходе транзакции можно использовать SQL-хинты.

Начиная с PHP 5.4.0, библиотека mysqlnd позволяет отслеживать изменение состояния режима autocommit, путем перехвата вызовов функции set_autocommit(). Если задано set_stickiness=master и autocommit отключен расширением PHP MySQL путем вызова mysqlnd внутренней функции set_autocommit(), плагин понимает, что начата транзакция, и все последующие запросы будут выполняться на основном сервере, пока не будет включен autocommit. Соответственно можно не использовать SQL-хинты.

Например, для вызова внутренней функции библиотеки mysqlnd set_autocommit(), можно использовать mysqli_autocommit().

Следует учесть, что даже с установкой trx_stickiness=master, плагин не сможет перехватывать изменение режима autocommit, вызваное SQL-запросами, такими как SET AUTOCOMMIT=0 или BEGIN.

Начиная с PHP 5.5.0, библиотека mysqlnd содержит дополнительные вызовы C API для контроля транзакций. Уровень контроля совпадает с заданным SQL-запросом. mysqli API был изменен для использования этих вызовов. Начиная с версии 1.5.0, PECL/mysqlnd_ms может отслеживать не только mysqli_autocommit(), но также mysqli_begin(), mysqli_commit() и mysqli_rollback(). Это позволяет отслеживать границы транзакций и останавливать для них балансировку.

Пример #30 Использование основного сервера для запуска транзакции

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
        },
        "trx_stickiness": "master"
    }
}

Начиная с версии 1.5.0, в рамках транзакции запрещен автоматическая обработка отказов. Если границы транзакции определены верно, разрешена привязка транзакции к серверу и, в этот момент, сервер перестает отвечать, то плагин не будет пытаться переключиться на другой сервер, если явно не настроены политики обработки отказов. Пользователь должен обработать такие ошибки самостоятельно. В зависимости от настроек, плагин может выдать ошибку уровня E_WARNING с текстом вроде такого: (mysqlnd_ms) Automatic failover is not permitted in the middle of a transaction. Эта ошибка может быть перебита другими ошибками, выданными функциями, исполняющими запрос, например (mysqlnd_ms) No connection selected by the last filter.

Пример #31 Нет автоматического переключения при сбое, обработка ошибок

<?php
/* предположение: настроена автоматическая обработка ошибок */
$mysqli = new mysqli("myapp""username""password""database");

/* переключаем внутреннее состояние плагина в in_trx = 1 */
$mysqli->autocommit(false);

/* предположение: сервер перестал отвечать */
if (!($res $mysqli->query("SELECT 'Этот запрос не выполнился' AS _msg FROM DUAL"))) {
 
/* обрабатываем ошибку транзакции, внутреннее состояние плагина все еще in_trx = 1 */
 
printf("[%d] %s"$mysqli->errno$mysqli->error);
 
/*
  Если используется механизм определения транзакции на базе autocommit(),
  то НЕОБХОДИМО вызвать autocommit(true). В противном случае плагин
  будет думать, что продолжается текущая транзакция и переключиться на
  другой сервер не получится.
 */
 
$mysqli->autocommit(true);
 
/* Ну и теперь вы хотите начать новую транзакцию */
 
$mysqli->autocommit(false);
}
/* с этого момента используем latin1 */
$mysqli->set_charset("latin1");
?>

Если сервер отказывает в середине транзакции, плагин будет игнорировать все попытки переключиться на другой сервер, пока транзакция не будет завершена. Напомним, что плагин контролирует вызовы API для обнаружения границ транзакций. Таким образом, вам нужно, например, включить режим автокоммита для завершения текущей транзакции для того, чтобы плагин включит балансировку нагрузки. Собственно вы можете сразу же начать новую транзакцию, снова отключив режим автоматического подтверждения.

Если вы не будете обрабатывать ошибки и завершать некорректную транзакцию, то дальнейшие вызовы API могут привести к ошибкам, таким как Commands out of sync; you can't run this command now. Запомните - это очень важно, обрабатывать все ошибки.

transient_error объект

Настройка появилась в версии 1.6.0.

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

PECL/mysqlnd_ms может выполнить повтор запроса от имени приложения. С помощью transient_error можно настроить плагин для повтора запроса, завершившегося с конкретными ошибками определенное количество раз с паузой между попытками. Если в процессе повтора ошибка пропадет, то приложение этого даже не заметит. Если все попытки завершились неудачей, то приложению будет возвращена ошибка.

Пример #32 Цикл повторов исправляемых ошибок

{
    "myapp": {
        "master": {
            "master_0": {
                "host": "localhost"
            }
        },
        "slave": {
            "slave_0": {
                "host": "192.168.78.136",
                "port": "3306"
            }
       },
       "transient_error": {
          "mysql_error_codes": [
            1297
          ],
          "max_retries": 2,
          "usleep_retry": 100
       }
    }
}

Ключ Описание Версия
mysql_error_codes

Список исправляемых кодов ошибок. Вы можете добавить любые коды ошибок в этот список. Допустимо считать исправляемой любую ошибку, а не только 1297 (HY000 (ER_GET_TEMPORARY_ERRMSG), Message: Got temporary error %d '%s' from %s). Перед добавлением других кодов, кроме 1297, в список, убедитесь, что кластер поддержитвает повтор такого запроса без нарушения состояния вашего приложения.

Начиная с 1.6.0.
max_retries

Максимальное количество повторов перед отправкой ошибки пользователю.

По умолчанию: 1

Начиная с 1.6.0.
usleep_retry

Задержка в миллисекундах между повторами. Это значение будет передано C-функции usleep().

По умолчанию: 100

Начиная с 1.6.0.
xa объект

Настройка появилась в версии 1.6.0.

Замечание: Эксперементальный функционал

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

state_store
record_participant_credentials

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

Обратите внимание, что при использовании этого функционала, имя и пароль пользователя в хранилище состояния MySQL будут храниться в текстовом виде. Вся отвественность за обеспечение безопасности этой информации лежит на вас.

По умолчанию: false

participant_localhost_ip

Во время сбора мусора XA, плагин может найти сервер участник, для которого был был записан хост localhost. Если сбор мусора запустился на другом хосте, но хост сделал запись участника в хранилище состояния, то localhost будет разрешаться как другой хост. Поэтому при записи имени хоста участника в хранилище состояния следует использовать не localhost, а актуальный IP-адрес localhost.

Установка participant_localhost_ip должна использоваться только если нельзя избежать использования localhost. Если смотреть только с точки зрения сбора мусора, желательно не настраивать соединения с сокетом, а использовать IP-адрес и порт.

mysql

В качестве хранилища состояния доступно только хранилище состояния MySQL.

global_trx_table

Имя таблицы MySQL, используемой для хранения состояния текущей или оборванной глобальной транзакции. Для создания таблицы используйте приведенный ниже SQL-запрос. Не забудьте заменить имя таблицы на то, которое будете использовать в настройках.

По умолчанию: mysqlnd_ms_xa_trx

Пример #33 SQL-запрос для создания таблицы хранилища состояния транзакции

CREATE TABLE mysqlnd_ms_xa_trx (
  store_trx_id int(11) NOT NULL AUTO_INCREMENT,
  gtrid int(11) NOT NULL,
  format_id int(10) unsigned NOT NULL DEFAULT '1',
  state enum('XA_NON_EXISTING','XA_ACTIVE','XA_IDLE','XA_PREPARED','XA_COMMIT','XA_ROLLBACK') NOT NULL DEFAULT 'XA_NON_EXISTING',
  intend enum('XA_NON_EXISTING','XA_ACTIVE','XA_IDLE','XA_PREPARED','XA_COMMIT','XA_ROLLBACK') DEFAULT 'XA_NON_EXISTING',
  finished enum('NO','SUCCESS','FAILURE') NOT NULL DEFAULT 'NO',
  modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  started datetime DEFAULT NULL,
  timeout datetime DEFAULT NULL,
  PRIMARY KEY (store_trx_id),
  KEY idx_xa_id (gtrid,format_id,finished),
  KEY idx_state (state)
) ENGINE=InnoDB

participant_table

Имя таблицы MySQL, используемой для хранения информации об участниках текущей или оборванной глобальной транзакции. Для создания таблицы используйте приведенный ниже SQL-запрос. Не забудьте заменить имя таблицы на то, которое будете использовать в настройках.

Хранение имени и пароля пользователя контролируется настройкой record_participant_credentials

По умолчанию: mysqlnd_ms_xa_participants

Пример #34 SQL-запрос для создания таблицы хранилища участников транзакции

CREATE TABLE mysqlnd_ms_xa_participants (
  fk_store_trx_id int(11) NOT NULL,
  bqual varbinary(64) NOT NULL DEFAULT '',
  participant_id int(10) unsigned NOT NULL AUTO_INCREMENT,
  server_uuid varchar(127) DEFAULT NULL,
  scheme varchar(1024) NOT NULL,
  host varchar(127) DEFAULT NULL,
  port smallint(5) unsigned DEFAULT NULL,
  socket varchar(127) DEFAULT NULL,
  user varchar(127) DEFAULT NULL,
  password varchar(127) DEFAULT NULL,
  state enum('XA_NON_EXISTING','XA_ACTIVE','XA_IDLE','XA_PREPARED','XA_COMMIT','XA_ROLLBACK')
   NOT NULL DEFAULT 'XA_NON_EXISTING',
  health enum('OK','GC_DONE','CLIENT ERROR','SERVER ERROR') NOT NULL DEFAULT 'OK',
  connection_id int(10) unsigned DEFAULT NULL,
  client_errno smallint(5) unsigned DEFAULT NULL,
  client_error varchar(1024) DEFAULT NULL,
  modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (participant_id),
  KEY idx_xa_bqual (bqual),
  KEY idx_store_trx (fk_store_trx_id),
  CONSTRAINT mysqlnd_ms_xa_participants_ibfk_1 FOREIGN KEY (fk_store_trx_id)
    REFERENCES mysqlnd_ms_xa_trx (store_trx_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB
garbage_collection_table

Имя таблицы MySQL используемой для отслеживания и синхронизации запуска сбора мусора. Для создания таблицы используйте приведенный ниже SQL-запрос. Не забудьте заменить имя таблицы на то, которое будете использовать в настройках.

По умолчанию: mysqlnd_ms_xa_gc

Пример #35 SQL-запрос для создания таблицы хранилища тнформации о сборе мусора

CREATE TABLE mysqlnd_ms_xa_gc (
  gc_id int(10) unsigned NOT NULL AUTO_INCREMENT,
  gtrid int(11) NOT NULL,
  format_id int(10) unsigned NOT NULL DEFAULT '1',
  fk_store_trx_id int(11) DEFAULT NULL,
  modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  attempts smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (gc_id),
  KEY idx_store_trx (gtrid,format_id,fk_store_trx_id)
) ENGINE=InnoDB
host

Имя хоста сервера MySQL.

user

Имя пользователя.

password

Пароль.

db

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

port

Порт сервера MySQL.

socket

Доменный сокет unix для сервера MySQL. Обратите внимание, что если вы используете несколько серверов PHP, то любой из них может попытаться выполнить сборку мусора и должен иметь доступ к хранилищу состояния. В этом случае вы можете настроить IP-адрес и порт для сервера MySQL, хранящего состояние, чтобы все серверы PHP могли к нему обратиться.

rollback_on_close

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

По умолчанию: true

garbage_collection
max_retries

Максимальное количество попыток запуска сборки мусора. Допустимы значения в диапазоне от 0 до 100. 0 означает отсутствие ограничений, только если ограничения не настроены в хранилище состояния. Ограничения хранилища состояния, предположительно, значительно выше, чем 100. Доступно с версии 1.6.0.

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

По умолчанию: 5

probability

Вероятность сбора мусора. Допустимы значения от 0 до 1000. Установка 0 запрещает автоматический фоновый сбор мусора. Помните, что даже установив 0, вы сможете запускать сборку мусора самостоятельно с помощью mysqlnd_ms_gc(). Доступно с версии 1.6.0.

Автоматический сбор мусора для зависшей XA-транзакции возможен только если настроено хранение состояния. Хранилище состояния отвечает за отслеживание XA-транзакции. Основываясь на записях в хранилище состояния можно определить участников транзакции и выполнить на них откат.

Сбор мусора запускается в качестве в процессе завершения обработки веб-запроса PHP, как только ваш скрипт завершил работу. Надо ли запускать сборщик мусора, определяется вычислением случайного числа между 0 и 1000. Если число выше или равно настроеной вероятности, то сборка будет запущена.

По умолчанию: 5

max_transactions_per_run

Максимальное количество XA-транзакций, которые будут обработаны за один запуск сборки мусора. Допустимы значения от 1 до 32768. Доступно с версии 1.6.0.

Зачистка незавершенный XA-транзакций требует значительных затрат времени и ресурсов. Сборщик мусора должен соединиться с несколькими участниками незавершенной транзакции и выполнить определенные SQL-запросы для ее отката.

По умолчанию: 100

Файл конфигурации плагина (<= 1.0.x)

Замечание:

Все дальнейшие описания относятся к PECL/mysqlnd_ms < 1.1.0-beta. Они не используются в более ранних версиях.

Плагин использует собственный файл конфигурации. Файл конфигурации содержит информацию о главном сервере репликации MySQL, подчиненных серверах репликации MySQL, политике выбора сервера (балансировка нагрузки), стратегии обработки отказов и использовании ленивых соединений.

Для определения конфигурационного файла используется директива конфигурации PHP mysqlnd_ms.ini_file.

Конфигурационный файл используйет стандартный формат php.ini. Он содержит одну или несколько секций. Каждая секция содержит свою порцию настроек. Глобальной секции с настройками по умолчанию нет.

Приложения обращаются к секциям по их именам. Имена секций сравниваются с именем хоста (сервера) при использовании функций соединения с MySQL расширений mysqli, mysql и PDO_MYSQL. В момент соединения плагин mysqlnd сравнивает имя хоста с именами секций конфигурационного файла. Если они совпадают, то используются настройки соответствующей секции.

Пример #36 Пример использования имен секций

[myapp]
master[] = localhost
slave[] = 192.168.2.27
slave[] = 192.168.2.28:3306
[localhost]
master[] = localhost:/tmp/mysql/mysql.sock
slave[] = 192.168.3.24:3305
slave[] = 192.168.3.65:3309
<?php
/* Все соединения будут сбалансированы по нагрузке */
$mysqli = new mysqli("myapp""username""password""database");
$pdo = new PDO('mysql:host=myapp;dbname=database''username''password');
$mysql mysql_connect("myapp""username""password");

$mysqli = new mysqli("localhost""username""password""database");
?>

Имена секций являются строками. Вполне допустимо использовать имена 192.168.2.1, 127.0.0.1 или localhost. Если, к примеру, приложение соединяется с localhost и в файле конфигурации есть секция [localhost], то семантика операции соединения изменится. Приложение больше не будет использовать БД запущенную только на сервере localhost, так как плагин запустит процесс балансировки нагрузки в соответствии с настройкой секции [localhost]. Это позволит включить балансировку нагрузки для приложения без переписывания его кода.

Директивы master[], slave[] и pick[] используют спископодобный синтаксис. Директивы с таким синтаксисом могут встречаться в конфигурационном файле множество раз в одной секции. Плагин использует опции в том порядке, как они определены в файле конфигурации. Рассмотрим пример ниже. В нем заданы две директивы slave[] в секции [myapp]. И если для операций чтения настроена политика балансировки round-robin, то первый такой запрос будет отправлен серверу mysql_slave_1, потому что он первый в списке. Второй запрос будет направлен серверу mysql_slave_2, так как он второй в списке. И т.д. Таким образом, спископодобные директивы читаются в том порядке, как они определены в файле конфигурации.

Пример #37 Спископодобный синтакцис

[myapp]
master[] = mysql_master_server
slave[] = mysql_slave_1
slave[] = mysql_slave_2

Ниже приведено краткое описание директив конфигурации, которые можно использовать.

master[] строка

URI основного сервера репликации MySQL. URI имеет следующий синтаксис hostname[:port|unix_domain_socket].

Плагин может работать только с одним основным сервером.

Определение основного сервера является обязательным. Если его не указать, то плагин выдаст предупреждение такого вида (mysqlnd_ms) Cannot find master section in config. Furthermore the plugin may set an error code for the connection handle such as HY000/2000 (CR_UNKNOWN_ERROR). Текст ошибки может отличаться в зависимости от ваших языковых настроек.

slave[] строка

URI одного или нескольких подчиненных серверов. URI имеет следующий синтаксис hostname[:port|unix_domain_socket].

Плагин может работать с одним и более подчиненными серверами.

Определение хотя бы одного подчиненного сервера является обязательным. Если его не указать, то плагин выдаст предупреждение такого вида (mysqlnd_ms) Cannot find slaves section in config. Furthermore the plugin may set an error code for the connection handle such as HY000/2000 (CR_UNKNOWN_ERROR). Текст ошибки может отличаться в зависимости от ваших языковых настроек.

pick[] строка

Политики балансировки нагрузки (выбора сервера). Поддерживаемые политики: random, random_once (по умолчанию), roundrobin, user.

Если политики не определены, то будет использоваться random_once. Политика random_once выбирает один подчиненный сервер при первом запросе на чтение и использует его для таких запросов до конца работы скрипта.

Политика random выбирает случайный подчиненный сервер для каждого нового запроса на чтение.

Если настроена политика roundrobin, плагин перебирает подчиненные сервера по очереди, начиная с первого в списке и, когда доходит до конца списка, переходит в его начало.

Установка более одной политики балансировки будет работать только для политики user и функции mysqlnd_ms_set_user_pick_server(). Если определенная пользователем функция обратного вызова не смогла выбрать сервер, то плагин перейдет к использованию второй сконфигурированной политики балансировки.

failover строка

Политика обработки отказов. Допустимые политики: disabled (по умолчанию), master.

Если политика обработки отказов не определена, то никакой автоматической обработки отказов происходить не будет (failover=disabled). Всякий раз, когда плагин не может подключиться к серверу, он будет выдавать предупреждение и устанавливать код ошибки и сообщение для соединения. Соответственно обработка ошибки, к примеру перезапуск последнего запроса на другом сервере, ложится на плечи приложения.

Если используется failover=master, плагин будет неявно откатываться к подчиненному, если он есть. Пожалуйста, изучите документацию для понимания всех потенциальных рисков использования failover=master.

lazy_connections логическое

Контролирует использование ленивых подключений. Ленивые подключения - это такие подключения, которые не устанавливаются пока клиент не пошлет первый запрос. Включены по умолчанию.

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

Леничные подключения не представляют опасности, если вы часто меняете состояние подключения. Плагин не отправляет все изменения состояния во все соединения из пула соединений. Небольшое количество изменений посылаются только в открытые соединения. Ленивые соединения, открытые позже, не будут затронуты. Только небольшое количество настроек будут "запоминаться" и применяться к новым открытым ленивым соединениям.

master_on_write логическое

Если установлено, то плагин начнет использовать основной сервер только после того, как на нем будет выполнен первый запрос. Приложение также сможет использовать подчиненные сервера используя SQL-хинты.

Эта настройка может помочь победить задержку репликации. Если, например, будет запущен запрос INSERT, то, после него, все последующие запросы плагин будет отправлять на основной сервер, включая запросы типа SELECT. Это может помочь в случае, когда подчиненные сервера не успеют среплицировать результаты запроса INSERT.

trx_stickiness строка

Политики привязки транзакции к серверу. Поддерживаемые политики: disabled (по умолчанию), master.

Эксперементальная функциональность.

Эта настройка требует версии PHP 5.4.0 или выше. Если использовать с более ранними версиями PHP, то плагин выдаст предупреждение вида (mysqlnd_ms) trx_stickiness strategy is not supported before PHP 5.3.99.

Если политика не определена, либо установлена как trx_stickiness=disabled, то плагин может балансировать нагрузку и переключаться между серверами в течение транзакции. Плагин не транзакционно-безопасен. Для предотвращения переключения соединения в ходе транзакции можно использовать SQL-хинты.

Начиная с PHP 5.4.0, библиотека mysqlnd позволяет отслеживать изменение состояния режима autocommit, путем перехвата вызовов функции trx_autocommit(). Если задано set_stickiness=master и autocommit отключен расширением PHP MySQL путем вызова mysqlnd внутренней функции trx_autocommit(), плагин понимает, что начата транзакция, и все последующие запросы будут выполняться на основном сервере, пока не будет включен autocommit. Соответственно можно не использовать SQL-хинты.

Например, для вызова внутренней функции библиотеки mysqlnd trx_autocommit(), можно использовать mysqli_autocommit().

Следует учесть, что даже с установкой trx_stickiness=master, плагин не сможет перехватывать изменение режима autocommit, вызваное SQL-запросами, такими как SET AUTOCOMMIT=0.

Тестирование

Замечание:

Секция применима к mysqlnd_ms 1.1.0 или выше, не не к 1.0.

Набор тестов PECL/mysqlnd_ms содержится в директории tests/ в исходных кодах расширения. Там содержатся стандартные тесты phpt, которые описаны на сайте PHP Quality Assurance Teams.

Запуск тестов требует наличия от одного до четырех серверов MySQL. Некоторые тесты вообще не соединяются с MySQL. Другим нужен один сервер. Некоторым два. В определенных случаях два сервера используются для эмуляции репликации. В других случаях требуются настроенные подчиненный и основной сервер репликации. Тесты попытаются определить, сколько всего и каким образом настроено серверов. Если конфигурация не подходящаяя, то соответствующие тесты будут автоматически пропущены.

Перед запуском тестов отредактируйте tests/config.inc для настройки тестовых серверов MySQL.

Наиболее простая конфигурация.

 putenv("MYSQL_TEST_HOST=localhost");
 putenv("MYSQL_TEST_PORT=3306");
 putenv("MYSQL_TEST_USER=root");
 putenv("MYSQL_TEST_PASSWD=");
 putenv("MYSQL_TEST_DB=test");
 putenv("MYSQL_TEST_ENGINE=MyISAM");
 putenv("MYSQL_TEST_SOCKET=");

 putenv("MYSQL_TEST_SKIP_CONNECT_FAILURE=1");
 putenv("MYSQL_TEST_CONNECT_FLAGS=0");
 putenv("MYSQL_TEST_EXPERIMENTAL=0");

 /* эмуляция кластера репликации */
 putenv("MYSQL_TEST_EMULATED_MASTER_HOST=". getenv("MYSQL_TEST_HOST"));
 putenv("MYSQL_TEST_EMULATED_SLAVE_HOST=". getenv("MYSQL_TEST_HOST"));

 /* реальный кластер репликации */
 putenv("MYSQL_TEST_MASTER_HOST=". getenv("MYSQL_TEST_EMULATED_MASTER_HOST"));
 putenv("MYSQL_TEST_SLAVE_HOST=". getenv("MYSQL_TEST_EMULATED_SLAVE_HOST"));

MYSQL_TEST_HOST, MYSQL_TEST_PORT и MYSQL_TEST_SOCKET определяют имя хоста, TCP/IP порт и сокет Unix для тестового сервера по умолчанию. MYSQL_TEST_USER и MYSQL_TEST_PASSWD содержат имя пользователя и пароль для подключения к MYSQL_TEST_DB. Все настроенные сервера должны иметь одинаковых пользователя и его пароль.

Можно использовать любой синтаксис host, host:port или host:/path/to/socket.

putenv("MYSQL_TEST_SLAVE_HOST=192.168.78.136:3307"));
putenv("MYSQL_TEST_MASTER_HOST=myserver_hostname:/path/to/socket"));

Отладка и диагностика

Для отладки PECL/mysqlnd_ms можно использовать лог отладки mysqlnd. О том, как mysqlnd PECL/mysqlnd_ms добавляет записи в лог библиотеки mysqlnd, читайте тут mysqlnd.debug.

Пример настроек для активации записи в лог отладки:

mysqlnd.debug=d:t:x:O,/tmp/mysqlnd.trace

Замечание:

Эта возможность доступна только совместно с отладочной сборкой PHP. Работает в Microsoft Windows если используется отладочная сборка PHP, собранная в Microsoft Visual C версии 9 и выше.

Лог отладки показывает вызовы функций библиотеки mysqlnd и плагина PECL/mysqlnd_ms одинаково. Вызовы библиотеки mysqlnd обычно идут с префиксами mysqlnd_. Вызовы PECL/mysqlnd начинаются с mysqlnd_ms.

Пример записи в отладочный лог (соединение):

[...]
>mysqlnd_connect
| info : host=myapp user=root db=test port=3306 flags=131072
| >mysqlnd_ms::connect
| | >mysqlnd_ms_config_json_section_exists
| | | info : section=[myapp] len=[5]
| | | >mysqlnd_ms_config_json_sub_section_exists
| | | | info : section=[myapp] len=[5]
| | | | info : ret=1
| | | <mysqlnd_ms_config_json_sub_section_exists
| | | info : ret=1
| | <mysqlnd_ms_config_json_section_exists
[...]

Отладочный лог полезен не только разработчикам плагина, но и обычным пользователям для поиска ошибок. Например если приложение некорректно обрабатывает ошибки или не может самостоятельно их логировать, то разбор этого лога может помочь. Но помните, что использование этого лога для отладки ошибок приложения - это самое крайнее средство. Запись отладочного лога на диск - это медленная операция, которая может негативно сказаться на скорости работы приложения.

Пример лога с ошибкой соединения:

[...]
| | | | | | | info : adding error [Access denied for user 'root'@'localhost' (using password: YES)] to the list
| | | | | | | info : PACKET_FREE(0)
| | | | | | | info : PACKET_FREE(0x7f3ef6323f50)
| | | | | | | info : PACKET_FREE(0x7f3ef6324080)
| | | | | | <mysqlnd_auth_handshake
| | | | | | info : switch_to_auth_protocol=n/a
| | | | | | info : conn->error_info.error_no = 1045
| | | | | <mysqlnd_connect_run_authentication
| | | | | info : PACKET_FREE(0x7f3ef63236d8)
| | | | | >mysqlnd_conn::free_contents
| | | | | | >mysqlnd_net::free_contents
| | | | | | <mysqlnd_net::free_contents
| | | | | | info : Freeing memory of members
| | | | | | info : scheme=unix:///tmp/mysql.sock
| | | | | | >mysqlnd_error_list_pdtor
| | | | | | <mysqlnd_error_list_pdtor
| | | | | <mysqlnd_conn::free_contents
| | | | <mysqlnd_conn::connect
[...]

Лог трассировки также может быть полезен для проверки корректности поведения самого PECL/mysqlnd_ms. К примеру для проверки, какой сервер был выбран для выполнения запроса, и почему.

Пример лога трассировки (выбор сервера):

[...]
>mysqlnd_ms::query
| info : query=DROP TABLE IF EXISTS test
| >_mysqlnd_plugin_get_plugin_connection_data
| | info : plugin_id=5
| <_mysqlnd_plugin_get_plugin_connection_data
| >mysqlnd_ms_pick_server_ex
| | info : conn_data=0x7fb6a7d3e5a0 *conn_data=0x7fb6a7d410d0
| | >mysqlnd_ms_select_servers_all
| | <mysqlnd_ms_select_servers_all
| | >mysqlnd_ms_choose_connection_rr
| | | >mysqlnd_ms_query_is_select
[...]
| | | <mysqlnd_ms_query_is_select
[...]
| | | info : Init the master context
| | | info : list(0x7fb6a7d3f598) has 1
| | | info : Using master connection
| | | >mysqlnd_ms_advanced_connect
| | | | >mysqlnd_conn::connect
| | | | | info : host=localhost user=root db=test port=3306 flags=131072 persistent=0 state=0

В этом примере был выполнен запрос DROP TABLE IF EXISTS test. Обратите внимание, что SQL-запрос был записан в лог. Возможно вам придется озаботится ограничением доступа к этому логу для сокрытия векретной информации.

Для запроса использовалась политика балансировки round robin, как видно из имени функции >mysqlnd_ms_choose_connection_rr. Он был послан на основной сервер host=localhost user=root db=test port=3306 flags=131072 persistent=0 state=0.

Мониторинг

Работу плагина можно мониторить с помощью лога трассировки mysqlnd, статистики mysqlnd, статистики плагина mysqlnd_ms plugin и прочих внешних инструментов. Использование лога трассировки рекомендуется использовать только в целях отладки. Для стандартного мониторинга рекомендуется использовать статистику плагина.

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

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

Статистика собирается для каждого процесса PHP. В зависимости от модели развертывания, один процесс PHP может обслуживать как один, так и несколько пользовательских запросов. Если используется модель CGI, то один процесс PHP обслуживает ровно один пользовательский запрос. Для FastCGI или для модуля веб-сервера, один процесс PHP, обычно, обслуживает несколько запросов. Аналогично и для веб-серверов использующих потоки. Обратите внимание, что работающие параллельно потоки могут параллельно обновлять статистику. Таким образом, при использовании многопоточной модели развертывания PHP, статистику могут одновременно обновлять несколько пользовательских запросов, и это следует помнить при ее анализе.

Пример #38 Проверка активности плагина в однопоточной модели развертывания

mysqlnd_ms.enable=1
mysqlnd_ms.collect_statistics=1
<?php
/* Балансировка нагрузки происходит согластно настройкам для секции "myapp" (не показаны) */
$mysqli = new mysqli("myapp""username""password""database");
if (
mysqli_connect_errno())
  
/* Тут бы, конечно, ошибку обработать... Ну да ладно. */
  
die(sprintf("[%d] %s\n"mysqli_connect_errno(), mysqli_connect_error()));

$stats_before mysqlnd_ms_get_stats();
if (
$res $mysqli->query("SELECT 'Read request' FROM DUAL")) {
  
var_dump($res->fetch_all());
}
$stats_after mysqlnd_ms_get_stats();
if (
$stats_after['use_slave'] <= $stats_before['use_slave']) {
  echo 
"Согласно статистике, запрос на чтение выполнялся не на подчиненном сервере!";
}
?>

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

С помощью функции register_shutdown_function() или настройки PHP auto_append_file можно легко настроить запись статистической информации в, например, файл, по завершению работы скрипта. Или не писать в файл, а посылать во внешнюю систему мониторинга.

Пример #39 Запись статистики в процессе остановки

mysqlnd_ms.enable=1
mysqlnd_ms.collect_statistics=1
error_log=/tmp/php_errors.log
<?php
function check_stats() {
  
$msg str_repeat("-"80) . "\n";
  
$msg .= var_export(mysqlnd_ms_get_stats(), true) . "\n";
  
$msg .= str_repeat("-"80) . "\n";
  
error_log($msg);
}
register_shutdown_function("check_stats");
?>