Rozwiązuj problemy ze sterownikami Microsoft dla PHP dla SQL Server

Pobieranie sterownika PHP

Zdiagnozuj i rozwiązuj typowe problemy, gdy używasz sterowników Microsoft for PHP for SQL Server, aby połączyć się z SQL Server, Azure SQL Database, Azure SQL Managed Instance oraz bazą danych SQL Microsoft Fabric.

Ogólne wzorce obsługi błędów i ostrzeżeń można znaleźć w sekcji Obsługa błędów i ostrzeżeń. Aby uzyskać przechwytywanie diagnostyczne po stronie sterownika, zobacz Rejestrowanie aktywności.

Problemy z instalacją

Rozszerzenie nie zostało załadowane

Objawy:

  • phpinfo() nie zawiera sekcji sqlsrv lub pdo_sqlsrv.
  • PDOException: could not find driver podczas tworzenia obiektu PDO przy użyciu identyfikatora DSN sqlsrv:.
  • Fatal error: Uncaught Error: Call to undefined function sqlsrv_connect().

Możliwe przyczyny i rozwiązania:

  • Rozszerzenie nie jest włączone w php.ini. Sprawdź, czy zarówno extension=sqlsrv, jak i extension=pdo_sqlsrv nie są zakomentowane. Na Windows używaj pełnej nazwy pliku (extension=php_sqlsrv_84_ts_x64.dll). Szczegółowe informacje znajdują się w Ładowanie sterowników.
  • Zła konstrukcja bezpieczeństwa gwintu. Plik binarny sterownika musi odpowiadać ustawieniu bezpieczeństwa wątkowego w Twojej kompilacji PHP (ts dla wersji bezpiecznej wątkowo, nts dla wersji niebezpiecznej wątkowo). Uruchom php -i | grep "Thread Safety" , aby sprawdzić. Pobierz pasujący binarny plik z strony pobierania.
  • Brakuje sterownika Microsoft ODBC. Sterowniki PHP stanowią otoczkę dla Microsoft ODBC Driver for SQL Server. Na Linuksie i macOS zainstaluj msodbcsql18 (lub msodbcsql17) za pomocą menedżera pakietów przed załadowaniem rozszerzeń. Na Windows zainstaluj sterownik ODBC ze strony pobierania.

Sprawdź pomyślny montaż:

php -m | grep -i sqlsrv

Powinieneś zobaczyć zarówno pdo_sqlsrv, jak i sqlsrv w danych wyjściowych.

Instalacja PECL nie działa na Linuksie ani macOS

Objawy:

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

Poprawka:

Zainstaluj pliki nagłówkowe ODBC przed uruchomieniem pecl install:

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

Następnie spróbuj ponownie:

sudo pecl install sqlsrv
sudo pecl install pdo_sqlsrv

Jeśli pecl nadal nie działa po zainstalowaniu plików nagłówkowych, łańcuch narzędzi kompilacji może być niekompletny. Zainstaluj phpize, re2c, oraz kompilator C++ (build-essential na Debianie i Ubuntu, gcc-c++ make na Red Hat i Fedora, build-base na Alpine).

Pełną ścieżkę instalacji znajdziesz w poradniku instalacyjnym dla Linuksa i macOS.

Zainstalowano wiele wersji PHP

Objawy:

phpinfo() na serwerze WWW pokazuje jedną wersję PHP, ale php -v w wierszu poleceń inną, a sterownik jest załadowany tylko w jednej z nich.

Poprawka:

Każda wersja PHP ma własny php.iniext katalog. Znajdź właściwy plik konfiguracyjny php --ini w środowisku, w którym brakuje sterownika, i dodaj tam wiersze extension=. Po każdej php.ini zmianie ponownie uruchom serwer WWW (Apache, Nginx + PHP-FPM lub IIS).

Problemy z połączeniem

Nie można połączyć się z serwerem

Objawy:

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

Możliwe przyczyny i rozwiązania:

  • Serwer jest niedostępny. Sprawdź, czy nazwa serwera i port są poprawne. Z hosta PHP testuj surową łączność TCP.

    # Linux and macOS
    nc -vz <server>.database.windows.net 1433
    
    # Windows PowerShell
    Test-NetConnection -ComputerName <server>.database.windows.net -Port 1433
    
  • Zapora blokuje wychodzący ruch na porcie 1433. Korporacyjne zapory sieciowe i chmurowe NSG często blokują port wychodzący 1433. Dodaj wyjątek lub zezwól na zakresy adresów IP usługi Azure SQL Database dla swojego regionu.

  • Azure SQL server firewall. Dodaj publiczny adres IP klienta do reguł zapory na poziomie serwera w portalu Azure.

  • Nazwana instancja. Dla nazwanego przypadku sprawdź, czy usługa SQL Server Browser działa na serwerze i czy UDP 1434 jest otwarty. Albo połącz się za pomocą portu zamiast nazwy instancji.

Logowanie nie powiodło się

Objawy:

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

Możliwe przyczyny i rozwiązania:

  • Tryb uwierzytelniania SQL wyłączony. Lokalne instancje SQL Server domyślnie używają tylko uwierzytelniania Windows. Włącz uwierzytelnianie w trybie mieszanym w SQL Server Management Studio w sekcjiBezpieczeństwo> a następnie ponownie uruchom usługę SQL Server.
  • Azure SQL credentials format. Azure SQL wymaga w pełni kwalifikowanej nazwy użytkownika (user@servername) podczas łączenia się z narzędziami, które nie dodają jej automatycznie.
  • Użytkownik nie przypisany do bazy danych. Sprawdź, czy logowanie ma przypisanie użytkownika do docelowej bazy danych oraz czy użytkownik ma wymagane uprawnienia.
  • Wolę Microsoft Entra ID. W przypadku usługi Azure SQL, Azure SQL Managed Instance i bazy danych SQL w usłudze Fabric używaj uwierzytelniania Microsoft Entra (Authentication=ActiveDirectoryMsi, Authentication=ActiveDirectoryServicePrincipal lub tokenu dostępu) zamiast identyfikatorów logowania SQL. Zobacz Nawiązywanie połączenia przy użyciu uwierzytelniania Microsoft Entra.

Nieprawidłowa wartość określona dla atrybutu parametry połączenia 'Authentication'

Objawy:

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

Przyczyna:

Sterownik ODBC zgłasza błąd, ale prawdziwym problemem jest to, z którym sterownikiem jest powiązany PDO_SQLSRV. Jeśli DSN nie zawiera Driver= słowa kluczowego, a host ma zainstalowane zarówno ODBC 17, jak i ODBC 18, PDO_SQLSRV może powiązać z starszą wersją. Starsze wersje ODBC 17.x nie znają nowszych Authentication wartości, takich jak ActiveDirectoryServicePrincipal lub ActiveDirectoryDefault, i wymagają nawet ActiveDirectoryMsi ODBC 17.3.1.1 lub nowszej wersji.

Poprawka:

Przypnij sterownik w DSN:

<?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]);

Forma nawiasowa ({ODBC Driver 18 for SQL Server}) wychodzi poza przestrzeni w nazwie kierowcy. Sam komunikat o błędzie zawsze podaje nazwę sterownika, który go zgłosił, więc prefiks [Microsoft][ODBC Driver 17 for SQL Server] w komunikacie o błędzie jest najszybszym sposobem na potwierdzenie, że powiązano niewłaściwy sterownik.

Podano nieprawidłowe słowo kluczowe „UID” w ciągu DSN

Objawy:

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

Przyczyna:

PDO_SQLSRV wymusza listę dozwolonych słów kluczowych DSN i nie akceptuje w DSN elementów UID ani PWD. PDO rezerwuje drugi i trzeci argument konstruktora dla nich, a PDO_SQLSRV tłumaczy je wewnętrznie na ODBC UID/PWD.

Poprawka:

Przenieś nazwę użytkownika (i hasło, do uwierzytelniania SQL) do konstruktora PDO:

<?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);

Sterownik proceduralny SQLSRV natomiast akceptuje UID i PWD w tablicy opcji połączenia przekazywanej do sqlsrv_connect().

PDO_SQLSRV cicho ignoruje AccessToken w tablicy opcji

Objaw:

Masz token dostępu Microsoft Entra (na przykład z az account get-access-token --resource https://database.windows.net/, ManagedIdentityCredential lub ClientSecretCredential) i przekazujesz go do PDO_SQLSRV jako ['AccessToken' => $token] w czwartym argumencie konstruktora. Próba połączenia kończy się niepowodzeniem z mylącym błędem, takim jak Windows logins are not supported in this version of SQL Server lub Login failed for user '', jakby nie podano żadnych danych uwierzytelniających.

Przyczyna:

Czwarty argument konstruktora PDO jest zarezerwowany dla stałych atrybutów właściwych dla sterownika (kluczy całkowitoliczbowych, takich jak PDO::ATTR_ERRMODE). PDO cicho usuwa wpisy z kluczem łańcuchowym, takie jak AccessToken, więc PDO_SQLSRV nigdy nie widzi tokena. Połączenie następnie przechodzi na uwierzytelnianie zintegrowane systemu Windows, które jest odrzucane przez serwer.

Poprawka:

Przenieś AccessToken do ciągu DSN. Zarezerwuj tablicę opcji dla stałych PDO::ATTR_*.

<?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,
]);

Aby uzyskać dodatkowe przykłady uwierzytelniania Microsoft Entra, w tym format DSN dla PDO_SQLSRV, zobacz artykuł Nawiązywanie połączenia przy użyciu uwierzytelniania Microsoft Entra.

W przypadku proceduralnego interfejsu SQLSRV element AccessToken powinien znaleźć się w tablicy informacji o połączeniu przekazywanej do sqlsrv_connect(), która rzeczywiście opakowuje surowy token JWT w SQL_COPT_SS_ACCESS_TOKEN.

<?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);
}

Błędy certyfikatów TLS

Objawy:

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

Rozwiązania:

Wolę zaufany certyfikat. Używaj TrustServerCertificate=true tylko do lokalnego rozwoju na serwerze, który kontrolujesz.

Do programowania z użyciem certyfikatu z podpisem własnym:

<?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 wyłącza walidację certyfikatu serwera. Nigdy nie przenosz tego ustawienia do środowisk produkcyjnych, stagingowych czy współdzielonych.

Dla nazwy hosta produkcyjnego, która nie odpowiada nazwie Common Certificate (na przykład podczas łączenia przez listener), określ rzeczywisty podmiot certyfikatu:

<?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,
]);

Przekroczenie limitu czasu połączenia

Objawy:

SQLSTATE[HYT00]: Login timeout expired

Możliwe przyczyny i rozwiązania:

  • LoginTimeout nie jest ustawione lub jest ustawione zbyt nisko dla zimnego przełączenia awaryjnego. Ustaw jawnie LoginTimeout (w sekundach) w DSN podczas nawiązywania połączenia z usługą Azure SQL. Awaryjne przełączania grupowe i bazy danych z zimnym startem mogą trwać dłużej niż pozwala na to krótki timeout po stronie klienta. Zobacz Opcje połączenia, aby uzyskać informacje o opcjach.
  • Limit ponownych połączeń w stanie bezczynności został obcięty. Jeśli ustawisz ConnectRetryCount i ConnectRetryInterval, upewnij się, że LoginTimeout >= ConnectRetryCount * ConnectRetryInterval. W przeciwnym razie limit czasu logowania przerywa pętlę ponawiania połączenia przedwcześnie. Zobacz odporność połączenia bezczynnego.
<?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,
]);

Problemy z wykonywaniem zapytań

Niesygnalizowane błędy w PDO

Objaw:

Wywołanie PDO::exec() lub PDOStatement::execute() zwraca false, ale nie zgłasza wyjątku.

Poprawka:

W PHP 8.0 i nowszych wersjach domyślnym trybem błędów PDO jest PDO::ERRMODE_EXCEPTION. Jeśli wywołanie zwraca false bez rzutu, aplikacja zmienia tryb na PDO::ERRMODE_SILENT lub PDO::ERRMODE_WARNING. Ustawij go z powrotem na tryb wyjątku, aby niepowodzenia wywołały wyjątki:

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

Jeśli nie możesz globalnie zmienić trybu, sprawdź $conn->errorInfo() (lub $stmt->errorInfo()) po każdym połączeniu. Tablica zawiera [SQLSTATE, driver code, driver message].

Nieprawidłowa nazwa obiektu

Objawy:

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

Możliwe przyczyny i rozwiązania:

  • Zły kontekst bazy danych. Sprawdź to szybkim pytaniem:

    <?php
    $stmt = $conn->query("SELECT DB_NAME()");
    echo $stmt->fetchColumn();
    
  • Brakuje kwalifikatora schematu. Używaj w pełni kwalifikowanych nazw, aby uniknąć zależności od domyślnego schematu dzwoniącego:

    SELECT * FROM dbo.Products;
    
  • Wielkość liter. Bazy danych utworzone z sortowaniem rozróżniającym wielkość liter traktują products i Products jako różne obiekty. Zrównaj dokładny przypadek z definicji tabeli.

Błędna liczba parametrów

Objawy:

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

Poprawka:

W przypadku PDO_SQLSRV liczba symboli zastępczych ? musi być zgodna z liczbą wartości przekazywanych do execute(), a każdy ? powiązuje pojedynczą wartość skalarną (nie tablicę). W przypadku parametrów nazwanych każdy parametr w instrukcji SQL :name musi występować w tablicy i odwrotnie.

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

Dla SQLSRV przekażmy tablicę parametrów do sqlsrv_query() lub 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));
}

Szersze wprowadzenie do wiązania parametrów można znaleźć w artykule Wykonuj zapytania parametryzowane.

Emulowane zapytania przygotowane PDO maskują błędy

Objawy:

Instrukcja wykonuje się pomyślnie na jednym połączeniu, ale powoduje błąd składniowy na innym połączeniu, które używa tego samego tekstu zapytania.

Przyczyna:

PDO_SQLSRV obsługuje zarówno emulowane, jak i natywne przygotowane instrukcje. Emulowane instrukcje prepare (PDO::ATTR_EMULATE_PREPARES = true) interpolują parametry po stronie klienta. Natywne przygotowuje (false) wysyła zapytanie i parametry osobno do serwera. Zachowanie różni się w przypadku TOP (?), parametrów o wartościach tabelarycznych oraz niektórych przypadków brzegowych związanych z wymuszaniem typu.

Poprawka:

Preferuję lokalne produkty w produkcji. Ustaw PDO::ATTR_EMULATE_PREPARES => false podczas nawiązywania połączenia, aby zachowanie było spójne we wszystkich środowiskach:

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

Szczegółowe informacje o tym, kiedy używać każdego trybu, można znaleźć w PDO::prepare.

Problemy z typem danych

Znaki Unicode pojawiają się jako ? lub zniekształcone

Objawy:

Wiersze zapisywane przez PHP zawierają znaki zapytania lub znaki zastępcze zamiast oryginalnych znaków nie-ASCII. Odczyty zwracają nieczytelny tekst.

Możliwe przyczyny i rozwiązania:

  • Typ kolumny to VARCHAR, nie NVARCHAR. kolumny varchar używają strony kodowej, a nie Unicode. Użyj nvarchar do tekstów międzynarodowych.

  • Brakuje wskazówki kodowania UTF-8 na PDO_SQLSRV. Gdy kolumna SQL Server to nvarchar, a dane PHP to UTF-8, powiedz sterownikowi, aby konwertował między UTF-8 (klient) a UTF-16 (serwer):

    <?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,
        ]
    );
    
  • Sterownik SQLSRV: zażądaj UTF-8 wyraźnie. SQLSRV_ENC_CHAR to domyślna 8-bitowa strona kodowa systemu, a nie UTF-8. W przypadku UTF-8 z SQLSRV ustaw "CharacterSet" => "UTF-8" w połączeniu i przekaż literał 'UTF-8' do SQLSRV_PHPTYPE_STRING podczas pobierania lub wiązania. Zobacz Wyślij i pobierz dane UTF-8.

Błędy konwersji daty i godziny

Objawy:

SQLSTATE[22007]: Invalid character value for cast specification

Poprawka:

Na PDO_SQLSRV nie wiązaj surowego obiektu DateTime . PDO konwertuje wiązane wartości na ciągi znaków przed ich powiązaniem, a PHP nie ma metody DateTime__toString(), więc execute([new DateTime(...)]) zgłasza Object of class DateTime could not be converted to string. Najpierw sformatuj wartość lub przekaż ciąg ISO 8601 (YYYY-MM-DD HH:MM:SS[.fff]), a nie łańcuch sformatowany lokalnie.

<?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")]);

Aby pobierać kolumny datetime jako obiekty DateTime zamiast ciągów znaków w PDO_SQLSRV, ustaw atrybut instrukcji:

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

Szczegółowe informacje znajdują się w sekcji Pobieranie obiektów datetime (PDO_SQLSRV).

Problemy z formatowaniem dziesiętnym

Objawy:

Wartościom z zakresu od -1 do 1 brakuje wiodącego zera lub wartości money i smallmoney wyświetlają nieoczekiwaną liczbę miejsc po przecinku.

Poprawka:

PDO_SQLSRV zawsze pobiera wartości dziesiętne i liczbowe jako ciągi znaków z ich dokładną precyzją i skalą. Ustaw PDO::SQLSRV_ATTR_FORMAT_DECIMALS tak, by dodawać wiodące zero do wartości między -1 a 1:

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

PDO::SQLSRV_ATTR_DECIMAL_PLACES Dotyczy tylko pieniędzy i wartości drobnych pieniędzy . Ustawia skalę wyświetlaną od 0 do 4 i może zaokrąglać wyświetlaną wartość. Nie wpływa to na wartości dziesiętne ani liczbowe .

Szczegóły można znaleźć w Formatowanie dziesiętnych i pieniędzy (PDO_SQLSRV) lub Formatowanie dziesiętnych i pieniądza (SQLSRV).

Problemy transakcyjne

Zmiany danych nie utrzymują się

Objawy:

Wiersze, które wstawiasz lub aktualizujesz za pomocą PHP, nie pojawiają się przy wykonywaniu zapytania z innej sesji.

Przyczyna:

PDO::beginTransaction() otwiera jawną transakcję, która wymaga jawnego commit(). Jeśli skrypt PHP zakończy się bez wywołania commit(), PDO cofa transakcję podczas czyszczenia połączenia.

Poprawka:

Zawsze łącz beginTransaction() z commit() i używaj try/catch, aby wycofać zmiany w przypadku błędu:

<?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;
}

Dla SQLSRV używaj sqlsrv_begin_transaction, sqlsrv_commit, oraz sqlsrv_rollback.

Błędy zakleszczeń

Objawy:

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

Poprawka:

Obsługa przejściowych błędów zablokowania za pomocą logiki powtórek. Obejmij całą transakcję (nie tylko nieudaną instrukcję), aby wcześniejsze instrukcje zostały ponownie wykonane w nowej transakcji. Aby uzyskać wzorzec powtórek zorientowany na produkcję – zobacz przykład na stronie docelowej sterownika PHP.

Powtarzające się zakleienia wskazują na problem projektowy. Uchwyć graf blokad i przeanalizować, które instrukcje i typy blokad są zaangażowane. Typowe poprawki obejmują zmianę kolejności operacji, tak aby konkurencyjne transakcje zdobywały blokady w tej samej sekwencji, ograniczanie zakresu transakcji oraz dodawanie indeksów skracających czas blokady. Pełny poradnik znajdziesz w przewodniku Deadlocks.

Problemy z odpornością połączeń

Ponowne połączenie się nie zdarza

Objawy:

Połączenie bezczynne pozostaje przerwane po przełączeniu awaryjnym usługi Azure SQL Database, mimo że ustawiono ConnectRetryCount i ConnectRetryInterval.

Możliwe przyczyny i rozwiązania:

  • Aktywny kursor po stronie serwera. Odporność nieaktywnych połączeń na zerwanie ponownie nawiązuje tylko bezczynne połączenia. Otwarty kursor po stronie serwera lub oczekująca transakcja utrzymuje połączenie aktywne. Zwolnij kursory po stronie serwera, używając sqlsrv_free_stmt() lub $stmt = null; (PDO) przed oknem przełączenia awaryjnego, albo przełącz się na kursor buforowany po stronie klienta. Zobacz odporność połączenia bezczynnego.
  • Stan sesji nieodwracalny. Niektórych stanów sesji nie da się przywrócić, w tym tabel tymczasowych, kursorów globalnych i lokalnych, kontekstu transakcji, blokad aplikacji, EXECUTE AS/REVERT, uchwytów automatyzacji OLE, przygotowanych uchwytów XML oraz flag śledzenia. Każdy z tych stanów sesji uniemożliwia automatyczne ponowne połączenie.
  • LoginTimeout za mały. Jeśli ConnectRetryCount * ConnectRetryInterval > LoginTimeout, sterownik przestaje ponawiać próby po osiągnięciu wartości LoginTimeout. Zwiększ wartość LoginTimeout, aby objąć pełny limit ponownych prób.

Problemy z wydajnością

Informacje na temat diagnozowania i usuwania problemów związanych z wolnymi zapytaniami, zimnymi startami, dużymi zbiorami wyników oraz wstawianiem zbiorczym można znaleźć w sekcji Dostrajanie wydajności.

Włącz diagnostykę sterowników

Gdy wywołania na poziomie error_log() aplikacji nie dostarczają wystarczających informacji, włącz logowanie po stronie kierowcy. Raportuje każde połączenie ODBC, które wykonuje kierowca.

PDO_SQLSRV

Ustaw pdo_sqlsrv.log_severity w php.ini i uruchom ponownie serwer WWW. To ustawienie jest czytelne tylko podczas inicjalizacji:

[pdo_sqlsrv]
pdo_sqlsrv.log_severity = 1

Wartości to 0 (wyłączone, domyślne), -1 (błędy, ostrzeżenia i powiadomienia), 1 (błędy), 2 (ostrzeżenia) oraz 4 (powiadomienia).

SQLSRV

Włącz logowanie w czasie wykonywania za pomocą 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);

Wpisy dziennika są zapisywane do pliku skonfigurowanego przez error_log w php.ini. Pełną listę podsystemów i skali nasilenia można znaleźć w artykule Aktywność logowania.

Problemy z kontenerami i CI

Brakujące biblioteki systemowe na Linuksie

Objawy:

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

Poprawka:

Zainstaluj zależności wymagane w czasie wykonywania przed zainstalowaniem sterownika PHP:

Dystrybucja Zainstaluj polecenie
Ubuntu i Debian sudo apt-get install unixodbc libgssapi-krb5-2
Red Hat i Fedora sudo dnf install unixODBC krb5-libs
Alpine apk add unixodbc gcompat

Następnie instaluj msodbcsql18 z repozytorium pakietów Microsoft. Aby poznać repozytoria pakietów i wersje specyficzne dla dystrybucji, zobacz przewodnik instalacji sterowników ODBC.

Kompilacja obrazów Dockera kończy się powodzeniem, ale połączenia nie działają w czasie działania

Objawy:

Obraz jest budowany, a PHP się uruchamia, ale PDO::__construct() zgłasza błąd „nie znaleziono sterownika ODBC”.

Poprawka:

Sprawdź, czy sterownik ODBC jest zainstalowany w obrazie uruchomieniowym, a nie tylko na etapie budowy. Zainstaluj msodbcsql18 i unixodbc-dev na tym samym etapie, co wysyła się do produkcji. W budowie wieloetapowej instaluj je na ostatnim etapie. Jednoetapowa instalacja oparta na Debianie wygląda tak:

# 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/*