<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/features.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'features.persistent-connections.php',
    1 => 'Постоянные соединения с базами данных',
    2 => 'Постоянные соединения с базами данных',
  ),
  'up' => 
  array (
    0 => 'features.php',
    1 => 'Особенности',
  ),
  'prev' => 
  array (
    0 => 'features.connection-handling.php',
    1 => 'Работа с соединениями',
  ),
  'next' => 
  array (
    0 => 'features.commandline.php',
    1 => 'Работа с PHP из командной строки',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'features/persistent-connections.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="features.persistent-connections" class="chapter">
 <h1 class="title">Постоянные соединения с базами данных</h1>


 <div class="simplesect">
  <h3 class="title">Что такое постоянные соединения?</h3>
  <p class="simpara">
   Постоянные соединения представляют собой связи с базами данных,
   которые не закрываются при завершении скрипта. При получении запроса
   на постоянное соединение PHP вначале проверяет, имеется ли
   идентичное постоянное соединение (которое было открыто при предыдущих
   обращениях) и, если таковое было найдено, использует его.
   В случае если идентичного соединения нет, PHP создаёт новое. Под
   &quot;идентичным&quot; подразумевается соединение, открытое на том же хосте с
   таким же именем пользователя и паролем (если они указаны).
  </p>
  <p class="simpara">
   Нет способа запросить конкретное соединение или гарантировать,
   что вы получите существующее соединение или совершенно новое
   (если все существующие соединения заняты или запрос обслуживается другим процессом,
   у которого есть отдельный пул соединений).
  </p>
  <p class="simpara">
   Это значит, что вы не можете использовать постоянные соединения PHP, например, для:
  </p>
  <ul class="simplelist">
   <li>назначения определённой сессии базы данных конкретному веб-пользователю</li>
   <li>создания крупной транзакции, охватывающей несколько запросов</li>
   <li>инициализации запроса для одного запроса и собирания результатов по другому</li>
  </ul>
  <p class="simpara">
   Постоянные соединения не предоставляют вам <em>никаких</em> функций,
   которые были бы недоступны при использовании непостоянных соединений.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.web">
  <h3 class="title">Веб-запросы</h3>
  <p class="simpara">
   Существует два основных способа использования PHP сервером для генерации
   веб-страниц.
  </p>
  <p class="simpara">
   Первый способ заключается в том, чтобы использовать PHP как CGI-обёртку.
   При этом PHP-интерпретатор создаётся и уничтожается при каждом
   обращении к странице (PHP-скрипту). Поскольку интерпретатор
   уничтожается после каждого запроса к серверу, все используемые им ресурсы
   (в том числе и соединение с базой данных) закрываются. Следовательно,
   в этом случае вы не получите ничего от использования постоянных соединений -
   их просто нет.
  </p>
  <p class="simpara">
   Второй и наиболее популярный метод заключается в запуске PHP-FPM или PHP
   в качестве модуля в многопроцессорном веб-сервере, которым в настоящее время
   является только Apache.
   В таких конфигурациях обычно можно выделить один процесс
   (родительский), который координирует работу всех остальных процессов
   (дочерних), которые фактически и выполняют работу по обслуживанию веб-страниц.
   При каждом обращении клиента к серверу запрос перенаправляется одному из
   дочерних процессов, который в данный момент не занят обслуживанием другого
   клиента. Это означает, что когда тот же самый клиент выполняет повторный
   запрос к серверу, он может быть обработан другим дочерним процессом, отличным
   от того, который был при первом обращении. После открытия постоянного
   соединения каждая последующая страница, требующая соединения с базой данных,
   может использовать уже установленное ранее соединение с SQL-сервером.
  </p>
  <blockquote class="note"><p><strong class="note">Замечание</strong>: 
   <p class="para">
    Вы можете проверить, какой метод используют ваши веб-запросы,
    проверив значение «Server API» в выводе функции <span class="function"><a href="function.phpinfo.php" class="function">phpinfo()</a></span>
    или значение константы <strong><code><a href="reserved.constants.php#constant.php-sapi">PHP_SAPI</a></code></strong>,
    полученное из веб-запроса.
   </p>
   <p class="para">
    Если API сервера является «Apache 2 Handler» или «FPM/FastCGI»,
    то постоянные соединения будут использоваться для всех запросов,
    обслуживаемых одним и тем же рабочим процессом.
    Для любого другого значения постоянные соединения не будут сохраняться
    после каждого запроса.
   </p>
  </p></blockquote>
 </div>

 <div class="simplesect" id="persistent-connections.cli">
  <h3 class="title">Процессы командной строки</h3>
  <p class="simpara">
   Поскольку PHP в командной строке использует новый процесс для каждого скрипта,
   постоянные соединения не используются совместно между скриптами командной строки,
   поэтому нет смысла использовать их во временных скриптах, таких как задания Cron или команды.
   Однако они могут быть полезны, если, например, вы пишете долго работающий сервер приложений,
   который обслуживает много запросов или задач, и каждому из них может потребоваться
   собственное соединение с базой данных.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.why">
  <h3 class="title">Зачем их использовать?</h3>
  <p class="simpara">
   Постоянные соединения полезны в том случае, если при открытии большого количества SQL-соединений
   возникает ощутимая нагрузка на сервер. То, насколько велика эта нагрузка,
   зависит от многих факторов. Например, от того, какая именно база данных
   используется, находится ли она на том же компьютере что и ваш веб-сервер,
   насколько загружена машина, на которой установлен SQL-сервер, и так далее.
   В случае, если затраты на установку соединения велики, постоянные соединения
   могут вам существенно помочь. Они позволяют дочернему процессу на
   протяжении всего жизненного цикла использовать одно и то же соединение
   вместо того, чтобы создавать его при обработке каждой страницы,
   которая взаимодействует с SQL-сервером. Это означает, что каждый дочерний
   процесс, открывший постоянное соединение, будет иметь своё собственное
   соединение с сервером. Например, если у вас запущено 20 дочерних процессов,
   которые выполнили скрипт, использовавший постоянное соединение с SQL-сервером,
   вы получите 20 различных соединений с SQL-сервером, по одному на каждый дочерний
   процесс.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.drawbacks.conn-limits">
  <h3 class="title">Возможные недостатки: ограничения подключения</h3>
  <p class="simpara">
   Следует заметить, что этот подход имеет некоторые недостатки: если
   вы используете базу данных с ограниченным количеством возможных
   подключений, оно может быть превышено количеством запрашиваемых
   дочерними процессами постоянных соединений. Например, если ваша база
   данных позволяет 16 одновременных соединений, и во время нагрузки
   на сервер 17 дочерних процессов попробуют открыть соединение, одна из
   попыток потерпит неудачу. Если в вашем коде содержатся ошибки, не позволяющие
   закрывать соединение (например, бесконечные циклы), база данных с
   16 одновременными подключениями вскоре может оказаться заблокированной.
  </p>
  <p class="simpara">
   Постоянные соединения обычно увеличивают количество соединений,
   открытых в любой момент времени, поскольку неактивные рабочие процессы по-прежнему
   удерживают соединения для предыдущих запросов, которые они обслуживали.
   Если для обработки потока запросов запускается большое количество рабочих процессов,
   открытые ими соединения остаются открытыми до тех пор,
   пока рабочий процесс не будет завершён или сервер базы данных не закроет соединение.
  </p>
  <p class="simpara">
   Убедитесь, что максимальное количество подключений, разрешённое сервером базы данных,
   превышает максимальное количество рабочих процессов веб-запросов (плюс любое другое использование,
   такое как задания Cron или административные подключения).
  </p>
  <p class="simpara">
   Проверьте документацию по базе данных для получения информации об обработке заброшенных
   или неактивных соединений (время ожидания). Длительное время ожидания может
   значительно увеличить количество постоянных соединений, открытых в любой момент времени.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.drawbacks.state">
  <h3 class="title">Возможные недостатки: поддержание состояния соединения</h3>
  <p class="simpara">
   Некоторые модули баз данных выполняют автоматическую очистку
   при повторном использовании соединения; другие оставляют эту задачу
   на усмотрение разработчика приложения.
   В зависимости от выбранного модуля базы данных и конструкции приложения
   перед завершением работы скрипта может потребоваться ручная очистка.
   К изменениям, которые могут привести к неожиданному состоянию соединений,
   относятся:
  </p>
  <ul class="simplelist">
   <li>Выбранная / базовая база данных</li>
   <li>Блокировка таблиц</li>
   <li>Незавершённые транзакции</li>
   <li>Временные таблицы</li>
   <li>Настройки или функции, специфичные для подключения, такие как профилирование</li>
  </ul>
  <p class="simpara">
   Блокировки таблиц и транзакции, которые не очищаются или не закрываются,
   могут привести к бесконечной блокировке других запросов и/или
   к неожиданным изменениям при повторном использовании соединения.
  </p>
  <p class="simpara">
   Выбор неправильной базы данных приведёт к тому, что при последующем повторном
   использовании соединения запросы не будут выполняться должным образом
   (или будут выполняться в неправильной базе данных, если схемы достаточно похожи).
  </p>
  <p class="simpara">
   Если временные таблицы не очищаются, последующие запросы не смогут воссоздать ту же таблицу.
  </p>
  <p class="simpara">
   Вы можете реализовать очистку с помощью деструкторов классов или
   функции <span class="function"><a href="function.register-shutdown-function.php" class="function">register_shutdown_function()</a></span>.
   Вы также можете рассмотреть возможность использования специальных прокси-серверов для пула подключений,
   которые включают функцию в свой набор возможностей.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.final-words">
  <h3 class="title">Заключительные слова</h3>
  <p class="simpara">
   Учитывая их поведение и потенциальные недостатки, описанные выше,
   не следует использовать постоянные соединения без тщательного обдумывания.
   Их не следует использовать без внедрения дополнительных изменений в ваше приложение
   и тщательной настройки вашего сервера базы данных и веб-сервера и/или PHP-FPM.
  </p>
  <p class="simpara">
   Рассмотрите альтернативные решения, такие как исследование и устранение причин накладных расходов
   на создание соединений (например, отключение обратного DNS-поиска на сервере базы данных)
   или использование специальных прокси-серверов для объединения соединений.
  </p>
  <p class="simpara">
   Для веб API с большим объёмом данных рассмотрите возможность использования альтернативных сред
   выполнения или серверов приложений с длительным временем работы.
  </p>
 </div>

 <div class="simplesect" id="persistent-connections.seealso">
  <h3 class="title">Смотрите также</h3>
  <ul class="simplelist">
   <li><span class="function"><a href="function.ibase-pconnect.php" class="function">ibase_pconnect()</a></span></li>
   <li><span class="function"><a href="function.oci-pconnect.php" class="function">oci_pconnect()</a></span></li>
   <li><span class="function"><a href="function.odbc-pconnect.php" class="function">odbc_pconnect()</a></span></li>
   <li><span class="function"><a href="function.pfsockopen.php" class="function">pfsockopen()</a></span></li>
   <li><span class="function"><a href="function.pg-connect.php" class="function">pg_connect()</a></span></li>
   <li><a href="mysqli.persistconns.php" class="link">Модуль MySQLi и постоянные соединения</a></li>
   <li><a href="pdo.connections.php" class="link">Подключения и управление подключениями</a></li>
  </ul>
 </div>
</div>
<?php manual_footer($setup); ?>