Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anslutningsåterhämtning gör att Microsoft ODBC-drivrutinen för SQL Server kan återställa en trasig inaktiv anslutning när servern och sessionstillståndet stödjer återställning. PHP-tilläggen skickar ConnectRetryCount och ConnectRetryInterval till ODBC; de implementerar inte återanslutningsprotokollet. ODBC-dokumentationen listar stöd för SQL Server 2014 och senare versioner, Azure SQL Database och SQL-databas i Microsoft Fabric. Utgå inte från att stöd för SQL-databas i Fabric innebär stöd för Warehouse.
| Keyword | Värden | Förinställning | Description |
|---|---|---|---|
ConnectRetryCount |
Heltal mellan 0 och 255 (inklusive) | 1 | Det maximala antalet försök att återupprätta en bruten anslutning innan du ger upp. Ett värde av 0 avstänger återanslutning. |
ConnectRetryInterval |
Heltal mellan 1 och 60 (inklusive) | 10 | Antalet sekunder att vänta mellan försöken. Ignoreras om ConnectRetryCount är 0. |
Satsens frågetimeout begränsar hur lång tid ODBC kan tillbringa på att återställa en inaktiv anslutning under exekveringen. Ställ QueryTimeout in i SQLSRV-satsalternativ eller PDO::SQLSRV_ATTR_QUERY_TIMEOUT för PDO_SQLSRV.
LoginTimeout styr upprättandet av en anslutning; att öka den förlänger inte satsens återställningsbudget.
Sätt samma återförsöksnyckelord i en SQLSRV-anslutningsoptionsarray, såsom ["ConnectRetryCount" => 2, "ConnectRetryInterval" => 5], eller i ett PDO_SQLSRV datakällnamn (DSN), såsom ConnectRetryCount=2;ConnectRetryInterval=5. Dessa inställningar upprepar inte en misslyckad transaktion och försöker inte igen med applikationens initiala anslutningsanrop.
För återförsök på applikationsnivå av tillfälliga fel som uppstår medan en fråga körs (vilket återhämtning för inaktiva anslutningar inte täcker), se återförsöksmönstret i Microsoft Drivers for PHP for SQL Server.
Välj återhämtningspolicyn
Återställning av inaktiva anslutningar bevarar en återställbar session så att applikationen kan fortsätta använda sitt PHP-anslutningshandtag. Det är inte en allmän instruktion att utföra ett kommando igen.
| Failure | Kan återhämtning för inaktiva anslutningar hantera det? | Applikationsåtgärd |
|---|---|---|
| Den initiala anslutningen kan inte upprättas | No. Det finns ingen session att återställa. | Inspektera anslutningsfelet. Försök endast igen vid ett tillfälligt fel med en begränsad policy. |
| En inaktiv, återställbar anslutning bryts | ODBC kan försöka återställa anslutningen när anslutningen används igen. | Tillåt en tillräckligt lång timeoutgräns för uttrycket för de avsedda återställningsförsöken; hantera fortfarande ett slutgiltigt misslyckande. |
| Ett påstående misslyckas under exekveringen | Anta inte återhämtning eller omspelning. | Inspektera felet och avgör om operationen slutfördes. Försök göra om en läsning endast om det är acceptabelt för din applikation att upprepa den. |
| Förbindelsen bryts under en transaktion | Anta inte att transaktionen kan fortsätta. | Avbryt den trasiga sessionen och avgör om transaktionen har gått igenom innan du bestämmer dig för ett nytt försök. |
| Klienten stänger uttryckligen sin PHP-anslutning | No. Att stänga en referens är rensning i programmet, inte en inaktivitetsfrånkoppling. | Öppna en ny anslutning om mer arbete behövs. |
Håll återhämtningsintervaller och frågedeadlines konsekventa. Till exempel använder programmen två återanslutningsförsök med ett 5-sekunders intervall och en 30-sekunders satstimeout. Anslutningsförsök tar också tid; att multiplicera antalet försök med intervallet är inte en garanterad total återställningstid. En sista timeout kräver fortfarande felhantering.
Anmärkningar
Anslutningstålighet gäller bara när anslutningen är inaktiv. Ett fel under en transaktion utlöser inte återanslutning. Följande sessionsstater förhindrar också återanslutning:
- Tillfälliga bord.
- Globala och lokala kursorer.
- Transaktionskontext och sessionsnivå-transaktionslås.
- Applikeringslås.
-
EXECUTE AS/REVERTsäkerhetskontext. - Objektlänkning och inbäddning (OLE) automationsreferenser.
- Förberedda Extensible Markup Language (XML)-referenser.
- Spårningsflaggor.
Example
Varje program öppnar två anslutningar till en testinstans av SQL Server. Den andra anslutningen avslutar den första sessionen med KILL. En efterföljande fråga på den första anslutningen testar ODBC:s återhämtning från inaktiva anslutningar. Frågesatsen använder inline-rader, så ingen tabelluppsättning krävs. Programmen släpper varje sats innan de bryter anslutningen och verifierar både värdena i den återställda frågan och en ändrad inloggningstid för servern. Ett sessions-ID i sig är inte bevis på återställning eftersom SQL Server kan återanvända det.
Caution
Kör detta exempel endast mot en testinstans. Inloggningskontot behöver tillstånd för att avsluta en session med KILL. Koden avslutar bara den session den just öppnat. Ersätt inte sessions-ID:t med ett från en annan applikation.
Sätt SQL_SERVER, SQL_DATABASE, SQL_USER, och SQL_PASSWORD i processmiljön. Konfigurera ett betrott servercertifikat vars namn matchar SQL_SERVER. Detta exempel använder SQL-autentisering till en testinstans av SQL Server. Det är inte ett exempel på Fabric- eller autentisering med hanterad identitet.
Spara det faktiska databasnamnet, användarnamnet och lösenordet i miljövariablerna. Varje program escaperar dessa värden en gång för PHP-drivrutinens ODBC-liknande anslutningshantering genom att dubbla stängande klamrar och omsluta varje värde med klamrar. Båda kopplingarna återanvänder de escapade värdena.
SQLSRV
Spara programmet som recover-sqlsrv.php, och kör php recover-sqlsrv.php. Hjälparen utlöser ett undantag vid SQLSRV-fel; den försöker inte köra om någon applikationssats.
<?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
Spara detta separata program som recover-pdo.php, och kör php recover-pdo.php med samma miljö och behörigheter. Den sätter numerisk hämtning explicit så att heltalsassertionerna matchar SQLSRV-programmet. PDO har inget anrop till sqlsrv_errors(); operationsfel kastar PDOException i undantagsläge.
<?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;
}
Förväntad utgång från något av programmen när ODBC återställer anslutningen:
Recovered connection: 3 matching rows; login time changed.
Testet avslutar en avsiktligt inaktiv session. Den testar inte nätverkspartition, servicefailover, aktiv transaktionsåterställning, skrivreplay eller Azure-throttling. Tolka inte ett pass som bevis på att varje avbruten applikationsoperation är säker att upprepa.
Diagnostisera en misslyckad återhämtning
| Symtom | Kontrollera |
|---|---|
KILL har nekats |
Använd en tillåten testidentitet eller be administratören utföra det kontrollerade testet. Ersätt inte med ett annat sessions-ID. |
| Den första frågan misslyckas redan | Åtgärda anslutningen, certifikatet, förfrågan och behörigheterna innan du testar återställningen. |
Fråga efter KILL misslyckas |
Kontrollera ConnectRetryCount, frågetimeout, ODBC-diagnostik och om sessionstillståndet går att återställa. Dölj inte felet med ett ovillkorligt omförsök. |
| En tillfällig tabell försvinner efter att en ny anslutning öppnats | En applikationsskapad ersättningsanslutning är inte den gamla sessionen. Omdesigna sessionsberoende arbete istället för att anta automatisk tillståndsåterskapande. |
| En skrivåtgärd returnerar ett anslutningsfel | Fastställ dess utfall innan du försöker igen. Klientfelet i sig bevisar inte att servern rullade tillbaka det. |
Spara diagnostik i skyddade loggar. SQLSRV exponerar en felsamling som kan innehålla mer än en post; PDO tillhandahåller SQLSTATE- och drivrutinsdetaljer via undantaget. Spela in operationen och dess timeout-inställningar utan att registrera lösenord eller token. För mer information, se Loggningsaktivitet.
Måltillämplighet
ODBC och SQL-målet utför den automatiska återanslutningen. PHP skickar inställningar och rapporterar framgång eller misslyckande. Den återskapar inte tillfälliga objekt, återställer inte applikationens variabler eller spelar upp en transaktion igen.
De kontrollerade programmen använder SQL Server och kräver tillstånd för att avsluta sin egen testsession via en andra anslutning. Azure SQL Database och SQL Database in Fabric har egna tjänstebehörigheter och autentiseringskrav. Behandla inte dessa program som drop-in-tester för Azure SQL Managed Instance, Azure Synapse Analytics eller Warehouse in Fabric, och dra inte slutsatsen om deras återställningsstöd utifrån att en vanlig anslutning lyckas. Kontrollera den valda tjänstens återställningssupport innan du förlitar dig på den. Warehouse och SQL-databas i Fabric förblir separata mål.