Fehlerbehebung der Microsoft-Treiber für PHP für SQL Server

PHP-Treiber herunterladen

Diagnostizieren und beheben Sie häufige Probleme, wenn Sie die Microsoft-Treiber für PHP für SQL Server verwenden, um eine Verbindung mit SQL Server, Azure SQL-Datenbank, Azure SQL Managed Instance und SQL-Datenbank in Microsoft Fabric herzustellen.

Allgemeine Fehler- und Warnmuster finden Sie unter Handhabung von Fehlern und Warnungen. Zur Diagnoseerfassung auf der Treiberseite siehe Protokollierungsaktivität.

Installationsprobleme

Erweiterung nicht geladen

Symptome:

  • phpinfo() listet keinen Abschnitt für sqlsrv oder pdo_sqlsrv auf.
  • PDOException: could not find driver beim Erstellen eines PDO mit dem sqlsrv:-DSN.
  • Fatal error: Uncaught Error: Call to undefined function sqlsrv_connect().

Mögliche Ursachen und Lösungen:

  • Die Erweiterung ist in php.ini nicht aktiviert. Vergewissern Sie sich, dass sowohl extension=sqlsrv als auch extension=pdo_sqlsrv nicht auskommentiert sind. Unter Windows verwenden Sie den vollständigen Dateinamen (extension=php_sqlsrv_84_ts_x64.dll). Details finden Sie unter Laden der Treiber.
  • Falscher Gewindesicherungsbau. Die Treiber-Binärform muss mit der Thread-Sicherheit deines PHP-Builds übereinstimmen (ts für threadsicher, nts für nicht-threadsicher). Führen Sie php -i | grep "Thread Safety" zum Prüfen aus. Laden Sie die passende Binärdatei von der Download-Seite herunter.
  • Microsoft ODBC-Treiber fehlt. Die PHP-Treiber umschließen den Microsoft ODBC Driver für SQL Server. Unter Linux und macOS installiere msodbcsql18 (oder msodbcsql17) mit deinem Paketmanager, bevor du die Erweiterungen lädst. Installiere unter Windows den ODBC-Treiber von der Download-Seite.

Überprüfen Sie eine erfolgreiche Installation:

php -m | grep -i sqlsrv

Du solltest sowohl pdo_sqlsrv als auch sqlsrv in der Ausgabe sehen.

PECL-Installation schlägt auf Linux oder macOS fehl

Symptome:

error: ‘SQL_HANDLE_DBC’ undeclared (first use in this function)
fatal error: 'sql.h' file not found

Korrektur:

Installieren Sie die ODBC-Entwicklungs-Header-Dateien, bevor Sie pecl install ausführen:

  • Ubuntu und Debian: sudo apt-get install unixodbc-dev
  • Red Hat, Fedora und CentOS:sudo dnf install unixODBC-devel
  • Alpine: apk add unixodbc-dev
  • macOS:brew install unixodbc

Versuchen Sie es dann noch einmal:

sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv

Wenn pecl nach der Installation der Header weiterhin fehlschlägt, ist die Build-Toolchain möglicherweise unvollständig. Installiere phpize, re2c, und einen C++-Compiler (build-essential auf Debian und Ubuntu, gcc-c++ make auf Red Hat und Fedora, build-base auf Alpine).

Für den vollständigen Installationspfad siehe Installationsanleitung für Linux und macOS.

Mehrere PHP-Versionen installiert

Symptome:

phpinfo() auf deinem Webserver wird eine PHP-Version angezeigt, aber php -v in der Kommandozeile eine andere, und der Treiber wird nur in einer davon geladen.

Korrektur:

Jede PHP-Version hat ihr eigenes php.ini- und ext-Verzeichnis. Finde die richtige Konfigurationsdatei php --ini aus der Umgebung, der der Treiber fehlt, und füge dort die extension= Zeilen hinzu. Neustarte den Webserver (Apache, Nginx + PHP-FPM oder IIS) nach jeder php.ini Änderung.

Verbindungsprobleme

Keine Verbindung zum Server möglich

Symptome:

SQLSTATE[08001]: [Microsoft][ODBC Driver 18 for SQL Server]TCP Provider: A connection attempt failed
SQLSTATE[HYT00]: [Microsoft][ODBC Driver 18 for SQL Server]Login timeout expired

Mögliche Ursachen und Lösungen:

  • Der Server ist nicht erreichbar. Überprüfen Sie, ob Servername und Port korrekt sind. Vom PHP-Host aus testen Sie die rohe TCP-Konnektivität.

    # Linux and macOS
    nc -vz <server>.database.windows.net 1433
    
    # Windows PowerShell
    Test-NetConnection -ComputerName <server>.database.windows.net -Port 1433
    
  • Firewall blockiert ausgehenden Port 1433. Unternehmensfirewalls und Cloud-NSGs blockieren häufig den ausgehenden Port 1433. Füge eine Ausnahme hinzu oder erlaube die Azure SQL-Datenbank-IP-Bereiche für deine Region.

  • Azure SQL Server Firewall. Füge die öffentliche IP deines Kunden zu den Server-Firewall-Regeln im Azure-Portal hinzu.

  • Benannte Instanz. Für eine benannte Instanz überprüfen Sie, ob der SQL Server Browser-Dienst auf dem Server läuft und dass UDP 1434 offen ist. Oder verbinden Sie sich über den Port statt über den Instanznamen.

Fehler bei der Anmeldung

Symptome:

SQLSTATE[28000]: [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Login failed for user '<user_id>'.

Mögliche Ursachen und Lösungen:

  • SQL-Authentifizierungsmodus deaktiviert. Lokale SQL Server-Instanzen verwenden standardmäßig nur Windows-Authentifizierung. Aktivieren Sie die Authentifizierung im gemischten Modus im SQL Server Management Studio unter Server Properties>Security und starten Sie dann den SQL Server-Dienst neu.
  • Azure SQL credentials format. Azure SQL benötigt den vollständig qualifizierten Benutzernamen (user@servername) beim Verbinden mit Tools, die es nicht automatisch anhängen.
  • Benutzer nicht der Datenbank zugeordnet. Überprüfen Sie, dass der Login eine Benutzerzuordnung in der Zieldatenbank enthält und der Benutzer die erforderlichen Berechtigungen hat.
  • Bevorzuge Microsoft Entra ID. Für Azure SQL, Azure SQL Managed Instance und SQL-Datenbank in Fabric verwenden Sie Microsoft Entra-Authentifizierung (Authentication=ActiveDirectoryMsi, Authentication=ActiveDirectoryServicePrincipal, oder ein Zugriffstoken) anstelle von SQL-Logins. Siehe Verbinden mit der Microsoft Entra-Authentifizierung.

Ungültiger Wert angegeben für das Verbindungszeichenfolge-Attribut 'Authentication'

Symptome:

SQLSTATE[08001]: [Microsoft][ODBC Driver 17 for SQL Server]Invalid value specified for connection string attribute 'Authentication'

Ursache:

Der ODBC-Treiber meldet den Fehler, aber das eigentliche Problem ist, an welchen Treiber PDO_SQLSRV gebunden ist. Wenn das DSN kein Driver= Schlüsselwort enthält und der Host sowohl ODBC 17 als auch ODBC 18 installiert hat, kann PDO_SQLSRV auf die ältere Version binden. Ältere ODBC 17.x-Builds kennen keine neueren Authentication Werte wie ActiveDirectoryServicePrincipal oder ActiveDirectoryDefault, und benötigen sogar ActiveDirectoryMsi ODBC 17.3.1.1 oder eine neuere Version.

Korrektur:

Treiber im DSN anheften:

<?php
$dsn = "sqlsrv:Driver={ODBC Driver 18 for SQL Server};Server=$server;Database=$db;" .
       "Encrypt=true;Authentication=ActiveDirectoryMsi";
$conn = new PDO($dsn, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);

Die in Klammern gesetzte Form ({ODBC Driver 18 for SQL Server}) entfällt den Leerzeichen im Fahrernamen. Die Fehlermeldung selbst nennt immer den gemeldeten Treiber, sodass das Präfix [Microsoft][ODBC Driver 17 for SQL Server] im Fehler der schnellste Weg ist, die falsche Treiberbindung zu bestätigen.

Das ungültige Schlüsselwort 'UID' wurde im DSN-String angegeben

Symptome:

SQLSTATE[IMSSP]: An invalid keyword 'UID' was specified in the DSN string.

Ursache:

PDO_SQLSRV erzwingt eine Liste zulässiger DSN-Schlüsselwörter und akzeptiert UID oder PWD in der DSN nicht. PDO reserviert das zweite und dritte Konstruktorargument dafür, und PDO_SQLSRV übersetzt diese intern in ODBC UID/PWD.

Korrektur:

Übertragen Sie den Benutzernamen (und das Passwort für SQL-Authentifizierung) in den PDO-Konstruktor:

<?php
// SQL authentication.
$dsn = "sqlsrv:Driver={ODBC Driver 18 for SQL Server};Server=$server;Database=$db;Encrypt=true";
$conn = new PDO($dsn, $user, $password);

// User-assigned managed identity. Pass the identity's client ID as $username.
$dsn = "sqlsrv:Driver={ODBC Driver 18 for SQL Server};Server=$server;Database=$db;" .
       "Encrypt=true;Authentication=ActiveDirectoryMsi";
$conn = new PDO($dsn, $clientId, null);

Der prozedurale SQLSRV-Treiber hingegen akzeptiert UID und PWD im Array der Verbindungsoptionen, das an sqlsrv_connect() übergeben wird.

PDO_SQLSRV ignoriert AccessToken im Options-Array stillschweigend

Symptom:

Du verfügst über ein Microsoft Entra-Zugriffstoken (zum Beispiel von az account get-access-token --resource https://database.windows.net/, ManagedIdentityCredential oder ClientSecretCredential) und übergibst es als ['AccessToken' => $token] im vierten Konstruktorargument an PDO_SQLSRV. Der Verbindungsversuch scheitert mit einem verwirrenden Fehler wie Windows logins are not supported in this version of SQL Server oder Login failed for user '', als ob keine Zugangsdaten angegeben worden wären.

Ursache:

Das vierte Konstruktorargument von PDO ist für treiberspezifische Attributkonstanten (ganzzahlige Schlüssel wie PDO::ATTR_ERRMODE) reserviert. PDO verwirft stillschweigend Einträge mit Zeichenfolgenschlüsseln wie AccessToken, sodass PDO_SQLSRV den Token nie zu Gesicht bekommt. Die Verbindung fällt dann auf Windows Integrated Authentication zurück, die der Server ablehnt.

Korrektur:

Verschieben Sie AccessToken in die DSN-Zeichenfolge. Reserviere das Options-Array für PDO::ATTR_* Konstanten.

<?php
$server = '<server>.database.windows.net';
$token  = getenv('SQL_ACCESS_TOKEN');   // raw JWT, no "Bearer " prefix

$dsn = "sqlsrv:Server=$server;Database=<database>;Encrypt=true;AccessToken=$token";
$conn = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Für weitere Beispiele Microsoft Entra Authentifizierung, einschließlich des DSN-Formulars für PDO_SQLSRV, siehe Verbinden mit Microsoft Entra Authentifizierung.

Für prozedurales SQLSRV gehört AccessToken in das connection-info-Array, das an sqlsrv_connect() übergeben wird, welches das unformatierte JWT für Sie in SQL_COPT_SS_ACCESS_TOKEN umschließt:

<?php
$server = '<server>.database.windows.net';
$token  = getenv('SQL_ACCESS_TOKEN');   // raw JWT, no "Bearer " prefix

$connectionInfo = [
    'Database'               => '<database>',
    'AccessToken'            => $token,
    'Encrypt'                => true,
    'TrustServerCertificate' => false,
    'Driver'                 => '{ODBC Driver 18 for SQL Server}',
];

$conn = sqlsrv_connect($server, $connectionInfo);
if ($conn === false) {
    print_r(sqlsrv_errors());
    exit(1);
}

TLS-Zertifikatsfehler

Symptome:

SQLSTATE[08001]: SSL Provider: The certificate chain was issued by an authority that is not trusted
SQLSTATE[08001]: SSL Provider: The target principal name is incorrect

Lösungen:

Bevorzuge ein vertrauenswürdiges Zertifikat. Verwenden Sie TrustServerCertificate=true es nur für lokale Entwicklung gegen einen Server, den Sie steuern.

Für die Entwicklung mit einem selbstsignierten Zertifikat:

<?php
$server   = 'localhost';
$database = '<database>';
$user     = '<user_id>';
$password = '<password>';

$dsn = "sqlsrv:Server=$server;Database=$database;Encrypt=true;TrustServerCertificate=true";
$conn = new PDO($dsn, $user, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Caution

TrustServerCertificate=true Deaktivieren Sie die Validierung von Serverzertifikaten. Nimm dieses Setting niemals in Produktion, Inszenierung oder gemeinsame Umgebungen mit.

Für einen Produktions-Hostnamen, der nicht mit dem Common Name des Zertifikats übereinstimmt (zum Beispiel, wenn die Verbindung über einen Listener hergestellt wird), geben Sie den tatsächlichen Zertifikatssubjektnamen an:

<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;Encrypt=true;HostNameInCertificate=*.database.windows.net;Authentication=ActiveDirectoryMsi";
$conn = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Verbindungstimeout

Symptome:

SQLSTATE[HYT00]: Login timeout expired

Mögliche Ursachen und Lösungen:

  • LoginTimeout nicht festgelegt oder zu niedrig für ein Cold Failover festgelegt. Geben Sie beim Herstellen der Verbindung mit Azure SQL im DSN einen expliziten Wert LoginTimeout (in Sekunden) an. Failover von Failovergruppen und Kaltstartdatenbanken können länger dauern, als ein kurzes clientseitiges Timeout zulässt. Siehe Verbindungsoptionen für die Optionsreferenz.
  • Budget für die Wiederverbindung im Leerlauf abgeschnitten. Wenn Sie ConnectRetryCount und ConnectRetryInterval festlegen, stellen Sie sicher, dass LoginTimeout >= ConnectRetryCount * ConnectRetryInterval. Andernfalls beendet die Login-Timeout die Wiederverbindungsschleife vorzeitig. Siehe Inaktiv-Verbindungs-Resilienz.
<?php
$dsn = "sqlsrv:Driver={ODBC Driver 18 for SQL Server};Server=<server>.database.windows.net;Database=<database>;" .
       "Encrypt=true;LoginTimeout=90;ConnectRetryCount=5;ConnectRetryInterval=15;" .
       "Authentication=ActiveDirectoryMsi";
$conn = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Abfrageausführungsprobleme

Stille Fehler bei PDO

Symptom:

Ein PDO::exec()- oder PDOStatement::execute()-Aufruf gibt false zurück, löst aber keine Ausnahme aus.

Korrektur:

Bei PHP 8.0 und höheren Versionen ist der Standard-PDO-Fehlermodus PDO::ERRMODE_EXCEPTION. Wenn ein Aufruf false zurückgibt, ohne eine Ausnahme auszulösen, hat die Anwendung den Modus in PDO::ERRMODE_SILENT oder PDO::ERRMODE_WARNING geändert. Setze es zurück in den Ausnahmemodus, damit Fehler Ausnahmen auslösen:

<?php
$conn = new PDO($dsn, $user, $password, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);

Wenn du den Modus global nicht ändern kannst, überprüfe $conn->errorInfo() (oder $stmt->errorInfo()) nach jedem Anruf. Das Array enthält [SQLSTATE, driver code, driver message].

Ungültiger Objektname

Symptome:

SQLSTATE[42S02]: [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Invalid object name 'Products'.

Mögliche Ursachen und Lösungen:

  • Falscher Datenbankkontext. Überprüfen Sie dies mit einer kurzen Frage:

    <?php
    $stmt = $conn->query("SELECT DB_NAME()");
    echo $stmt->fetchColumn();
    
  • Fehlende Schema-Qualifikation. Verwenden Sie vollständig qualifizierte Namen, um nicht auf das Standardschema des Anrufers zu vertrauen:

    SELECT * FROM dbo.Products;
    
  • Unterscheidung nach Groß-/Kleinschreibung. Datenbanken, die mit einer fallabhängigen Sortierung erstellt werden, behandeln products und Products als verschiedene Objekte. Achten Sie auf die genaue Groß-/Kleinschreibung in der Tabellendefinition.

Falsche Anzahl von Parametern

Symptome:

SQLSTATE[HY093]: Invalid parameter number
SQLSTATE[07002]: COUNT field incorrect or syntax error

Korrektur:

Für PDO_SQLSRV muss die Anzahl der ?-Platzhalter mit der Anzahl der Werte übereinstimmen, die du an execute() übergibst, und jedes ? bindet genau einen skalaren Wert (kein Array). Für benannte Parameter muss jeder :name im SQL im Array erscheinen und umgekehrt.

<?php
$stmt = $conn->prepare(
    "SELECT * FROM dbo.Products WHERE CategoryID = ? AND ListPrice > ?"
);
$stmt->execute([1, 50.0]);
foreach ($stmt as $row) {
    // ...
}

Für SQLSRV übergeben Sie das Parameterarray an sqlsrv_query() oder sqlsrv_prepare():

<?php
$stmt = sqlsrv_query(
    $conn,
    "SELECT * FROM dbo.Products WHERE CategoryID = ? AND ListPrice > ?",
    [1, 50.0]
);
if ($stmt === false) {
    die(print_r(sqlsrv_errors(), true));
}

Für eine umfassendere Einführung in die Parameterbindung siehe Parametrisierte Abfragen ausführen.

Emulierte PDO-Prepares maskieren Fehler

Symptome:

Eine Anweisung läuft erfolgreich auf einer Verbindung, wirft aber einen Syntaxfehler bei einer anderen Verbindung, die denselben Abfragetext verwendet.

Ursache:

PDO_SQLSRV unterstützt sowohl emulierte als auch native vorbereitete Anweisungen. Emulierte Prepares (PDO::ATTR_EMULATE_PREPARES = true) interpolieren Parameter clientseitig. Native Prepares (false) senden die Abfrage und die Parameter separat an den Server. Das Verhalten unterscheidet sich für TOP (?), Tabellenwertparameter und einige Randfälle bei der Typkoersion.

Korrektur:

Bevorzugen Sie native Prepares in der Produktion. Legen Sie PDO::ATTR_EMULATE_PREPARES => false beim Verbindungsaufbau fest, damit das Verhalten in allen Umgebungen konsistent ist:

<?php
$conn = new PDO($dsn, null, null, [
    PDO::ATTR_ERRMODE          => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

Einzelheiten dazu, wann die einzelnen Modi verwendet werden sollten, finden Sie unter PDO::prepare.

Datentypprobleme

Unicode-Zeichen erscheinen als ? oder verzerrt

Symptome:

Zeilen, die PHP schreibt, enthalten Fragezeichen oder Ersatzzeichen anstelle der ursprünglichen nicht-ASCII-Zeichen. Lesevorgänge geben verstümmelten Text zurück.

Mögliche Ursachen und Lösungen:

  • Der Spaltentyp ist VARCHAR, nicht NVARCHAR. Varchar-Spalten verwenden eine Codepage, nicht Unicode. Verwenden Sie nvarchar für internationalisierte Texte.

  • Fehlt der Hinweis zur UTF-8-Codierung auf PDO_SQLSRV. Wenn deine SQL Server-Spalte nvarchar ist und deine PHP-Daten UTF-8, sag dem Treiber, er soll zwischen UTF-8 (Client) und UTF-16 (Server) konvertieren:

    <?php
    $conn = new PDO(
        "sqlsrv:Server=<server>;Database=<database>;Encrypt=true",
        $user,
        $password,
        [
            PDO::ATTR_ERRMODE                    => PDO::ERRMODE_EXCEPTION,
            PDO::SQLSRV_ATTR_ENCODING            => PDO::SQLSRV_ENCODING_UTF8,
        ]
    );
    
  • SQLSRV-Treiber: Fordern Sie explizit UTF-8 an. SQLSRV_ENC_CHAR ist die Standard-8-Bit-Systemcodepage, nicht UTF-8. Legen Sie für UTF-8 mit SQLSRV "CharacterSet" => "UTF-8" für die Verbindung fest, und übergeben Sie das Literal 'UTF-8' beim Abrufen oder Binden an SQLSRV_PHPTYPE_STRING. Siehe Senden und abrufen von UTF-8-Daten.

Fehler bei Date-Time-Umrechnungen

Symptome:

SQLSTATE[22007]: Invalid character value for cast specification

Korrektur:

In PDO_SQLSRV binden Sie kein unverarbeitetes DateTime-Objekt. PDO stringifiziert gebundene Werte vor der Bindung, und PHP DateTime hat keine Methode __toString() , daher execute([new DateTime(...)]) erhöht Object of class DateTime could not be converted to string. Formatiere zuerst den Wert oder übergebe eine ISO-8601-Zeichenkette (YYYY-MM-DD HH:MM:SS[.fff]), nicht eine lokalformatierte Zeichenkette.

<?php
$stmt = $conn->prepare("INSERT INTO dbo.Events (EventDate) VALUES (?)");
$stmt->execute([(new DateTime("2026-03-15 10:00:00"))->format("Y-m-d H:i:s.u")]);

Um Datetime-Spalten als DateTime Objekte statt Strings auf PDO_SQLSRV abzurufen, setzen Sie das Statement-Attribut:

<?php
$stmt = $conn->prepare("SELECT EventDate FROM dbo.Events");
$stmt->setAttribute(PDO::SQLSRV_ATTR_FETCHES_DATETIME_TYPE, true);
$stmt->execute();

Details finden Sie unter Datetime-Objekte abrufen (PDO_SQLSRV).

Dezimalformatierungsprobleme

Symptome:

Werte zwischen -1 und 1 fehlen eine führende Null, oder Geld- und Smallmoney-Werte zeigen eine unerwartete Anzahl von Dezimalstellen.

Korrektur:

PDO_SQLSRV ruft decimal- und numeric-Werte immer als Zeichenfolgen mit ihrer genauen Präzision und Skalierung ab. Setzen Sie PDO::SQLSRV_ATTR_FORMAT_DECIMALS so ein, dass eine führende Null zu Werten zwischen -1 und 1 hinzugefügt wird:

<?php
$conn->setAttribute(PDO::SQLSRV_ATTR_FORMAT_DECIMALS, true);

PDO::SQLSRV_ATTR_DECIMAL_PLACES gilt nur für Geld- und Kleingeldwerte . Legt die angezeigte Skala auf einen Bereich von 0 bis 4 fest und kann den angezeigten Wert runden. Sie beeinflusst keine Dezimalwerte oder numerischen Werte.

Einzelheiten finden Sie unter Format decimals and money (PDO_SQLSRV) oder Format decimals and money (SQLSRV).

Transaktionsprobleme

Datenänderungen werden nicht gespeichert

Symptome:

Zeilen, die du in PHP einfügst oder aktualisierst, erscheinen nicht, wenn du aus einer anderen Sitzung abfragst.

Ursache:

PDO::beginTransaction() öffnet eine explizite Transaktion, die ein explizites commit() erfordert. Wenn das PHP-Skript ohne Aufruf commit()endet, rollt PDO die Transaktion während der Verbindungsreinigung zurück.

Korrektur:

beginTransaction() immer mit commit() koppeln und try/catch verwenden, um bei einem Fehler ein Rollback durchzuführen:

<?php
try {
    $conn->beginTransaction();
    $conn->exec("INSERT INTO dbo.Orders (CustomerID, Total) VALUES (1, 100)");
    $conn->exec("UPDATE dbo.Inventory SET Stock = Stock - 1 WHERE ProductID = 5");
    $conn->commit();
} catch (PDOException $e) {
    $conn->rollBack();
    throw $e;
}

Verwenden Sie für SQLSRV sqlsrv_begin_transaction, sqlsrv_commit und sqlsrv_rollback.

Deadlock-Fehler

Symptome:

SQLSTATE[40001]: [Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Transaction (Process ID 62) was deadlocked

Korrektur:

Behandle vorübergehende Deadlock-Fehler mit Retry-Logik. Umschließen Sie die gesamte Transaktion (nicht nur die fehlerhafte Anweisung), damit frühere Anweisungen in der neuen Transaktion wiederholt werden. Für ein produktionsorientiertes Wiederholungsmuster siehe das Beispiel auf der PHP-Treiber-Landingpage.

Wiederkehrende Deadlocks weisen auf ein Designproblem hin. Erfassen Sie das Deadlockdiagramm, und analysieren Sie, welche Anweisungen und Sperrtypen beteiligt sind. Gängige Lösungen umfassen die Umordnung von Operationen, sodass konkurrierende Transaktionen Sperren in derselben Reihenfolge erhalten, die Verringerung des Transaktionsumfangs sowie das Hinzufügen von Indizes zur Verkürzung der Sperrdauer. Für eine vollständige Anleitung siehe Deadlocks-Leitfaden.

Probleme der Verbindungsresilienz

Eine Wiederverbindung passiert nicht

Symptome:

Eine im Leerlauf befindliche Verbindung bleibt nach einem Azure SQL-Datenbank-Failover unterbrochen, obwohl Sie ConnectRetryCount und ConnectRetryInterval festgelegt haben.

Mögliche Ursachen und Lösungen:

  • Aktiver serverseitiger Cursor. Die Resilienz von Verbindungen im Leerlauf stellt nur im Leerlauf befindliche Verbindungen wieder her. Ein offener serverseitiger Cursor oder eine ausstehende Transaktion hält die Verbindung aktiv. Geben Sie serverseitige Cursor vor dem Failover-Fenster mithilfe von sqlsrv_free_stmt() oder $stmt = null; (PDO) frei, oder wechseln Sie zu einem clientseitigen gepufferten Cursor. Siehe Inaktiv-Verbindungs-Resilienz.
  • Zustand der nicht wiederherstellbaren Sitzung. Einige Sitzungszustände können nicht wiederhergestellt werden, einschließlich temporärer Tabellen, globalen und lokalen Cursors, Transaktionskontext, Anwendungssperren, EXECUTE AS/REVERTOLE-Automatisierungshandles, vorbereiteten XML-Handles und Trace-Flags. Jeder dieser Sitzungszustände verhindert eine automatische Wiederverbindung.
  • LoginTimeout zu klein. Wenn ConnectRetryCount * ConnectRetryInterval > LoginTimeout, beendet der Treiber die Wiederholungsversuche, sobald LoginTimeout erreicht ist. Erhöhen Sie LoginTimeout, um das gesamte Wiederholungsbudget abzudecken.

Leistungsprobleme

Informationen zur Diagnose und Behebung von langsamen Abfragen, Kaltstarts, großen Resultsets und Masseneinfügungen finden Sie unter Leistungsoptimierung.

Treiberdiagnostik aktivieren

Wenn Anrufe auf Anwendungsebene error_log() nicht genügend Informationen liefern, aktivieren Sie die Fahrerseiten-Protokollierung. Es meldet jeden ODBC-Anruf, den der Fahrer tätigt.

PDO_SQLSRV

Setze pdo_sqlsrv.log_severity in php.ini und starte den Webserver neu. Diese Einstellung ist nur bei der Initialisierung lesbar:

[pdo_sqlsrv]
pdo_sqlsrv.log_severity = 1

Die Werte sind 0 (ausgeschaltet, der Standard), -1 (Fehler, Warnungen und Hinweise), 1 (Fehler), (Warnungen 2 ) und 4 (Hinweise).

SQLSRV

Aktiviere das Logging zur Laufzeit mit sqlsrv_configure():

<?php
sqlsrv_configure("LogSubsystems", SQLSRV_LOG_SYSTEM_CONN | SQLSRV_LOG_SYSTEM_STMT);
sqlsrv_configure("LogSeverity", SQLSRV_LOG_SEVERITY_ERROR | SQLSRV_LOG_SEVERITY_WARNING);

Protokolleinträge werden in die Datei geschrieben, die von error_log in php.ini konfiguriert wurde. Für die vollständige Liste der Teilsysteme und Schweregrade siehe Aktivitätenprotokoll.

Container- und CI-Probleme

Fehlende Systembibliotheken unter Linux

Symptome:

error while loading shared libraries: libodbc.so.2: cannot open shared object file
error while loading shared libraries: libssl.so.1.1: cannot open shared object file

Korrektur:

Installiere die Laufzeitabhängigkeiten, bevor du den PHP-Treiber installierst:

Verteilung Installationsbefehl
Ubuntu und Debian sudo apt-get install unixodbc libgssapi-krb5-2
Red Hat und Fedora sudo dnf install unixODBC krb5-libs
Alpine apk add unixodbc gcompat

Installieren Sie dann msodbcsql18 aus dem Microsoft-Paket-Repository. Für distribution-spezifische Paket-Repositories und Versionen siehe die ODBC-Treiberinstallationsanleitung.

Das Erstellen von Docker-Images ist erfolgreich, aber Verbindungen schlagen zur Laufzeit fehl

Symptome:

Das Image wird erstellt und PHP startet, wirft PDO::__construct() aber einen ODBC-Treiber-nicht-gefunden-Fehler.

Korrektur:

Überprüfen Sie, ob der ODBC-Treiber im Laufzeit-Image installiert ist, nicht nur in der Build-Phase. Installieren Sie msodbcsql18 und unixodbc-dev in derselben Phase, die in die Produktion überführt wird. Bei einem mehrstufigen Bau installieren Sie sie in der Endstufe. Eine einstufige Debian-basierte Installation sieht so aus:

# Pin to a specific PHP minor version in production, for example php:8.4.11-cli.
FROM php:8.4-cli
RUN apt-get update && apt-get install -y --no-install-recommends \
        curl gnupg2 apt-transport-https ca-certificates \
    && curl -sSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /usr/share/keyrings/microsoft.gpg \
    && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list \
    && apt-get update \
    && ACCEPT_EULA=Y apt-get install -y --no-install-recommends msodbcsql18 unixodbc-dev \
    # $PHPIZE_DEPS ships in the official php image and includes gcc, make, autoconf, and re2c.
    && apt-get install -y --no-install-recommends $PHPIZE_DEPS \
    && pecl install sqlsrv pdo_sqlsrv \
    && docker-php-ext-enable sqlsrv pdo_sqlsrv \
    && apt-get purge -y --auto-remove $PHPIZE_DEPS \
    && rm -rf /var/lib/apt/lists/*