Полное руководство по функции mysqli_thread_safe в PHP
Узнайте, что означает потокобезопасность в PHP, почему mysqli_thread_safe() не существует и как проверить ZTS/NTS-сборку вашего PHP.
При работе с базами данных MySQL в PHP через расширение mysqli часто возникает вопрос: «Является ли mysqli потокобезопасным и есть ли функция mysqli_thread_safe(), которую можно вызвать для проверки?»
Краткий ответ: mysqli_thread_safe() не является функцией PHP, а потокобезопасность — это не то, что можно запросить во время выполнения. Это свойство определяется тем, как был скомпилирован ваш бинарный файл PHP. В этом руководстве объясняется, что на самом деле означает «потокобезопасность» для PHP, почему потокобезопасность mysqli является решением времени компиляции, когда это важно на практике и как проверить это в вашей среде.
Что означает «потокобезопасность» в PHP
Программа является потокобезопасной, когда несколько потоков могут одновременно выполнять один и тот же код без повреждения общих данных. В потокобезопасной сборке движок PHP защищает внутреннее глобальное состояние (таблицу символов, менеджер памяти, глобальные переменные расширений и т. д.) так, что два потока, выполняющиеся внутри одного процесса, не могут затирать данные друг друга.
PHP поставляется в двух вариантах, которые определяются при компиляции бинарного файла:
- ZTS (Zend Thread Safety) — также называется потокобезопасной (TS) сборкой. Движок добавляет блокировки и копии глобального состояния для каждого потока, чтобы PHP мог работать внутри многопоточного хост-процесса.
- NTS (Non-Thread-Safe) — движок предполагает один запрос на процесс и пропускает эти накладные расходы, поэтому работает быстрее.
Переключаться между ними во время выполнения нельзя, и никакой mysqli_thread_safe() для переключения или отчёта не существует. Тип сборки фиксируется при выборе ./configure --enable-maintainer-zts (или эквивалента для конкретной платформы) во время компиляции.
Почему это настройка времени компиляции, а не функция
Люди ожидают функцию mysqli_thread_safe(), потому что некоторые библиотеки на C предоставляют вызов mysql_thread_safe(). PHP не предоставляет такой функции, потому что ответ никогда не меняется для данного бинарного файла — запрашивать его во время выполнения всегда возвращало бы одно и то же значение. Безопасность mysqli в потоках напрямую наследуется от выбора ZTS/NTS, зафиксированного в сборке PHP, плюс от используемой клиентской библиотеки (современный PHP использует mysqlnd, собственный драйвер, у которого нет отдельного переключателя потокобезопасности).
Поэтому вместо того, чтобы вызывать функцию, нужно изучить сборку.
Когда потокобезопасность действительно важна
Для подавляющего большинства PHP-приложений следует использовать сборку NTS, и потокобезопасность не является проблемой:
| Среда | Используемая сборка | Причина |
|---|---|---|
| Nginx + PHP-FPM | NTS | Каждый воркер — однопоточный процесс; общих потоков нет. |
Apache с mpr_prefork | NTS | Каждый запрос получает собственный процесс. |
| CLI-скрипты, задания cron | NTS | Один процесс, один поток. |
Apache с worker / event MPM + mod_php | ZTS | mod_php работает внутри многопоточных воркеров Apache. |
Расширения типа parallel / pthreads (устаревшее) | ZTS | Они создают PHP-потоки в одном процессе. |
Классическая ловушка в реальной жизни — это Apache + mod_php на многопоточном MPM: если загрузить непотокобезопасный PHP в многопоточный Apache, сервер может аварийно завершать работу или повреждать данные под нагрузкой. Использование PHP-FPM вместо mod_php полностью обходит эту проблему, поэтому FPM + NTS является стандартным современным развёртыванием. Смотрите руководство по установке PHP о том, как выбираются сборки.
Работа с mysqli в многопоточном контексте
Даже при правильно скомпилированном ZTS PHP сам объект соединения mysqli не предназначен для совместного использования между потоками. Ссылка mysqli содержит буферизованные результаты, состояние подготовленных запросов и открытый сокет — одновременное использование из двух потоков приводит к ошибкам рассинхронизации команд или некорректным результатам.
Правило простое: одно соединение на поток. Открывайте соединение внутри потока, который его использует, а не передавайте общий дескриптор.
<?php
// Each worker/thread creates and owns its own connection.
function runWorkerTask(int $workerId): void
{
// New, independent connection for THIS thread.
$db = new mysqli('localhost', 'user', 'password', 'shop');
if ($db->connect_errno) {
// Handle per-thread connection failure locally.
error_log("Worker {$workerId} failed: {$db->connect_error}");
return;
}
$result = $db->query('SELECT COUNT(*) AS total FROM orders');
$row = $result->fetch_assoc();
echo "Worker {$workerId} sees {$row['total']} orders\n";
$db->close(); // Release the connection when the thread is done.
}Об основах открытия и проверки соединения смотрите Подключение к MySQL с помощью mysqli и mysqli_connect_errno().
Как проверить потокобезопасность в вашей среде
Поскольку функции времени выполнения не существует, используйте один из следующих способов для чтения настройки сборки.
Использование phpinfo()
Создайте однострочный скрипт и откройте его в браузере или запустите из CLI:
<?php
phpinfo();В выводе найдите верхнюю таблицу и посмотрите на строку Thread Safety (она находится рядом с информацией о Zend Engine / сборке). Там будет написано либо enabled (ZTS), либо disabled (NTS).
Проверка через командную строку
Из терминала отфильтруйте полный дамп конфигурации:
php -i | grep "Thread Safety"Это выведет одно из:
Thread Safety => enabled
Thread Safety => disabledВнутри выполняемого PHP-кода
Если вам нужно это значение программно — например, для страницы диагностики — читайте константу PHP_ZTS вместо того, чтобы искать несуществующую функцию:
<?php
// PHP_ZTS is 1 on a Thread Safe (ZTS) build, 0 on a Non-Thread-Safe (NTS) build.
echo PHP_ZTS === 1 ? "Thread-safe (ZTS) build\n" : "Non-thread-safe (NTS) build\n";Это правильная, поддерживаемая замена воображаемому вызову mysqli_thread_safe().
Распространённые ошибки
- Вызов
mysqli_thread_safe()— функция не существует и вызываетError: Call to undefined function. Используйте вместо неёPHP_ZTSилиphpinfo(). - Совместное использование одного соединения
mysqliмежду потоками — всегда открывайте отдельное соединение для каждого потока. - Загрузка NTS PHP в многопоточный Apache MPM — подбирайте сборку под сервер или переходите на PHP-FPM.
- Предположение, что ZTS «лучше» — она медленнее и нужна только для реально многопоточных хостов; в остальных случаях предпочитайте NTS.
Заключение
mysqli_thread_safe() — это функция, которой не существует. Потокобезопасность в PHP фиксируется во время компиляции выбором Zend Thread Safety (ZTS), а не переключается или отображается вызовом во время выполнения. Большинство современных стеков (Nginx/Apache-prefork + PHP-FPM) используют более быструю сборку NTS и никогда не нуждаются в ZTS. Когда вам нужно проверить это, читайте с помощью phpinfo(), php -i | grep "Thread Safety" или константы PHP_ZTS — и всегда давайте каждому потоку собственное соединение mysqli для обеспечения согласованности данных.