<?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); ?>