W3docs

Полное руководство по функции 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-FPMNTSКаждый воркер — однопоточный процесс; общих потоков нет.
Apache с mpr_preforkNTSКаждый запрос получает собственный процесс.
CLI-скрипты, задания cronNTSОдин процесс, один поток.
Apache с worker / event MPM + mod_phpZTSmod_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 для обеспечения согласованности данных.

Практика

Практика
Что означает потокобезопасность PHP?
Что означает потокобезопасность PHP?
Was this page helpful?