Ladění výkonu pro Microsoft ovladače pro PHP pro SQL Server

Stáhnout ovladač PHP

Tento článek popisuje, jak psát rychlý PHP kód na SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics a SQL databázi v Microsoft Fabric. Pokyny se vztahují jak na SQLSRV, tak na PDO_SQLSRV, které obsahují stejný základní Microsoft ODBC ovladač pro SQL Server.

Začněte s největšími změnami

Pokud můžete provést jen tři změny, udělejte tyto změny:

  • Povolte poolování připojení. Navázání nového TLS připojení k SQL Server trvá desítky až stovky milisekund v závislosti na síťové cestě a TLS vyjednávání. Opětovné použití sdílených spojení eliminuje tyto náklady na požadavek. Viz Efektivně spravujte spojení.
  • Načítej jen ty sloupce a řádky, které potřebuješ. SELECT * a neomezené dotazy jsou nejčastějšími příčinami pomalých koncových bodů. Podívejte se na dotaz pouze na to, co potřebujete.
  • Pro hromadné vklady použijte parametry s hodnotami tabulky. Pro stovky řádků a více jsou parametry s tabulkovými hodnotami (TVP) obvykle mnohem rychlejší než řádky po řádku INSERT a škálují lineárně s počtem řádků. Viz Vložit data efektivně.

Efektivně spravujte spojení

Navázání spojení je nejnákladnější operace, kterou ovladač vykonává. Téměř každé šetření výkonu v PHP končí opravou správy spojení.

Povolení sdružování připojení

Pooling znovu využívá ODBC spojení napříč PHP požadavky místo jejich odstraňování na konci požadavku. Objekt connection je zamítnut, když váš skript skončí, ale základní ODBC handle zůstává živý v poolu správce ovladačů ODBC a je znovu použit dalším požadavkem, který žádá stejný připojovací řetězec.

Windows: Pooling připojení je zapnutý ve výchozím nastavení. Pro potvrzení tuto ConnectionPooling možnost vynechte ve svém DSN. Pro deaktivaci poolingu pro ladění nastavte ConnectionPooling=0.

Linux a macOS: Pooling připojení není na těchto platformách DSN možností. Povolte ho ve správci ovladačů nastavením Pooling=Yes v [ODBC] sekci odbcinst.ini, a nastavte kladný CPTimeout bod pod strofou ovladače. Příklady:

[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

Najděte skutečnou cestu knihovny pomocí odbcinst -q -d -n "ODBC Driver 18 for SQL Server" nebo ls /opt/microsoft/msodbcsql18/lib64/. Název souboru vkládá nainstalovanou verzi ovladače ODBC a mění se s každým vydáním.

CPTimeout (za pár sekund) Ovládá, jak dlouho zůstávají nečinné spoje v poolu, než se uzavře. Nastavte ho dostatečně vysoko, aby většina požadavků našla spojení, ale zároveň dostatečně nízko, aby se zastaralá připojení k failover-over serveru poměrně rychle ukončila. 60 až 300 sekund funguje dobře pro většinu webových zátěží.

Podrobnosti viz sdružování připojení.

Pochopte náklady na první dotaz

Ve výchozím nastavení je povoleno více aktivních sad výsledků (MARS). Když jsou MARS i pooling spojení aktivní, ovladač resetuje poolované spojení při prvním dotazu a tento reset ignoruje časový limit dotazu, který jste nastavili pro ten první dotaz. Pozdější dotazy na stejné spojení obvykle respektují časovou pauzu. Pokud nastavíte agresivní timeouty pro první dotaz u sdružené pracovní zátěže, zohledněte toto chování, nebo vypněte MARS, MultipleActiveResultSets=false pokud ho nepotřebujete. Viz poznámku o MARS a sdružování v sekci Connection pooling.

Trvalá PDO připojení nejsou podporována

PDO_SQLSRV odmítá PDO::ATTR_PERSISTENT. Nastavení na konstruktoru hodí:

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

Používejte ODBC pooling připojení pro opakované použití křížových požadavků. Je to mechanismus nativní pro ovladače, funguje jak pro PDO_SQLSRV, tak pro SQLSRV a ukončuje nečinné připojení (CPTimeoutcož také udržuje Microsoft Entra token refresh poctivý).

Znovu použijte spojení v rámci požadavku

I při poolování znamená otevření nového PDO nebo SQLSRV připojení ODBC zpět pro načtení a ověření poolovaného handle. Otevřete spojení jednou za každý požadavek a pošlete ho do každé funkce, která ho potřebuje.

Tip

Stačí kontejner pro injekci závislostí nebo líný přístupák. Jde o to, aby se nešlo new PDO(...) přímo uprostřed obslužného modulu požadavků.

Dotazujte se pouze na to, co potřebujete

Síťové okružní cesty a materializace výsledků dominují latenci dotazů u většiny PHP zátěží. Opravy jsou stejné, které platí pro každou vrstvu přístupu k databázi.

Vyberte pouze sloupce, které používáte

SELECT * Vytahuje všechny sloupce, včetně sloupců varchar(max) a varbinary(max), které převyšují data, která skutečně spotřebujete. Pojmenujte sloupce:

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

Přines jen ty řádky, které potřebuješ

Push filtrování na SQL Server. Nikdy nenačítejte celou tabulku do PHP jen proto, abyste filtrovali v foreach smyčce.

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

Stránkování velkých sad výsledků

Pro zobrazení seznamu, které ukazuje několik stovek řádků z milionů, nevracejte všechny řádky a nechte to na klientovi, ať si to vyřeší. Použijte stránkování na straně serveru s 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);
}

Vyberte správnou metodu načtení

  • Použijte fetch(PDO::FETCH_ASSOC) v smyčce pro streamování iterací, když nepotřebujete všechny řádky najednou v paměti.
  • Použijte fetchAll(PDO::FETCH_ASSOC) ji, když volající skutečně potřebuje celou sadu (například při vykreslení plné JSON odpovědi).
  • PoužijtefetchColumn(), když vám záleží jen na jednom skaláru (, SUMCOUNT, nebo MAX).
  • Používejte PDO::FETCH_KEY_PAIR nebo PDO::FETCH_UNIQUE vytvářejte vyhledávací slovníky bez druhého průchodu.

Numerické režimy načítání (PDO::FETCH_NUM) jsou o něco rychlejší než asociativní režimy načítání, protože přeskakují vytváření mapy názvů sloupců. Preferujte jasnost; Přepínat jen tehdy, když profiler označí fetch overhead jako významný.

Preferujte SET NOCOUNT ON při uložených postupech a dávkách

Každý INSERTpříkaz , UPDATE, a DELETE vrací DONE_IN_PROC token s postiženým počtem řádků, který PHP obvykle zahazuje. Token nepřidává zpáteční cestu, ale každý z nich stále stojí bajty na kabelu a trochu práce s ovladači. V procedurách s více příkazy nebo dávkování, které spouštějí stovky příkazů za jeden hovor, se úspory sčítají. Vypněte to:

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;

Efektivně vkládejte data

Vyberte správnou metodu vkládání podle toho, kolik řad přesouváte. Špatná volba může být 100krát pomalejší.

Méně než asi 100 řádků: připravený výrok v smyčce

Pro malé dávky proveďte jedno připravené prohlášení v cyklu:

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

Obalte smyčku v transakci, aby všechny inserty commitovaly jako jedna jednotka a log se nemusel po každém řádku vymazovat:

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

Stovky až miliony řádků: parametry s tabulkovou hodnotou

Parametry s tabulkovými hodnotami (TVP) odešlou celou dávku do SQL Server najednou a umožní SQL Server zpracovat sadu jako jeden příkaz. Pro dávky o stovkách a více řádků jsou TVP obvykle mnohem rychlejší než smyčka připravených výkazů a škálují lineárně s počtem řádků.

Nejprve vytvořte typ tabulky na serveru:

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

PDO_SQLSRV předává TVP jako asociativní pole, jehož klíčem je název typu a hodnotou je sada řádků. Spojte ji s 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();

Pro nevýchozí schéma předáme schéma jako další prvek pole: ["ProductTableType" => $rows, "Sales"]. Pro příklady procedurální syntaxe a uložených procedur SQLSRV viz Použít parametry s tabulkovými hodnotami.

Miliony řádků: bcp nebo BULK INSERT

Pro skutečně hromadné operace (zatížení datového skladu, počáteční migrace) použijte bcp nebo BULK INSERT místo PHP. Zapisujte data do odděleného nebo nativního formátu souboru, pak spusť bcp nebo BULK INSERT z plánované úlohy, ETL kroku nebo admin skriptu.

Caution

Pokud investujete do bcp z PHP s shell_exec() nebo proc_open(), nikdy neinterpolujte nedůvěryhodný vstup do příkazové řádky. Používejte escapeshellarg() na každý argument a preferuji spuštění načítání mimo pásmo místo cesty webového požadavku.

Snižte zpáteční jízdy tam a zpět

Každá síťová cesta mezi PHP a SQL Server má pevné náklady. Když pošlete pět výpisů najednou, zaplatíte tuto cenu jednou místo pětikrát.

Pro související práci, která běží dohromady, vložte příkazy do jedné dávky a spotřebujte všechny výsledky v množině:

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

Pro SQLSRV používejte sqlsrv_next_result postup mezi sadami výsledků.

Povolte více aktivních sad výsledků, když to potřebujete

Více aktivních sad výsledků (MARS) umožňuje jednomu spojení mít více aktivních příkazů. Bez MARS nemůžete zadat nový dotaz na spojení, které má stále otevřenou sadu výsledků. Oba ovladače MARS ve výchozím nastavení zapínají (MARS). Pro vypnutí nastavte MultipleActiveResultSets=false svůj připojovací řetězec. Viz Disable více aktivních sad výsledků (MARS).

MARS je pohodlný, ale není zdarma. Každá aktivní sada výsledků spotřebovává zdroje na straně serveru. Raději před začátkem dalšího spotřebujte kompletní výsledek. Použijte MARS k odblokování skutečně vnořených kurzorových vzorů.

Tune připravená prohlášení

Připravené příkazy šetří ovladač před opětovným parsováním SQL na serveru a umožňují vám bezpečně navázat nedůvěryhodné vstupy jako parametry.

Preferujte místní přípravky

PDO_SQLSRV může připravovat výkazy ve dvou režimech. Nativní příprava odesílá SQL text serveru jednou a znovu použije parsovaný příkaz pro každé vykonání, přičemž na každém execute(). Emulované přípravy uchovávají SQL text v klientovi a znovu vytvářejí celý SQL řetězec s parametry interpolovanými při každém vykonání.

Nastavte PDO::ATTR_EMULATE_PREPARES => false tak, aby ovladač používal nativní přípravy. Nativní přípravy umožňují SQL Server cachovat a znovu používat plán dotazů a vyhýbají se opětovnému analyzování SQL textu při každém vykonání.

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

Znovu použít připravené výroky

Připrav se jednou, poprav mnoho. Každý prepare() hovor stojí alokaci ODBC handle a serverovou analýzu. V horké smyčce udržujte $stmt objekt naživu a uvnitř smyčky volejte execute() :

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

Dávejte pozor na TOP (?) a IN (?, ?, ...)

TOPvyžaduje závorky kolem parametru , SELECT TOP (?) ...takže SQL Server může počet řádků parsovat jako parametr. IN (?, ?, ?, ?) vyžaduje pevný počet zástupců v době přípravy. Pro dynamické IN velikosti seznamů buď sestavte zástupný řetězec z ověřeného počtu celých čísel, nebo seznam předejte jako parametr s tabulkovou hodnotou.

Caution

Nikdy neinterpolujte surový uživatelský vstup do SQL textu (včetně počtu zástupců). Před vytvořením placeholder řetězce sesílejte počítadlo s a (int) vždy přeneste skutečné hodnoty jako execute() parametry.

Správa kurzorů a paměti

Výchozí typ kurzoru je PDO::CURSOR_FWDONLY, hasicí hadice pouze vpřed. Streamuje řádky do PHP jeden po druhém a nebufferuje, takže velká množina výsledků je omezena řádkovou pamětí místo celkového počtu řádků. To je většinou to, co chcete.

Používejte bufferované kurzory jen tehdy, když potřebujete jít zpět nebo počítat řádky

PDO::SQLSRV_CURSOR_BUFFERED (statický kurzor na straně klienta) načítá celou sadu výsledků do paměti PHP předem. Tento přístup vám umožňuje volat rowCount(), hledat zpět a znovu použít tvrzení. Výchozí nastavení je buffer omezen na 10 240 KB (10 MB) přes PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, a dotaz, jehož výsledná sada přesahuje limit, se vrací false místo přetékající PHP paměti. Můžete zvýšit limit k PHP paměťovému limitu, ale tím byste vyměnili false návrat za skutečnou fatální chybu Allowed memory size exhausted , když dotaz přeroste nový limit. Ladit to záměrně. Viz Typy kurzorů (PDO_SQLSRV).

Serverově posouvatelné kurzory (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMIC, ) PDO::SQLSRV_CURSOR_KEYSETse vyrovnávají na serveru místo na klientovi, takže nespotřebovávají PHP paměť. Nicméně uchovávají serverové zdroje po celou dobu kurzoru a jsou pomalejší na řádek než pouze forward.

Používejte výchozí přesměrování pouze pro čtení ve streamování. Používejte buffered na klientské straně pro malé sady výsledků, když je potřeba rowCount() , nebo zpětné posouvání. Vyhněte se posouvatelným kurzorům na serveru, pokud neděláte něco konkrétního.

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

Pro kompletní rozbor viz Typy kurzorů (PDO_SQLSRV) a Typy kurzorů (SQLSRV).

Proudové velké binární a znakové hodnoty

Pro varbinary(max), varchar(max), nvarchar(max), xml a další velké typy použijte PHP streamy místo materializace celé hodnoty v paměti:

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

Pro vkládání nebo aktualizaci s velkými hodnotami použijte SendStreamParamsAtExec=false v SQLSRV pro odesílání datových proudů v částech po sqlsrv_execute(). Pro podrobnosti viz Odesílat data jako tok.

Nastavte vhodné časové limity

Časové limity jsou stejně výkonnostní nastavení jako nastavení spolehlivosti. Dlouho visící dotazy drží připojení k bazénu a hladoví jiné požadavky.

Vypršení časového limitu příkazu

Nastavte časový limit na příkaz, aby nekontrolovaný dotaz nedržel spojení s poolem navždy. Pro PDO_SQLSRV:

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

Pro SQLSRV předejte "QueryTimeout" => 30 pole možností do sqlsrv_query nebo sqlsrv_prepare.

Nastavte hodnotu, která odpovídá vaší pracovní zátěži. Pro synchronní webový požadavek je obvykle 15 až 30 sekund. Pro batch práci na pozadí může být rozumných pár minut. Nikdy nenastavujte timeout na nulu (neomezenou) v webovém požadavku.

Časový limit přihlášení

LoginTimeoutv připojovací řetězec ovládá, jak dlouho ovladač čeká na navázání spojení. Nastavte explicitní hodnotu při připojení k Azure SQL Database nebo Azure SQL Managed Instance, aby cold starty a failover-group failovery klienta nezasekly navždy. Hodnoty od 30 do 90 sekund fungují dobře pro většinu cloudových zátěží. Podrobnosti o velikosti LoginTimeout a ConnectRetryCount * ConnectRetryInterval následných režimech selhání viz Časový limit připojení. Pro referenci k možnosti viz Možnosti připojení.

Směrování zátěží pouze pro čtení do repliky

Pro dotazy pouze pro čtení vůči databázi ve skupině Always On availability, Azure SQL Managed Instance nebo Azure SQL Database s read scale-out nebo geo-replikou, přidejte ApplicationIntent=ReadOnly do svého připojovací řetězec:

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

Směrování pouze pro čtení posílá spojení do synchronizované sekundární repliky, čímž se práce odkládá od primární části. Spojte s pro MultiSubnetFailover=true nejrychlejší připojení k skupinovým posluchačům dostupnosti více subsítí.

Sledujte výkon ze serveru

Časování na straně klienta vám řekne jen, jak dlouho trval dotaz od začátku do konce. Chcete-li zjistit, proč je pomalý, použijte vestavěnou diagnostiku SQL Server.

úložiště dotazů

Query Store zachycuje plány provádění, statistiky běhu a čekací statistiky pro každý dotaz v databázi. Je to ve výchozím nastavení povoleno v Azure SQL Database, Azure SQL Managed Instance a SQL databázi ve Fabric. Na SQL Server to povolte pro každou databázi:

ALTER DATABASE <database_name> SET QUERY_STORE = ON;

Poté použijte SQL Server Management Studio Query Store reporty k nalezení nejpomalejších a nejčastěji prováděných dotazů. Viz Monitorování výkonu pomocí Query Store.

Azure SQL Query Performance Insight

Pro Azure SQL Database automaticky zobrazuje Query Performance Insight portálu Azure nejvíce náročné dotazy bez jakékoli konfigurace. Další informace najdete v tématu Query Performance Insight pro službu Azure SQL Database.

SET STATISTICS pro jednorázové vyšetřování

Pro jeden dotaz, který chcete profilovat, ho spusť v SQL Server Management Studio se zapnutými statistikami:

SET STATISTICS TIME ON;
SET STATISTICS IO ON;

-- your query here

Vysoké logické hodnoty téměř vždy znamenají chybějící nebo nepoužitelný index. Vysoká doba CPU při nízkém počtu logických čtení obvykle znamená špatný plán (sniffing parametrů, implicitní konverze bránící použití indexu, nebo skalární funkce, která zabraňuje paralelismu).

Rozšířené události pro trasování na úrovni řidiče

Chcete-li přesně zjistit, co ovladač posílá SQL Server (včetně skutečných hodnot parametrů, které interpoluje), zachytíte relaci Extended Events pomocí a rpc_completedsql_batch_completed events.

Kontrolní seznam výkonu

Použijte tento kontrolní seznam jako předběžnou kontrolu jakékoli PHP aplikace, která se připojuje k SQL Server:

Oblasti Zkontrolovat Reference
Connection Pooling připojení je povoleno a nakonfigurováno pro platformu Efektivně spravujte spojení
Connection Aplikace znovu používá spojení v rámci požadavku a neotevírá spojení na dotaz Znovu použijte spojení v rámci požadavku
Connection LoginTimeoutcovers cold starts and failover for Azure SQL Časový limit přihlášení
Query Dotazy vybírají pouze potřebné sloupce, ne SELECT * Vyberte pouze sloupce, které používáte
Query Filtrování probíhá v SQL, ne v PHP s array_filter Přines jen ty řádky, které potřebuješ
Query Velké množiny výsledků jsou stránkovány s OFFSET ... FETCH Stránkujte velké množiny výsledků
Query Množina uložených procedur SET NOCOUNT ON Preferovat SET NOCOUNT ON
Vložení Hromadné vkládání používají tabulkové parametry, nikoli smyčky po řádcích Efektivně vkládejte data
Statements PDO::ATTR_EMULATE_PREPARES je nastavená na false Preferujte místní přípravky
Statements Aplikace znovu využívá připravené příkazy napříč prováděním Znovu použít připravené výroky
Cursors Aplikace používá výchozí kurzor pouze pro dopředání, pokud není potřeba bufferování Správa kurzorů a paměti
Memory Velké binární a znakové hodnoty jsou streamovány, nikoli materializovány Proudové velké binární a znakové hodnoty
Časové limity Timeout příkazu je nastaven na všechny dotazy orientované na uživatele Časový limit výroku
Směrování Pouze čtecí pracovní zátěže nastavené ApplicationIntent=ReadOnly tam, kde existuje replika Pracovní zátěže pouze pro čtení trasy
Observability Query Store je aktivován a pravidelně kontrolován Úložiště dotazů