Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Odolnost připojení umožňuje ovladači Microsoft ODBC pro SQL Server obnovit přerušené nečinné připojení, když server a stav relace podporují obnovu. Rozšíření PHP předávají ConnectRetryCount a ConnectRetryInterval rozhraní ODBC; neimplementují protokol pro opětovné připojení. Dokumentace ODBC uvádí podporu pro verze SQL Server 2014 a novější, Azure SQL Database a SQL databázi v Microsoft Fabric. Neodvozujte podporu Warehouse z podpory SQL databáze ve Fabric.
| Keyword | Hodnoty | Výchozí | Description |
|---|---|---|---|
ConnectRetryCount |
Celé číslo mezi 0 a 255 (včetně) | 1 | Maximální počet pokusů o obnovení přerušeného připojení před vzdáním se. Hodnota 0 zakáže opětovné připojení. |
ConnectRetryInterval |
Celé číslo mezi 1 a 60 (včetně) | 10 | Počet sekund čekání mezi pokusy. Ignorováno, pokud ConnectRetryCount je 0. |
Časový limit dotazu příkazu omezuje, jak dlouho může ODBC strávit obnovou nečinného spojení během vykonání. Nastavte QueryTimeout v možnostech příkazu SQLSRV nebo PDO::SQLSRV_ATTR_QUERY_TIMEOUT pro PDO_SQLSRV.
LoginTimeout určuje navázání spojení; jeho zvýšení neprodlužuje rozpočet na obnovu příkazu.
Nastavte stejná klíčová slova pro opakované pokusy v poli možností připojení SQLSRV, například ["ConnectRetryCount" => 2, "ConnectRetryInterval" => 5], nebo v názvu zdroje dat PDO_SQLSRV (DSN), například ConnectRetryCount=2;ConnectRetryInterval=5. Tato nastavení neopakují neúspěšnou transakci ani znovu nezkoušejí počáteční požadavek aplikace na připojení.
Pro opakované pokusy na úrovni aplikace v případě přechodných chyb, ke kterým dochází během provádění dotazu (což odolnost nečinného připojení nepokrývá), viz vzor opakování v Microsoft Drivers for PHP for SQL Server.
Vyberte zásady obnovení
Obnova nečinného připojení zachovává obnovitelnou relaci, takže aplikace může pokračovat v používání svého PHP connection handle. Není to obecný pokyn k opětovnému provedení příkazu.
| Selhání | Zvládne to odolnost nečinného připojení? | Akce aplikace |
|---|---|---|
| Počáteční spojení nelze navázat | No. Neexistuje žádná relace k obnovení. | Prohlédněte si chybu připojení. Zkuste znovu pouze při přechodném selhání s omezenou politikou. |
| Nečinné, obnovitelné spojení přeruší | ODBC se může pokusit o obnovu, když je spojení znovu použito. | Nechte dostatečný časový limit příkazů pro zamýšlené pokusy o obnovení; stále zvládněte konečné selhání. |
| Instrukce selže během vykonávání | Nepředpokládejte obnovu nebo opakování. | Prohlédněte chybu a zjistěte, zda operace byla dokončena. Operaci čtení opakujte pouze tehdy, pokud je její opakování pro vaši aplikaci přijatelné. |
| Během transakce dojde k přerušení spojení | Nepředpokládejte, že transakce může pokračovat. | Opustit nefunkční relaci a zjistit, zda transakce byla provedena, než se rozhodneš pro další pokus. |
| Klient explicitně ukončí své PHP připojení | No. Zavření rukojeti je čištění aplikace, ne nečinné odpojení. | Otevřete nové spojení, pokud je potřeba provést další kroky. |
Udržujte intervaly zotavení a časové limity dotazů konzistentní. Například programy používají dva pokusy o opětovné připojení s 5sekundovým intervalem a 30sekundový časový limit příkazu. Pokusy o připojení také trvají čas; násobení počtu intervaly není zaručená celková doba obnovy. Poslední timeout stále vyžaduje řešení chyb.
Poznámky
Odolnost připojení vůči výpadkům platí pouze tehdy, když je spojení nečinné. Selhání během transakce nespustí opětovné připojení. Následující stavy relace také brání opětovnému připojení:
- Dočasné stoly.
- Globální a lokální kurzory.
- Transakční kontext a zámky transakcí na úrovni relace.
- Zámky aplikace
-
EXECUTE AS/REVERTBezpečnostní kontext. - Automatizace Object Linking and Embedding (OLE) zajišťuje.
- Připravené popisovače Extensible Markup Language (XML).
- Příznaky trasování.
Example
Každý program otevírá dvě připojení k testovací instanci SQL Server. Druhé spojení ukončuje první seanci s KILL. Následný dotaz na první spojení otestuje obnovení nečinného připojení ODBC. Dotaz používá vložené řádky, takže není potřeba žádné nastavení tabulky. Programy uvolní každý příkaz SQL před přerušením spojení a ověří jak hodnoty obnoveného dotazu, tak změněný čas přihlášení k serveru. Samotné ID relace není důkazem obnovení, protože ho SQL Server může znovu použít.
Caution
Spusť tento příklad pouze na testovací instanci. Přihlašovací účet potřebuje oprávnění k ukončení relace pomocí KILL. Kód ukončí pouze relaci, kterou právě otevřel. Nenahrazujte ID relace jiné aplikace.
Nastavte SQL_SERVER, SQL_DATABASE, SQL_USER, a SQL_PASSWORD v procesním prostředí. Nakonfigurujte důvěryhodný serverový certifikát, jehož název odpovídá SQL_SERVER. Tento příklad používá SQL autentizaci na testovací instanci SQL Server. Nejde o příklad ověřování pomocí Fabric ani spravované identity.
Uložte skutečné jméno databáze, uživatelské jméno a heslo do proměnných prostředí. Každý program tyto hodnoty jednou ošetří pro zpracování připojení ve stylu ODBC v ovladači PHP tím, že zdvojnásobí zavírací závorky a každou hodnotu uzavře do závorek. Obě spojení znovu používají escapované hodnoty.
SQLSRV
Uložte program jako recover-sqlsrv.php a spusťte php recover-sqlsrv.php. Helper při selhání SQLSRV vyvolá výjimku; nezkouší znovu žádné příkazy aplikace.
<?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
Uložte tento samostatný program jako recover-pdo.php a spusťte php recover-pdo.php se stejným prostředím a oprávněními. Nastavuje numerické načítání explicitně tak, aby celočíselné aserce odpovídaly programu SQLSRV. PDO nemá sqlsrv_errors() volání; při selhání operace je vyhozena PDOException v režimu výjimky.
<?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;
}
Očekávaný výstup z kteréhokoli programu při obnovení spojení ODBC:
Recovered connection: 3 matching rows; login time changed.
Test ukončí záměrně nečinnou seanci. Netestuje rozdělení sítě, failover služby, aktivní obnovu transakcí, opakování zápisů ani Azure throttling. Nechápejte úspěšné provedení jako důkaz, že každá přerušená operace aplikace je bezpečná k opakování.
Diagnostikujte neúspěšné obnovení
| Symptom | Zaškrtnutí |
|---|---|
KILL byl(a) zamítnut(a) |
Použijte povolenou testovací identitu nebo požádejte administrátora, aby provedl kontrolovaný test. Nenahrazujte ID relace jiným ID. |
| První dotaz už selhal | Před testováním obnovy opravte spojení, certifikát, dotaz a oprávnění. |
Dotaz po KILL selže |
Zkontrolujte ConnectRetryCount, časový limit dotazu, diagnostiku ODBC a zda je stav relace obnovitelný. Neskrývejte chybu bezpodmínečným opakováním pokusu. |
| Dočasná tabulka zmizí po otevření nového spojení | Nahrazující připojení vytvořené aplikací není stará relace. Přepracujte práci závislou na relaci namísto předpokladu, že se stav obnoví automaticky. |
| Zápis vrací chybu spojení | Zjistěte jeho výsledek před dalším pokusem. Chyba klienta sama o sobě nedokazuje, že server ji zrušil. |
Uchovávejte diagnostiku v chráněných logech. SQLSRV zpřístupňuje kolekci chyb, která může obsahovat více než jeden záznam; PDO poskytuje SQLSTATE a podrobnosti o ovladači prostřednictvím výjimky. Nahrávejte operaci a její nastavení časového limitu bez zaznamenávání hesel nebo tokenů. Pro více informací viz Aktivita zaznamenávání.
Použitelnost cíle
ODBC a cílový objekt SQL provádějí automatické znovupřipojení. PHP předává nastavení a hlásí úspěch nebo neúspěch. Nevytváří dočasné objekty, neobnovuje proměnné vaší aplikace ani nepřehrání transakce.
Řízené programy používají SQL Server a vyžadují povolení ukončit vlastní testovací relaci prostřednictvím druhého připojení. Azure SQL Database a SQL database in Fabric mají vlastní oprávnění k službám a požadavky na ověřování. Nepovažujte tyto programy za drop-in testy pro Azure SQL Managed Instance, Azure Synapse Analytics nebo Warehouse in Fabric, ani nevyvozujte jejich podporu obnovy z toho, že běžné připojení uspěje. Než se na ni spolehnete, zkontrolujte podporu obnovy vybrané služby. Warehouse a databáze SQL ve Fabric zůstávají odlišnými cíli.