Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Устойчивость соединения позволяет драйверу Microsoft ODBC для SQL Server восстанавливать разорванное неактивное соединение, когда сервер и состояние сессии поддерживают восстановление. Расширения PHP передают ConnectRetryCount и ConnectRetryInterval в ODBC; они не реализуют протокол переподключения. Документация ODBC содержит поддержку SQL Server 2014 и более поздних версий, База данных SQL Azure и SQL Database в Microsoft Fabric. Не делайте выводов о поддержке Warehouse из поддержки SQL баз данных в Fabric.
| Ключевое слово | Значения | По умолчанию. | Описание |
|---|---|---|---|
ConnectRetryCount |
Целое число от 0 до 255 (включительно). | 1 | Максимальное количество попыток повторно установить разорванное соединение перед прекращением попыток. Значение 0 отключает переподключение. |
ConnectRetryInterval |
Целое число от 1 до 60 (включительно). | 10 | Количество секунд ожидания между попытками. Игнорируется, если ConnectRetryCount имеет значение 0. |
Тайм-аут запроса оператора ограничивает, сколько времени ODBC может потратить на восстановление неактивного соединения во время выполнения. Настройте QueryTimeout в параметрах оператора SQLSRV или PDO::SQLSRV_ATTR_QUERY_TIMEOUT для PDO_SQLSRV.
LoginTimeout управляет установлением соединения; увеличение этого не увеличивает бюджет восстановления оператора.
Установите те же ключевые слова для повторной попытки в массиве опций соединения SQLSRV, например ["ConnectRetryCount" => 2, "ConnectRetryInterval" => 5], или в имени PDO_SQLSRV источника данных (DSN), например ConnectRetryCount=2;ConnectRetryInterval=5. Эти настройки не повторяют выполнение сбойной транзакции и не повторяют первоначальный вызов подключения приложения.
Для повторных попыток при временных ошибках на уровне приложения, возникающих во время выполнения запроса (которые не покрываются механизмом устойчивости простаивающих подключений), см. шаблон повторных попыток в Microsoft Drivers for PHP for SQL Server.
Выберите политику восстановления
Восстановление при простое соединения сохраняет сессию, допускающую восстановление, чтобы приложение могло продолжать использовать свой PHP-дескриптор соединения. Это не общая инструкция для повторного выполнения команды.
| Failure | Справляется ли с этим устойчивость соединения в режиме простоя? | Действие с заявкой |
|---|---|---|
| Не удается установить первоначальное подключение | No. Нет существующего сеанса для восстановления. | Изучите ошибку подключения. Повторяйте операцию только при временном сбое с ограниченной политикой. |
| Разрывается неактивное, но восстанавливаемое соединение | ODBC может попытаться восстановить соединение, когда соединение будет использовано снова. | Установите достаточно большой тайм-аут операции для запланированных попыток восстановления; всё же обрабатывайте окончательный сбой. |
| Инструкция завершается с ошибкой во время выполнения | Не рассчитывайте на восстановление или повторное воспроизведение. | Проверьте ошибку и определите, завершена ли операция. Повторяйте чтение только если повторять это приемлемо для вашего приложения. |
| Соединение разрывается во время транзакции | Не думайте, что транзакция может быть продолжена. | Откажитесь от сломанной сессии и определите, была ли транзакция совершённой, прежде чем принять решение о следующей попытке. |
| Клиент явно закрывает своё PHP-соединение | No. Закрытие дескриптора — это очистка приложения, а не отключение по бездействию. | Откройте новое соединение, если потребуется дополнительная работа. |
Поддерживайте согласованные интервалы восстановления и сроки запросов. Например, программы используют две попытки повторного подключения с интервалом в 5 секунд и 30-секундным тайм-аутом оператора. Попытки подключения тоже занимают время; умножение счёта на интервал не гарантирует полную длительность восстановления. Последний тайм-аут всё равно требует обработки ошибок.
Замечания
Устойчивость соединения действует только при простое. Сбой во время транзакции не приводит к повторному подключению. Следующие состояния сессии также препятствуют повторному подключению:
- Временные столы.
- Глобальные и локальные курсоры.
- Контекст транзакций и блокировки транзакций на уровне сессии.
- Блокировки приложений.
-
EXECUTE AS/REVERTКонтекст безопасности. - Автоматизированные дескрипторы Object Linking and Embedding (OLE).
- Идентификаторы подготовленного расширяемого языка разметки (XML).
- Флаги трассировки.
Пример
Каждая программа открывает два подключения к тестовому экземпляру SQL Server. Второе соединение завершает первую сессию с помощью KILL. Последующий запрос к первому соединению тестирует восстановление простаивающего соединения ODBC. Запрос использует встроенные строки значений, поэтому настройка таблицы не требуется. Программы освобождают каждый оператор перед разрывом соединения и проверяют как значения восстановленного запроса, так и изменённое время входа на сервер. Сам по себе идентификатор сессии не является доказательством восстановления, потому что SQL Server может использовать его повторно.
Caution
Запускайте этот пример только против тестового экземпляра. Для завершения сеанса с помощью KILL учётной записи требуется разрешение. Код завершает только тот сеанс, который он только что открыл. Не подставляйте ID сессии другого приложения.
Установите SQL_SERVER, SQL_DATABASE, SQL_USER и SQL_PASSWORD в окружении процесса. Настройте доверенный серверный сертификат, имя которого соответствует SQL_SERVER. В этом примере используется аутентификация SQL к тестовому экземпляру SQL Server. Это не пример аутентификации ни Fabric, ни управляемой идентификации.
Храните фактическое имя базы данных, имя пользователя и пароль в переменных среды. Каждая программа экранирует эти значения один раз для обработки соединений драйвера PHP в стиле ODBC, удваивая закрывающие брекеты и заключая каждое значение в скобки. Оба соединения повторно используют экранированные значения.
SQLSRV
Сохраните программу как recover-sqlsrv.php, и запустите php recover-sqlsrv.php. Вспомогательная функция генерирует исключение при сбое SQLSRV; она не повторяет ни одну из инструкций приложения.
<?php
function requireSqlsrv($result)
{
if ($result === false) {
throw new RuntimeException(print_r(sqlsrv_errors(), true));
}
return $result;
}
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$connectionInfo = [
'Database' => $config['SQL_DATABASE'],
'UID' => $config['SQL_USER'],
'PWD' => $config['SQL_PASSWORD'],
'Encrypt' => true,
'TrustServerCertificate' => false,
'LoginTimeout' => 30,
'ConnectRetryCount' => 2,
'ConnectRetryInterval' => 5,
];
$conn = requireSqlsrv(sqlsrv_connect($config['SQL_SERVER'], $connectionInfo));
$breaker = null;
try {
$breaker = requireSqlsrv(sqlsrv_connect($config['SQL_SERVER'], $connectionInfo));
$sessionQuery = "SELECT @@SPID AS SessionId, "
. "CONVERT(varchar(33), login_time, 126) AS LoginTime "
. "FROM sys.dm_exec_sessions WHERE session_id = @@SPID";
$idStatement = requireSqlsrv(sqlsrv_query($conn, $sessionQuery));
$before = requireSqlsrv(sqlsrv_fetch_array($idStatement, SQLSRV_FETCH_ASSOC));
sqlsrv_free_stmt($idStatement);
if (!is_array($before) || !is_int($before['SessionId']) || $before['SessionId'] <= 0) {
throw new RuntimeException('Expected the current session identity.');
}
$spid = $before['SessionId'];
$query = 'SELECT id FROM (VALUES (1), (2), (3)) AS Demo(id) ORDER BY id';
$options = ['Scrollable' => SQLSRV_CURSOR_CLIENT_BUFFERED, 'QueryTimeout' => 30];
$stmt = requireSqlsrv(sqlsrv_query($conn, $query, [], $options));
if (requireSqlsrv(sqlsrv_num_rows($stmt)) !== 3) {
throw new RuntimeException('Expected three rows before the disconnect.');
}
sqlsrv_free_stmt($stmt);
// KILL doesn't accept a parameter marker; $spid is a checked server-returned integer.
$kill = requireSqlsrv(sqlsrv_query($breaker, 'KILL ' . $spid));
sqlsrv_free_stmt($kill);
// Allow the simulated disconnect to reach the client before the next query.
sleep(1);
$stmt = requireSqlsrv(sqlsrv_query($conn, $query, [], $options));
$ids = [];
while (($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) !== null) {
$row = requireSqlsrv($row);
$ids[] = $row['id'];
}
if ($ids !== [1, 2, 3]) {
throw new RuntimeException('Unexpected recovered rows or PHP types.');
}
sqlsrv_free_stmt($stmt);
$idStatement = requireSqlsrv(sqlsrv_query($conn, $sessionQuery));
$after = requireSqlsrv(sqlsrv_fetch_array($idStatement, SQLSRV_FETCH_ASSOC));
sqlsrv_free_stmt($idStatement);
if (!is_array($after) || $after['LoginTime'] === $before['LoginTime']) {
throw new RuntimeException('Expected a new physical session.');
}
echo "Recovered connection: 3 matching rows; login time changed.\n";
} finally {
if (is_resource($breaker)) {
sqlsrv_close($breaker);
}
sqlsrv_close($conn);
}
PDO_SQLSRV
Сохраните эту отдельную программу как recover-pdo.php, и запустите php recover-pdo.php в той же среде и с теми же правами. Он явно устанавливает получение числовых данных, чтобы проверки для целых чисел совпадали с программой SQLSRV. PDO не имеет вызова sqlsrv_errors(); при сбоях операций выбрасывается PDOException в режиме исключений.
<?php
$config = [];
foreach (['SQL_SERVER', 'SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name before running this example.");
}
$config[$name] = $value;
}
foreach (['SQL_DATABASE', 'SQL_USER', 'SQL_PASSWORD'] as $name) {
$config[$name] = '{' . str_replace('}', '}}', $config[$name]) . '}';
}
$server = $config['SQL_SERVER'];
$database = $config['SQL_DATABASE'];
$dsn = "sqlsrv:Server=$server;Database=$database;Encrypt=true;"
. "TrustServerCertificate=false;LoginTimeout=30;"
. "ConnectRetryCount=2;ConnectRetryInterval=5";
$attributes = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE => true,
PDO::SQLSRV_ATTR_QUERY_TIMEOUT => 30,
];
$pdo = new PDO($dsn, $config['SQL_USER'], $config['SQL_PASSWORD'], $attributes);
$breaker = null;
$stmt = null;
try {
$breaker = new PDO($dsn, $config['SQL_USER'], $config['SQL_PASSWORD'], $attributes);
$sessionQuery = "SELECT @@SPID AS SessionId, "
. "CONVERT(varchar(33), login_time, 126) AS LoginTime "
. "FROM sys.dm_exec_sessions WHERE session_id = @@SPID";
$stmt = $pdo->query($sessionQuery);
$before = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = null;
if (!is_array($before) || !is_int($before['SessionId']) || $before['SessionId'] <= 0) {
throw new RuntimeException('Expected the current session identity.');
}
$spid = $before['SessionId'];
$query = 'SELECT id FROM (VALUES (1), (2), (3)) AS Demo(id) ORDER BY id';
$stmt = $pdo->query($query);
if ($stmt->fetchAll(PDO::FETCH_COLUMN) !== [1, 2, 3]) {
throw new RuntimeException('Unexpected initial rows or PHP types.');
}
$stmt = null;
// KILL requires a checked integer, not a parameter marker.
$stmt = $breaker->query('KILL ' . $spid);
$stmt = null;
sleep(1);
$stmt = $pdo->query($query);
if ($stmt->fetchAll(PDO::FETCH_COLUMN) !== [1, 2, 3]) {
throw new RuntimeException('Unexpected recovered rows or PHP types.');
}
$stmt = null;
$stmt = $pdo->query($sessionQuery);
$after = $stmt->fetch(PDO::FETCH_ASSOC);
if (!is_array($after) || $after['LoginTime'] === $before['LoginTime']) {
throw new RuntimeException('Expected a new physical session.');
}
echo "Recovered connection: 3 matching rows; login time changed.\n";
} finally {
$stmt = null;
$breaker = null;
$pdo = null;
}
Ожидаемый результат от любой из программ при восстановлении соединения ODBC:
Recovered connection: 3 matching rows; login time changed.
Тест завершает намеренно бездействующую сессию. Он не тестирует разделение сети, переключение при отказе сервиса, активное восстановление транзакции, повторное воспроизведение операций записи или ограничение скорости со стороны Azure. Не воспринимайте успешное прохождение проверки как доказательство того, что каждая прерванная операция приложения безопасна для повторения.
Диагностировать неудачное восстановление
| Симптом | Проверка |
|---|---|
KILL запрещён |
Используйте разрешённый идентификатор теста или попросите администратора выполнить контролируемый тест. Не подставляйте другой идентификатор сессии. |
| Первый запрос уже завершается с ошибкой | Исправьте соединение, сертификат, запрос и разрешения перед тестированием восстановления. |
Запрос после KILL завершается с ошибкой |
Проверьте ConnectRetryCount, тайм-аут запроса, диагностику ODBC и можно ли восстановить состояние сеанса. Не скрывайте ошибку с помощью безусловной повторной попытки. |
| Временная таблица исчезает после открытия нового соединения | Заменяющее соединение, созданное приложением, — это не старая сессия. Перепроектировать работу, зависящую от сессии, вместо того чтобы предполагать автоматическое восстановление состояний. |
| Запись возвращает ошибку соединения | Определите результат перед повторной попыткой. Одна ошибка клиента не доказывает, что сервер откатил её назад. |
Храните диагностику в защищённых логах. SQLSRV предоставляет коллекцию ошибок, которая может содержать более одной записи; PDO предоставляет данные о SQLSTATE и драйвере через исключение. Записывайте операцию и её настройки тайм-аута без записи паролей или токенов. Для получения дополнительной информации см. раздел «Активность логирования».
Применимость целей
ODBC и SQL-цель выполняют автоматическое повторное подключение. PHP передаёт настройки и сообщает об успехе или неудаче. Он не воссоздаёт временные объекты, не восстанавливает переменные вашего приложения и не воспроизводит транзакцию.
Управляемые программы используют SQL Server и требуют разрешения на завершение собственной тестовой сессии через второе соединение. База данных SQL Azure и SQL Database в Fabric имеют собственные разрешения службы и требования к аутентификации. Не рассматривайте эти программы как готовые тесты для Управляемый экземпляр SQL Azure, Azure Synapse Analytics или Warehouse in Fabric и не делайте выводы об их поддержке восстановления по успешному соединению. Проверьте поддержку восстановления выбранного сервиса, прежде чем на неё полагаться. Warehouse и SQL база данных в Fabric остаются отдельными целями.