Réglage des performances pour les pilotes Microsoft pour PHP pour SQL Server

Télécharger le pilote PHP

Cet article explique comment écrire du code PHP rapide sur SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics et une base de données SQL dans Microsoft Fabric. Les directives s’appliquent à la fois à SQLSRV et PDO_SQLSRV, qui enveloppent le même Microsoft pilote ODBC sous-jacent pour SQL Server.

Commencez par les changements les plus impactants

Si vous ne pouvez effectuer que trois changements, faites-les :

  • Activez le pooling de connexions. Établir une nouvelle connexion TLS à SQL Server prend des dizaines à des centaines de millisecondes selon le chemin réseau et la négociation TLS. La réutilisation des connexions poolées élimine ce coût par requête. Voir Gérer les connexions efficacement.
  • Récupérez uniquement les colonnes et lignes dont vous avez besoin. SELECT * et les requêtes non bornées sont les causes les plus courantes de points de terminaison lents. Voir Interroger uniquement ce dont vous avez besoin.
  • Utilisez des paramètres à valeurs de table pour les inserts en masse. Pour des centaines de lignes ou plus, les paramètres à valeurs de table (TVP) sont généralement beaucoup plus rapides que les instructions ligne par ligne INSERT et évoluent linéairement avec le nombre de lignes. Voir Insérer les données efficacement.

Gérer les connexions efficacement

L’établissement de la connexion est l’opération la plus coûteuse effectuée par le conducteur. Presque toutes les analyses de performance de PHP se terminent par une correction de gestion de connexion.

Activer le regroupement de connexions

Le pooling réutilise les connexions ODBC entre requêtes PHP au lieu de les démanteler à la fin de la requête. L'objet connection est supprimé lorsque votre script se termine, mais le handle ODBC sous-jacent reste actif dans le pool du gestionnaire de pilotes ODBC et est réutilisé par la requête suivante qui demande la même chaîne de connexion.

Windows : Le pooling de connexions est activé par défaut. Pour confirmer, ne mentionnez pas cette ConnectionPooling option dans votre DSN. Pour désactiver le pooling pour le débogage, définissez ConnectionPooling=0.

Linux et macOS : le pooling de connexions n’est pas une option DSN sur ces plateformes. Activez-le dans le gestionnaire de pilotes en mettant Pooling=Yes dans la [ODBC] section de odbcinst.ini, et placez un positif CPTimeout sous la strophe du pilote. Par exemple:

[ODBC]
Pooling=Yes

[ODBC Driver 18 for SQL Server]
Description=Microsoft ODBC Driver 18 for SQL Server
Driver=/opt/microsoft/msodbcsql18/lib64/libmsodbcsql-18.<version>.so.1.1
CPTimeout=120

Trouvez le chemin réel de la bibliothèque avec odbcinst -q -d -n "ODBC Driver 18 for SQL Server" ou ls /opt/microsoft/msodbcsql18/lib64/. Le nom de fichier incorpore la version installée du pilote ODBC et change à chaque version.

CPTimeout (en quelques secondes) contrôle combien de temps les connexions au ralenti restent dans la piscine avant d’être fermées. Réglez-le suffisamment haut pour que la plupart des requêtes trouvent une connexion poolée, mais assez bas pour que les connexions obsolètes vers un serveur défaillant soient retirées assez rapidement. 60 à 300 secondes fonctionnent bien pour la plupart des charges de travail web.

Pour plus de détails, voir Pooling de connexions.

Comprenez le coût de la première requête

MARS (Multiple Active Result Sets) est activé par défaut. Lorsque MARS et le pooling de connexions sont tous deux actifs, le pilote réinitialise la connexion en pool dès la première requête, et cette réinitialisation ignore tout délai de requête que vous avez défini pour cette première requête. Les requêtes ultérieures sur la même connexion respectent normalement le time-out. Si vous définissez des délais agressifs pour la première requête sur une charge de travail regroupée, prenez en compte ce comportement, ou désactivez MARS si MultipleActiveResultSets=false vous n’en avez pas besoin. Voir la note MARS et le pooling dans Connection pooling.

Les connexions PDO persistantes ne sont pas prises en charge

PDO_SQLSRV rejette PDO::ATTR_PERSISTENT. En la posant sur les projections constructeurs :

SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.

Utilisez le pooling de connexions ODBC pour la réutilisation croisée. C'est le mécanisme natif du pilote, fonctionne à la fois pour PDO_SQLSRV et SQLSRV, et met hors service les connexions inactives CPTimeout (ce qui permet aussi de garder Microsoft Entra rafraîchissement des jetons honnête).

Réutilisez la connexion dans une requête

Même avec le pooling, ouvrir une nouvelle connexion PDO ou SQLSRV implique un aller-retour ODBC pour récupérer et valider un handle poolé. Ouvre une connexion une fois par requête et transmets-la à toutes les fonctions qui en ont besoin.

Tip

Un conteneur d’injection de dépendance ou un accesseur paresseux suffisent. L’objectif est d’éviter new PDO(...) au milieu d’un gestionnaire de requêtes.

Interrogez uniquement ce dont vous avez besoin

Les allers-retours réseau et la matérialisation des ensembles de résultats dominent la latence des requêtes pour la plupart des charges de travail PHP. Les correctifs sont les mêmes que ceux qui s’appliquent à chaque couche d’accès à la base de données.

Sélectionnez uniquement les colonnes que vous utilisez

SELECT * Extrait chaque colonne, y compris varchar(max) et varbinary(max) qui dépassent largement les données que vous consommez réellement. Nomme les colonnes :

<?php
// Slow: fetches all columns, including a 2 MB LOB column
$stmt = $conn->query("SELECT * FROM dbo.Products");

// Fast: fetches only the two columns the caller uses
$stmt = $conn->query("SELECT ProductID, Name FROM dbo.Products");

Prenez seulement les rangées dont vous avez besoin

Poussez le filtrage vers SQL Server. Ne jamais récupérer une table complète dans PHP juste pour filtrer en foreach boucle.

<?php
// Slow: transfers every row to PHP, then filters
$rows = $conn->query("SELECT * FROM dbo.Orders")->fetchAll(PDO::FETCH_ASSOC);
$recent = array_filter($rows, fn($r) => $r["OrderDate"] > "2026-01-01");

// Fast: filters on the server
$stmt = $conn->prepare("SELECT OrderID, CustomerID, Total FROM dbo.Orders WHERE OrderDate > ?");
$stmt->execute(["2026-01-01"]);
$recent = $stmt->fetchAll(PDO::FETCH_ASSOC);

Paginer les jeux de résultats volumineux

Pour une vue de liste qui affiche quelques centaines de lignes sur des millions, ne retournez pas toutes les lignes et laissez le client trier les choses. Utilisez la pagination côté serveur avec OFFSET ... FETCH:

<?php
function fetchPage(PDO $conn, int $page, int $pageSize): array {
    $stmt = $conn->prepare(
        "SELECT OrderID, CustomerID, Total
         FROM dbo.Orders
         ORDER BY OrderID
         OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"
    );
    // With native prepares, execute([...]) binds values as strings.
    // OFFSET and FETCH NEXT require integer bindings; bind explicitly.
    $stmt->bindValue(1, ($page - 1) * $pageSize, PDO::PARAM_INT);
    $stmt->bindValue(2, $pageSize, PDO::PARAM_INT);
    $stmt->execute();
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

Choisissez la bonne méthode de récupération

  • Utilisez-les fetch(PDO::FETCH_ASSOC) en boucle pour l’itération en streaming quand vous n’avez pas besoin de toutes les lignes en mémoire en même temps.
  • Utilisez-les fetchAll(PDO::FETCH_ASSOC) lorsque l’appelant a vraiment besoin de l’ensemble complet (par exemple, en affichant une réponse JSON complète).
  • À utiliser fetchColumn() lorsque vous ne vous intéressez qu’à un seul scalaire (un COUNT, SUM, ou MAX).
  • Utilisez PDO::FETCH_KEY_PAIR ou PDO::FETCH_UNIQUE construisez des dictionnaires de recherche sans second pass.

Les modes de récupération numérique (PDO::FETCH_NUM) sont légèrement plus rapides que les modes de récupération associatives car ils sautent la construction de la carte de noms de colonne. Privilégiez la clarté ; Changer uniquement lorsqu’un profileur signale la charge de récupération comme significative.

Privilégie SET NOCOUNT ON dans les procédures stockées et les lots

Chaque INSERTinstruction , UPDATE, et DELETE renvoie un DONE_IN_PROC jeton avec le nombre de lignes concerné, que PHP défausse généralement. Le jeton n’ajoute pas de trajet aller-retour, mais chacun coûte toujours des octets sur le câble et une petite partie du travail sur les pilotes. Dans une procédure ou un lot multi-états qui exécute des centaines de relevés par appel, les économies s’accumulent. Éteins-le :

CREATE OR ALTER PROCEDURE dbo.ProcessOrder
    @OrderID INT
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE dbo.Inventory SET Stock = Stock - 1 WHERE ProductID IN (SELECT ProductID FROM dbo.OrderLines WHERE OrderID = @OrderID);
    UPDATE dbo.Orders SET Status = 'Processed' WHERE OrderID = @OrderID;
END;

Insérer les données efficacement

Choisissez la bonne méthode d’insertion en fonction du nombre de rangées que vous déplacez. Le mauvais choix peut être 100 fois plus lent.

Moins d’environ 100 lignes : déclaration préparée dans une boucle

Pour de petits lots, exécutez une seule instruction préparée dans une boucle :

<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
    $stmt->execute([$p["name"], $p["price"]]);
}

Enroulez la boucle dans une transaction pour que toutes les insertions s’engagent en une seule unité et que le journal n’ait pas à se vider après chaque ligne :

<?php
$conn->beginTransaction();
try {
    $stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
    foreach ($products as $p) {
        $stmt->execute([$p["name"], $p["price"]]);
    }
    $conn->commit();
} catch (PDOException $e) {
    $conn->rollBack();
    throw $e;
}

Des centaines à des millions de lignes : paramètres à valeurs de table

Les paramètres à valeurs de table (TVP) envoient l’ensemble du lot à SQL Server en un aller-retour et laissent SQL Server traiter l’ensemble comme une seule instruction. Pour des lots de centaines de rangées ou plus, les TVP sont généralement beaucoup plus rapides qu’une boucle d’énoncé préparé et s’étendent linéairement avec le nombre de lignes.

Tout d’abord, créez un type de table sur le serveur :

CREATE TYPE dbo.ProductTableType AS TABLE (
    Name  NVARCHAR(100),
    Price DECIMAL(10, 2)
);

PDO_SQLSRV transmet le TVP comme un tableau associatif dont la clé est le nom du type et dont la valeur est l’ensemble de lignes. Liez-le avec PDO::PARAM_LOB:

<?php
$rows = [];
foreach ($products as $p) {
    $rows[] = [$p["name"], $p["price"]];
}
$tvpInput = ["ProductTableType" => $rows];

$stmt = $conn->prepare(
    "INSERT INTO dbo.Products (Name, Price) SELECT Name, Price FROM ?"
);
$stmt->bindParam(1, $tvpInput, PDO::PARAM_LOB);
$stmt->execute();

Pour un schéma non par défaut, passez le schéma comme élément suivant du tableau : ["ProductTableType" => $rows, "Sales"]. Pour les exemples de syntaxe procédurale SQLSRV et de procédures stockées, voir Utiliser les paramètres à valeurs de table.

Des millions de lignes : bcp ou BULK INSERT

Pour de véritables opérations en masse (charges d’entrepôt de données, migrations initiales), utilisez BCP ou BULK INSERT à la place de PHP. Écrivez vos données dans un fichier délimité ou au format natif, puis exécutez bcp ou BULK INSERT depuis un job programmé, une étape ETL ou un script admin.

Caution

Si vous payez du BCP depuis PHP avec shell_exec() ou proc_open(), n’interpole jamais d’entrée non fiable dans la ligne de commande. Utilisez-le escapeshellarg() sur chaque argument, et préférez exécuter la charge hors bande plutôt que dans un chemin de requête web.

Réduire les allers-retours

Chaque aller-retour réseau entre PHP et SQL Server a un coût fixe. Quand vous envoyez cinq relevés en un seul lot, vous payez ce coût une fois au lieu de cinq.

Pour les travaux connexes qui s’exécutent ensemble, mettez les instructions en un seul lot et consommez chaque ensemble de résultats :

<?php
$sql = "
    SELECT * FROM dbo.Customers WHERE CustomerID = ?;
    SELECT * FROM dbo.Orders WHERE CustomerID = ?;
    SELECT * FROM dbo.Addresses WHERE CustomerID = ?;
";
$stmt = $conn->prepare($sql);
$stmt->execute([$id, $id, $id]);

$customer = $stmt->fetch(PDO::FETCH_ASSOC);

$stmt->nextRowset();
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);

$stmt->nextRowset();
$addresses = $stmt->fetchAll(PDO::FETCH_ASSOC);

Pour SQLSRV, utilisez sqlsrv_next_result pour avancer entre les ensembles de résultats.

Activez plusieurs ensembles de résultats actifs lorsque vous en avez besoin

Multiple Active Result Sets (MARS) permet à une seule connexion d’avoir plusieurs instructions actives. Sans MARS, vous ne pouvez pas émettre une nouvelle requête sur une connexion qui a encore un ensemble de résultats ouvert. Les deux pilotes activent MARS par défaut. Pour l’éteindre, mets MultipleActiveResultSets=false ta chaîne de connexion. Voir Désactiver les ensembles de résultats actifs multiples (MARS).

MARS est pratique mais pas gratuit. Chaque ensemble de résultats actif consomme des ressources côté serveur. Préfèrent consommer un ensemble de résultats complet avant d’en commencer un autre. Utilisez MARS pour débloquer des motifs de curseurs réellement imbriqués.

Accordez des déclarations préparées

Les instructions préparées empêchent le pilote de re-parsasser SQL sur le serveur, et elles permettent de lier en toute sécurité des entrées non fiables comme paramètres.

Préfère les préparations locales

PDO_SQLSRV peut préparer des instructions en deux modes. Les préparations natives envoient le texte SQL au serveur une fois et réutilisent l’instruction analysée à chaque exécution, n’envoyant que les valeurs des paramètres sur chaque execute()fichier . Les prévisions émulées conservent le texte SQL dans le client et reconstruisent une chaîne SQL complète avec des paramètres interpolés à chaque exécution.

Réglez PDO::ATTR_EMULATE_PREPARES => false pour que le pilote utilise des préparations natives. Les préparations natives permettent à SQL Server de mettre en cache et de réutiliser le plan de requête, et elles évitent de re-analyser le texte SQL à chaque exécution.

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

Réutiliser les déclarations préparées

Préparez-vous une fois, exécutez plusieurs. Chaque prepare() appel coûte une allocation de handle ODBC et un parse côté serveur. Dans une boucle chaude, maintenez l’objet $stmt vivant et appelez execute() à l’intérieur de la boucle :

<?php
// Fast: one prepare, many executes.
$stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
foreach ($orderLines as $line) {
    $stmt->execute([$line["qty"], $line["productId"]]);
}

// Slow: re-prepares the same SQL on every iteration.
foreach ($orderLines as $line) {
    $stmt = $conn->prepare("UPDATE dbo.Inventory SET Stock = Stock - ? WHERE ProductID = ?");
    $stmt->execute([$line["qty"], $line["productId"]]);
}

Attention à TOP (?) et IN (?, ?, ...)

TOPnécessite des parenthèses autour d’un marqueur de paramètre, SELECT TOP (?) ..., afin que SQL Server puisse analyser le nombre de lignes comme un paramètre. IN (?, ?, ?, ?) nécessite un nombre de places fixes au moment de la préparation. Pour les tailles de liste dynamiques IN , soit construire la chaîne de place à partir d’un nombre d’entiers validé, soit passer la liste comme un paramètre à valeurs de table.

Caution

N’interpolez jamais les entrées brutes de l’utilisateur dans le texte SQL (y compris le nombre de placeholder). Lancez le compte avec (int) avant de construire la chaîne provisoire, et passez toujours les valeurs execute() réelles sous forme de paramètres.

Gérer les curseurs et la mémoire

Le type de curseur par défaut est PDO::CURSOR_FWDONLY, un tuyau d’incendie uniquement en avant. Il diffuse les lignes vers PHP une par une et ne met pas de mémoire tampon, donc un grand ensemble de résultats est limité par la mémoire de ligne au lieu du nombre total de lignes. C’est généralement ce que vous voulez.

Utilisez des curseurs tamponés uniquement lorsque vous devez reculer ou compter les lignes

PDO::SQLSRV_CURSOR_BUFFERED (un curseur statique tamponné côté client) récupère l’ensemble des résultats dans la mémoire PHP dès le départ. Cette approche vous permet d’appeler rowCount(), de chercher en arrière, puis de réutiliser l’énoncé. Par défaut, le tampon est limité à 10 240 Ko (10 Mo) via PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, et une requête dont l’ensemble de résultats dépasse les rendements false de la limite au lieu d’une mémoire PHP débordante. Vous pouvez augmenter la limite vers la limite mémoire PHP, mais cela échange false un retour contre une véritable Allowed memory size exhausted erreur fatale lorsqu’une requête dépasse la capacité du nouveau plafond. Accordez délibérément. Voir Types de curseurs (PDO_SQLSRV).

Les curseurs défilables côté serveur (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMIC, PDO::SQLSRV_CURSOR_KEYSET) tampon sur le serveur au lieu du client, afin qu’ils ne consomment pas de mémoire PHP. Cependant, ils retiennent les ressources côté serveur pendant toute la durée du curseur et sont plus lents par rangée que les données uniquement en avant.

Utilisez le mode avant uniquement par défaut pour les lectures en streaming. Utilisez le côté client tamponné pour de petits ensembles de résultats quand vous en avez besoin rowCount() , ou le défilement vers l’arrière. Évitez les curseurs défilables côté serveur, sauf si vous faites quelque chose de précis.

<?php
// Fast, low memory: default forward-only, one row at a time
$stmt = $conn->prepare("SELECT OrderID, Total FROM dbo.Orders");
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    // ...
}

// Buffered: only when you need rowCount() or seeking
$stmt = $conn->prepare("SELECT * FROM dbo.SmallLookup", [
    PDO::ATTR_CURSOR                    => PDO::CURSOR_SCROLL,
    PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED,
]);
$stmt->execute();
$rowCount = $stmt->rowCount();

Pour une analyse complète, voir Types de curseurs (PDO_SQLSRV) et Types de curseurs (SQLSRV).

Flux de grandes valeurs binaires et de caractères

Pour varbinary(max), varchar(max),nvarchar(max),xml et d’autres grands types, utilisez des flux PHP au lieu de matérialiser la valeur entière en mémoire :

<?php
$stmt = $conn->prepare("SELECT Name, PhotoBlob FROM dbo.Products WHERE ProductID = ?");
$stmt->execute([$id]);
$stmt->bindColumn("PhotoBlob", $photo, PDO::PARAM_LOB);
$stmt->fetch(PDO::FETCH_BOUND);

// $photo is a stream resource; write it directly to disk without loading it all
$outFile = fopen("/tmp/photo.bin", "wb");
stream_copy_to_stream($photo, $outFile);
fclose($outFile);

Pour insérer ou mettre à jour avec de grandes valeurs, utilisez SendStreamParamsAtExec=false dans SQLSRV pour envoyer des données de flux en blocs après sqlsrv_execute(). Pour plus de détails, voir Envoyer des données en tant que flux.

Définir les délais d’expiration appropriés

Les délais d’attente sont autant des réglages de performance que des réglages de fiabilité. Les longues demandes suspendues tiennent les connexions de la piscine et privent d’autres demandes.

Délai d'expiration de l'instruction

Fixez une limite de temps par instruction pour qu’une requête incontrôlée ne retiennent pas indéfiniment une connexion de pool. Pour PDO_SQLSRV :

<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();

Pour SQLSRV, passez "QueryTimeout" => 30 le tableau d’options à sqlsrv_query ou sqlsrv_prepare.

Définissez une valeur qui correspond à votre charge de travail. Pour une requête web synchrone, 15 à 30 secondes est la normale. Pour un travail en série de background, plusieurs minutes peuvent être raisonnables. Ne réglez jamais le délai d’attente à zéro (illimité) dans une requête web.

Délai de connexion dépassé

LoginTimeoutdans la chaîne de connexion, le pilote attend combien de temps il faut pour établir une connexion. Définissez une valeur explicite lors de la connexion à Azure SQL Database ou Azure SQL Managed Instance afin que les démarrages à froid et les basculements de groupe de basculement ne bloquent pas indéfiniment le client. Des valeurs allant de 30 à 90 secondes fonctionnent bien pour la plupart des charges de travail cloud. Pour plus de détails sur la dimensionnation LoginTimeout et ConnectRetryCount * ConnectRetryInterval les modes de défaillance qui en résultent, voir Délai d’attente de connexion. Pour la référence des options, voir Options de connexion.

Acheminer les charges de travail en lecture seule vers une réplique

Pour les requêtes en lecture seule sur une base de données dans un groupe de disponibilité Always On, Azure SQL Managed Instance, ou Azure SQL Database avec un scal-out en lecture ou une géo-réplice, ajoutez ApplicationIntent=ReadOnly à votre chaîne de connexion :

<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
       "Encrypt=true;ApplicationIntent=ReadOnly";

Le routage en lecture seule envoie la connexion vers une réplique secondaire synchronisée, déchargeant le travail du primaire. Combinez avec MultiSubnetFailover=true pour la connexion la plus rapide aux auditeurs de groupes de disponibilité multi-sous-réseau.

Observez les performances du serveur

Le timing côté client indique seulement combien de temps une requête a duré d’un bout à l’autre. Pour comprendre pourquoi c'était lent, utilisez les diagnostics intégrés de SQL Server.

Magasin des requêtes (magasin de requêtes)

Magasin des requêtes capture les plans d’exécution, les statistiques d’exécution et les statistiques d’attente pour chaque requête dans la base de données. Il est activé par défaut sur Azure SQL Database, Azure SQL Managed Instance et SQL Database dans Fabric. Sur SQL Server, activez-le par base de données :

ALTER DATABASE <database_name> SET QUERY_STORE = ON;

Utilisez ensuite les rapports Magasin des requêtes de SQL Server Management Studio pour trouver vos requêtes les plus lentes et les plus fréquemment exécutées. Voir Surveiller la performance avec le Magasin des requêtes.

Azure SQL Query Performance Insight

Pour Azure SQL Database, le Query Performance Insight du portail Azure affiche automatiquement les requêtes qui consomment le plus de ressources sans aucune configuration. Pour plus d’informations, consultez Query Performance Insight pour Azure SQL Database.

SET STATISTICS pour une enquête unique

Pour une requête unique que vous souhaitez profiler, exécutez-la dans SQL Server Management Studio avec les statistiques activées :

SET STATISTICS TIME ON;
SET STATISTICS IO ON;

-- your query here

Des lectures logiques élevées signifient presque toujours un index manquant ou inutilisable. Un temps CPU élevé avec peu de lectures logiques signifie généralement un mauvais plan (détection de paramètres, une conversion implicite empêchant l’utilisation de l’index, ou une fonction scalaire qui empêche le parallélisme).

Événements étendus pour le traçage au niveau du pilote

Pour voir exactement ce que le pilote envoie à SQL Server (y compris les valeurs réelles des paramètres qu’il interpole), capturez une session d’événements étendus en utilisant les rpc_completed événements et sql_batch_completed .

Liste de contrôle de performance

Utilisez cette liste de contrôle comme un examen préalable au déploiement de toute application PHP connectée à SQL Server :

Area Vérifier Reference
Connection Le pooling de connexions est activé et configuré pour la plateforme Gérer les connexions efficacement
Connection L’application réutilise les connexions dans une requête et n’ouvre pas les connexions par requête Réutilisez la connexion dans une requête
Connection LoginTimeoutcouvre les démarrages à froid et le basculement pour Azure SQL Délai d’expiration de connexion
Requête Les requêtes ne sélectionnent que les colonnes nécessaires, non SELECT * Sélectionnez uniquement les colonnes que vous utilisez
Requête Le filtrage se fait en SQL, pas en PHP avec array_filter Prenez seulement les rangées dont vous avez besoin
Requête Les grands ensembles de résultats sont paginés avec OFFSET ... FETCH Paginar de grands ensembles de résultats
Requête Ensemble de procédures stockées SET NOCOUNT ON Préfère SET NOCOUNT ON
Inserts Les inserts en bloc utilisent des paramètres à valeurs de table, et non des boucles par ligne Insérer les données efficacement
Statements PDO::ATTR_EMULATE_PREPARES est défini sur false Préfère les préparations locales
Statements L’application réutilise des instructions préparées entre les exécutions Réutiliser les déclarations préparées
Cursors L’application utilise le curseur par défaut uniquement en avant, sauf si un tampon est nécessaire Gérer les curseurs et la mémoire
Memory Les grandes valeurs binaires et de caractères sont diffusées en streaming, non matérialisées Flux de grandes valeurs binaires et de caractères
Délais d’expiration Le délai d’expiration de l’instruction est défini sur toutes les requêtes destinées à l’utilisateur Délai d’expiration de l’instruction
Routage Charges de travail en lecture seule définies ApplicationIntent=ReadOnly là où une réplique existe Charges de travail en lecture seule de routes
Observability Magasin des requêtes est activé et examiné régulièrement Magasin des requêtes