Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
La resilienza della connessione consente al driver Microsoft ODBC per SQL Server di ripristinare una connessione inattiva rotta quando il server e lo stato della sessione supportano il recupero. Le estensioni PHP passano ConnectRetryCount e ConnectRetryInterval a ODBC; non implementano il protocollo di riconnessione. La documentazione ODBC elenca il supporto per SQL Server 2014 e versioni successive, database SQL di Azure e SQL database in Microsoft Fabric. Non dedurre il supporto a Warehouse dal supporto per database SQL in Fabric.
| Parola chiave | Valori | Valore predefinito | Descrizione |
|---|---|---|---|
ConnectRetryCount |
Numero intero compreso tra 0 e 255 inclusi | 1 | Numero massimo di tentativi di ristabilire una connessione interrotta prima di rinunciare. Un valore di 0 disabilita la riconnessione. |
ConnectRetryInterval |
Numero intero compreso tra 1 e 60 inclusi | 10 | Il numero di secondi da aspettare tra un tentativo e l'altro. Ignorato se ConnectRetryCount è 0. |
Il timeout della query dell'istruzione limita quanto tempo ODBC può impiegare a recuperare una connessione inattiva durante l'esecuzione. Imposta QueryTimeout nelle opzioni di istruzioni SQLSRV o PDO::SQLSRV_ATTR_QUERY_TIMEOUT per PDO_SQLSRV.
LoginTimeout controlla l'instaurazione di una connessione; aumentarla non estende il budget di recupero dell'istruzione.
Imposta le stesse parole chiave retry in un array di opzioni di connessione SQLSRV, come ["ConnectRetryCount" => 2, "ConnectRetryInterval" => 5], oppure in un nome di sorgente dati PDO_SQLSRV (DSN), come ConnectRetryCount=2;ConnectRetryInterval=5. Queste impostazioni non rieseguono una transazione non riuscita né ritentano la chiamata iniziale dell'applicazione.
Per il ritentativo a livello applicativo di errori transitori che si verificano mentre una query è in esecuzione (che la resilienza della connessione inattiva non copre), si veda il pattern di ritentazione nei Microsoft Drivers for PHP for SQL Server.
Scegli il criterio di ripristino
Il recupero della connessione inattiva preserva una sessione recuperabile così che l'applicazione possa continuare a utilizzare il suo handle di connessione PHP. Non è un'istruzione generale per eseguire di nuovo un comando.
| Failure | La funzionalità di resilienza delle connessioni inattive gestisce questo caso? | Azione dell'applicazione |
|---|---|---|
| Il collegamento iniziale non può essere stabilito | No. Non esiste una sessione attiva da ripristinare. | Ispeziona l'errore di connessione. Riprova solo in caso di errore transitorio con un criterio limitato. |
| Interruzione di una connessione inattiva e recuperabile | ODBC può tentare il recupero quando la connessione viene nuovamente utilizzata. | Impostare un timeout dell'istruzione sufficientemente lungo per i tentativi di recupero previsti; gestire comunque un errore finale. |
| Un'istruzione fallisce durante l'esecuzione | Non dare per scontati il recupero o la riesecuzione. | Ispezionare l'errore e determinare se l'operazione è completata. Riprova una lettura solo se ripeterla è accettabile per la tua applicazione. |
| Una connessione si interrompe durante una transazione | Non dare per scontato che la transazione possa continuare. | Abbandona la sessione interrotta e verifica se la transazione è stata confermata prima di decidere un altro tentativo. |
| Il client chiude esplicitamente la connessione PHP | No. Chiudere un handle è una pulizia dell'applicazione, non una disconnessione per inattività. | Apri una nuova connessione se è necessario altro lavoro. |
Mantieni gli intervalli di recupero e le scadenze delle query coerenti. Ad esempio, i programmi utilizzano due tentativi di riconnessione con un intervallo di 5 secondi e un timeout di istruzioni di 30 secondi. Anche i tentativi di connessione richiedono tempo; moltiplicare il conteggio per l'intervallo non garantisce una durata totale del ripristino. Un timeout finale richiede comunque la gestione degli errori.
Osservazioni:
La resilienza della connessione si applica solo quando la connessione è inattiva. Un guasto durante una transazione non attiva la riconnessione. I seguenti stati della sessione impediscono anch'essi la riconnessione:
- Tabelle temporanee.
- Cursori globali e locali.
- Contesto delle transazioni e blocchi di transazione a livello di sessione.
- Blocco applicazioni
-
EXECUTE AS/REVERTContesto di sicurezza. - Handle di automazione Object Linking and Embedding (OLE).
- Handle preparati per il Linguaggio di Markup Estensibile (XML).
- Flag di traccia.
Esempio
Ogni programma apre due connessioni a un'istanza di SQL Server di test. La seconda connessione termina la prima sessione con KILL. Una successiva query sulla prima connessione verifica il recupero della connessione inattiva di ODBC. La query utilizza righe in linea, quindi non è necessaria la configurazione di tabelle. I programmi rilasciano ogni istruzione prima di interrompere la connessione e verificano sia i valori della query recuperata sia una modifica dell'orario di accesso al server. Un ID di sessione da solo non è una prova di recupero perché SQL Server può riutilizzarlo.
Caution
Esegui questo esempio solo su un'istanza di test. Il login richiede il permesso per terminare una sessione con KILL. Il codice termina solo la sessione appena aperta. Non sostituire l'ID sessione di un'altra applicazione.
Imposta SQL_SERVER, SQL_DATABASE, SQL_USER, e SQL_PASSWORD nell'ambiente di processo. Configura un certificato server affidabile il cui nome corrisponde a SQL_SERVER. Questo esempio utilizza l'autenticazione SQL per un'istanza di test di SQL Server. Non è un esempio di autenticazione di Fabric o tramite identità gestita.
Memorizza il nome effettivo del database, il nome utente e la password nelle variabili ambientali. Ogni programma esegue l'escape di questi valori una volta per la gestione della connessione in stile ODBC del driver PHP, raddoppiando le parentesi graffe di chiusura e racchiudendo ogni valore tra parentesi graffe. Entrambe le connessioni riutilizzano i valori con escape.
SQLSRV
Salva il programma come recover-sqlsrv.php, ed esegui php recover-sqlsrv.php. La funzione helper genera un'eccezione in caso di errore SQLSRV; non ritenta alcuna istruzione applicativa.
<?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
Salva questo programma separato come recover-pdo.php, ed esegui php recover-pdo.php con lo stesso ambiente e permessi. Imposta esplicitamente il recupero numerico in modo che le asserzioni intere corrispondano al programma SQLSRV. PDO non ha alcuna chiamata sqlsrv_errors(); gli errori operativi generano PDOException in modalità eccezione.
<?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;
}
Output atteso da uno dei due programmi quando ODBC ripristina la connessione:
Recovered connection: 3 matching rows; login time changed.
Il test termina una sessione volutamente inattiva. Non testa una partizione di rete, il failover del servizio, il recupero attivo delle transazioni, la riproduzione della scrittura o il throttling di Azure. Non interpretare un esito positivo come prova che ogni operazione interrotta dell'applicazione è sicura da ripetere.
Diagnosticare un ripristino non riuscito
| Sintomo | Seleziona |
|---|---|
KILL è negato |
Utilizzare un'identità di test consentita oppure chiedere all'amministratore di eseguire il test controllato. Non sostituire con un altro ID di sessione. |
| La prima query fallisce già | Risolvi la connessione, il certificato, la query e i permessi prima di testare il recupero. |
La query dopo KILL fallisce |
Controlla ConnectRetryCount, il timeout della query, la diagnostica ODBC e se lo stato della sessione è recuperabile. Non nascondere l'errore con un ritentativo incondizionato. |
| Una tabella temporanea scompare dopo l'apertura di una nuova connessione | Una connessione sostitutiva creata dall'applicazione non è la vecchia sessione. Riprogettare il lavoro dipendente dalla sessione invece di presumere una ricreazione automatica dello stato. |
| Un'operazione di scrittura restituisce un errore di connessione | Verifica l’esito prima di riprovare. L'errore del client da solo non dimostra che il server l'abbia annullato. |
Conserva i dati diagnostici nei log protetti. SQLSRV espone una raccolta di errori che può contenere più di un record; PDO fornisce SQLSTATE e dettagli del driver tramite l'eccezione. Registra l'operazione e le sue impostazioni di timeout senza registrare password o token. Per ulteriori informazioni, vedi Attività di registrazione.
Applicabilità della destinazione
ODBC e il target SQL effettuano la riconnessione automatica. PHP passa le impostazioni e segnala successo o fallimento. Non ricrea oggetti temporanei, non ripristina le variabili dell'applicazione e non riproduce una transazione.
I programmi controllati utilizzano SQL Server e richiedono il permesso di terminare la propria sessione di test tramite una seconda connessione. database SQL di Azure e SQL Database in Fabric hanno i propri permessi di servizio e requisiti di autenticazione. Non trattare quei programmi come test drop-in per Istanza gestita di SQL di Azure, Azure Synapse Analytics o Warehouse in Fabric, né dedurre il loro supporto di recupero dal fatto che una connessione ordinaria abbia successo. Controlla il supporto al recupero del servizio selezionato prima di affidarti ad esso. Warehouse e il database SQL in Fabric rimangono obiettivi distinti.