Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье рассматривается, как писать быстрый PHP-код на SQL Server, База данных SQL Azure, Управляемый экземпляр SQL Azure, Azure Synapse Analytics и SQL Database в Microsoft Fabric. Эти рекомендации применяются как к SQLSRV, так и к PDO_SQLSRV, которые обертывают один и тот же базовый Microsoft ODBC Driver для SQL Server.
Начните с самых значимых изменений
Если вы можете внести только три изменения, сделайте следующие изменения:
- Включите пулинг соединений. Установление нового TLS-соединения с SQL Server занимает от десятков до сотен миллисекунд в зависимости от сетевого пути и согласования TLS. Повторное использование соединений из пула устраняет эти затраты для каждого запроса. См. Эффективное управление соединениями.
- Берите только те столбцы и строки, которые вам нужны.
SELECT *и неограниченные запросы являются наиболее распространёнными причинами медленной работы эндпоинтов. См. «Запрашивайте только то, что вам нужно». - Используйте табличные параметры для пакетной вставки. Для сотен строк и более табличные параметры (TVP) обычно работают значительно быстрее, чем построчные операторы
INSERT, и масштабируются линейно по числу строк. См. Вставлять данные эффективно.
Эффективное управление соединениями
Установление соединения — самая дорогостоящая операция, которую выполняет водитель. Почти каждое исследование производительности PHP заканчивается исправлением управления соединением.
Включение пула подключений
Пулинг повторно использует ODBC-соединения между PHP-запросами вместо того, чтобы разрушать их в конце запроса. Объект соединения отбрасывается после завершения скрипта, но базовый дескриптор ODBC остаётся живым в пуле менеджера драйверов ODBC и повторно используется следующим запросом, который требует ту же строка подключения.
Windows: Пул соединений включен по умолчанию. Для подтверждения не включайте параметр ConnectionPooling в строку DSN. Чтобы отключить пул для отладки, установите ConnectionPooling=0.
Linux и macOS: Пул соединений не является опцией DSN на этих платформах. Включите это в диспетчере драйверов, установив Pooling=Yes в разделе [ODBC] в odbcinst.ini и задав положительное значение CPTimeout в секции драйвера. Рассмотрим пример.
[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
Найдите фактический путь к библиотеке с помощью odbcinst -q -d -n "ODBC Driver 18 for SQL Server" или ls /opt/microsoft/msodbcsql18/lib64/. Имя файла встраивает установленную версию драйвера ODBC и меняется с каждым релизом.
CPTimeout (в секундах) определяет, как долго неактивные соединения остаются в пуле, прежде чем они будут закрыты. Установите значение достаточно высоким, чтобы большинство запросов находили соединение из пула, но достаточно низким, чтобы устаревшие соединения с сервером после аварийного переключения выводились из эксплуатации достаточно быстро. 60–300 секунд хорошо подходит для большинства веб-нагрузок.
Подробнее см. «Пул соединений».
Понимайте стоимость первого запроса
По умолчанию множественные активные результирующие наборы (MARS) включены. Когда MARS и пул соединений активны, драйвер сбрасывает пуловое соединение при первом запросе, и этот сброс игнорирует установленный вами тайм-аут для первого запроса. Последующие запросы по тому же соединению обычно учитывают тайм-аут. Если вы задаёте агрессивные тайм-ауты для первого запроса для рабочей нагрузки, использующей пул подключений, учитывайте это поведение или отключите MARS с помощью MultipleActiveResultSets=false, если он не требуется. См. примечание о MARS и пулировании подключений в разделе «Пулирование подключений».
Постоянные PDO-соединения не поддерживаются
PDO_SQLSRV отвергает PDO::ATTR_PERSISTENT. Если задать это в конструкторе, возникает исключение:
SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.
Используйте пул соединений ODBC для повторного использования по перекрёстным запросам. Это нативный механизм драйвера, который работает как для PDO_SQLSRV, так и для SQLSRV, и закрывает неактивные соединения при CPTimeout (что также обеспечивает корректное обновление токена Microsoft Entra).
Повторное использование соединения внутри запроса
Даже при использовании пула соединений открытие нового соединения PDO или SQLSRV требует полного обмена с ODBC, чтобы получить и проверить дескриптор из пула. Открывайте соединение один раз для каждого запроса и передавайте его во все функции, которым оно нужно.
Подсказка
Контейнер для инъекции зависимостей или ленивый аксессуар — это достаточно. Суть в том, чтобы избегать new PDO(...) в середине обработчика запроса.
Задавайте только то, что вам нужно
Для большинства PHP-приложений основной вклад в задержку выполнения запросов вносят сетевые обмены и материализация наборов результатов. Исправления те же, что применимы к каждому слою доступа к базе данных.
Выбирайте только те столбцы, которые используете
SELECT * извлекает все столбцы, включая столбцы varchar(max) и varbinary(max), которые значительно превосходят по объёму данные, которые вы фактически используете. Назовите колонки:
<?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");
Извлекайте только те строки, которые вам нужны
Push-фильтрация на SQL Server. Никогда не извлекайте всю таблицу в PHP только чтобы фильтровать её в цикле foreach.
<?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);
Разбивайте большие наборы результатов на страницы
Для просмотра списка, который показывает несколько сотен строк из миллионов, не возвращайте все строки и позволяйте клиенту разбираться с этим. Используйте серверную пагинацию с 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);
}
Выберите правильный метод поиска
- Используйте
fetch(PDO::FETCH_ASSOC)в цикле для потоковой итерации, когда не нужны все строки в памяти одновременно. - Используйте
fetchAll(PDO::FETCH_ASSOC)тогда, когда звонящему действительно нужен весь набор (например, отрисовка полного JSON-ответа). - Используй,
fetchColumn()когда тебе важен только один скаляр (aCOUNT,SUM, илиMAX). - Используйте
PDO::FETCH_KEY_PAIRилиPDO::FETCH_UNIQUE, чтобы создать словари поиска без повторного прохода.
Числовые режимы выборки (PDO::FETCH_NUM) немного быстрее ассоциативных режимов, поскольку они пропускают создание отображения имён столбцов. Отдавайте предпочтение ясности; переключайтесь только тогда, когда профилировщик показывает, что накладные расходы на выборку существенны.
Отдавайте предпочтение SET NOCOUNT ON в хранимых процедурах и пакетах
Операторы INSERT, UPDATE и DELETE возвращают токен DONE_IN_PROC с количеством затронутых строк, который PHP обычно отбрасывает. Токен не требует дополнительного цикла обмена, но каждый из них всё равно увеличивает объём данных, передаваемых по сети, и требует небольшой дополнительной обработки со стороны драйвера. В процедуре с несколькими инструкциями или в пакете команд, который выполняет сотни инструкций за один вызов, экономия суммируется. Выключите его:
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;
Эффективная вставка данных
Выбирайте правильный способ вставки, исходя из того, сколько строк вы перемещаете. Неправильный выбор может быть в 100 раз медленнее.
Менее примерно 100 строк: подготовленное утверждение в цикле
Для небольших партий выполните одно подготовленное утверждение в цикле:
<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
$stmt->execute([$p["name"], $p["price"]]);
}
Оберните цикл транзакцией так, чтобы все вставки фиксировались как единое целое, и лог не должен был промываться после каждой строки:
<?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;
}
От сотен до миллионов строк: параметры с таблицьными значениями
Параметры со значением в виде таблицы (TVP) отправляют весь пакет в SQL Server за один цикл обмена и позволяют SQL Server обрабатывать набор как один оператор. Для партий по сотни строк и более, TVP обычно работают гораздо быстрее, чем цикл с подготовленным выпиской, и они масштабируются линейно с количеством строк.
Сначала создайте тип таблицы на сервере:
CREATE TYPE dbo.ProductTableType AS TABLE (
Name NVARCHAR(100),
Price DECIMAL(10, 2)
);
PDO_SQLSRV передаёт TVP в виде ассоциативного массива, ключом которого является имя типа, а значением — набор строк. Свяжите это с 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();
Для нестандартной схемы передайте схему как следующий элемент массива: ["ProductTableType" => $rows, "Sales"]. Для процедурного синтаксиса SQLSRV и примеров хранящихся процедур см. Использовать параметры с таблицными значениями.
Миллионы строк: bcp или BULK INSERT
Для действительно массовых операций (загрузки хранилища данных, начальные миграции) используйте BCP или BULK INSERT вместо PHP. Запишите данные в файл с разграничённым или родным форматом, затем запустите BCP или BULK INSERT с запланированного задания, ETL-шага или администраторского скрипта.
Предостережение
Если вы вызываете bcp из PHP с помощью shell_exec() или proc_open(), никогда не подставляйте непроверенный ввод в командную строку. Используйте escapeshellarg() для каждого аргумента и предпочитайте запускать нагрузку вне полосы, а не по пути веб-запроса.
Сократить количество рейсов туда и обратно
Каждый сетевой переход между PHP и SQL Server имеет фиксированную стоимость. Когда вы отправляете пять выписок одной партией, вы оплачиваете эту стоимость один раз, а не пять раз.
Объедините связанные инструкции в один пакет
Для связанных операций, которые выполняются вместе, объедините операторы в один пакет и обработайте каждый результирующий набор:
<?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);
Для SQLSRV используйте sqlsrv_next_result для перехода между наборами результатов.
Включайте несколько активных наборов результатов, когда это необходимо
Несколько активных наборов результатов (MARS) позволяет одному соединению иметь несколько активных инструкций. Без MARS нельзя отправлять новый запрос на соединение, где всё ещё установлен открытый результат. Оба драйвера по умолчанию включают MARS. Чтобы отключить это, установите MultipleActiveResultSets=false в строке подключения. См. Отключить несколько активных наборов результатов (MARS).
MARS удобен, но не бесплатен. Каждый активный набор результатов потребляет ресурсы на стороне сервера. Предпочтительно полностью обработать набор результатов, прежде чем начинать обработку следующего. Используйте MARS, чтобы разблокировать действительно вложенные узоры курсора.
Настройте подготовленные заявления
Подготовленные выражения избавляют драйвер от повторного разбора SQL-запроса на сервере и позволяют безопасно передавать ненадёжные входные данные в качестве параметров.
Отдавайте предпочтение продуктам местного производства
PDO_SQLSRV могут готовить заявления в двух режимах.
Native Prepares отправляет SQL-текст на сервер один раз и повторно использует разбор оператора для каждого выполнения, отправляя только значения параметров на каждом execute().
Эмулируемые подготовленные выражения хранят SQL-текст на стороне клиента и при каждом выполнении заново формируют полную строку SQL, подставляя параметры.
Установите PDO::ATTR_EMULATE_PREPARES => false, чтобы драйвер использовал нативные подготовленные выражения. Подготовленные инструкции позволяют SQL Server кэшировать и повторно использовать план запроса, а также избежать повторного разбора текста SQL при каждом выполнении.
<?php
$conn = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
Повторное использование подготовленных утверждений
Подготовьтесь один раз, исполняйте много раз. Каждый вызов prepare() требует выделения дескриптора ODBC и синтаксического разбора на стороне сервера. В горячем цикле не давайте объекту $stmt уничтожаться и вызывайте 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"]]);
}
Остерегайтесь TOP (?) и IN (?, ?, ...)
TOPтребует скобок вокруг маркера параметра, SELECT TOP (?) ..., чтобы SQL Server мог парсировать количество строк как параметр.
IN (?, ?, ?, ?) требует фиксированного количества заполнителей на этапе подготовки. Для динамических IN размеров списков либо постройте заполнительную строку из проверенного целого счёта, либо передайте список в виде параметра таблицы.
Предостережение
Никогда не подставляйте необработанные пользовательские данные в текст SQL-запроса (в том числе количество плейсхолдеров). Приведите count с помощью (int) перед созданием строки-заполнителя и всегда передавайте фактические значения через execute() в качестве параметров.
Управление курсорами и памятью
Стандартный тип курсора — PDO::CURSOR_FWDONLY, только вперёд — пожарный шланг. Он передаёт строки в PHP по одному и не буферизирует, поэтому большой набор результатов ограничен памятью строк-буфера, а не общее количество строк. Обычно это то, чего ты хочешь.
Используйте буферизованные курсоры только когда нужно двигаться назад или считать ряды
PDO::SQLSRV_CURSOR_BUFFERED (клиентский буферный статический курсор) заранее загружает весь набор результатов в память PHP. Этот подход позволяет вызывать rowCount(), искать обратно и повторно использовать это утверждение. По умолчанию буфер ограничен 10 240 КБ (10 МБ) через PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, и запрос, набор результатов которого превышает лимит, возвращает false вместо переполнения PHP-памяти. Вы можете повысить ограничение вплоть до лимита памяти PHP, но в этом случае false возврат будет заменён на реальную Allowed memory size exhausted фатальную ошибку, когда запрос превысит новое ограничение. Настраивайте осознанно. См. типы курсора (PDO_SQLSRV).
Прокручиваемые курсоры (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMICPDO::SQLSRV_CURSOR_KEYSET, ) на сервере буферизируются на сервере, а не на клиенте, чтобы не потреблять PHP-память. Однако они удерживают ресурсы на стороне сервера на протяжении всего времени существования курсора и при обработке каждой строки работают медленнее, чем курсоры, допускающие только прямую прокрутку.
Используйте стандартный режим только вперёд для стримингового чтения. Используйте буферизацию на стороне клиента для небольших наборов результатов, когда требуется rowCount() или обратная прокрутка. Избегайте прокручиваемых курсоров на сервере, если только вы не делаете что-то конкретное.
<?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();
Для полного разбора см. типы курсоров (PDO_SQLSRV) и типы курсоров (SQLSRV).
Потоковая передача больших бинарных и символьных значений
Для varbinary(max), varchar(max), nvarchar(max), xml и других крупных типов используйте потоки PHP вместо материализации всего значения в памяти:
<?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);
Для вставки или обновления с большими значениями используйте SendStreamParamsAtExec=false в SQLSRV для отправки потоковых данных блоками после sqlsrv_execute(). Для подробностей см. раздел «Отправить данные как поток».
Установка соответствующих тайм-аутов
Тайм-ауты — это в той же мере параметры производительности, что и параметры надёжности. Длинные запросы задерживают соединения пула и лишают других запросов.
Время простоя оператора
Установите ограничение времени для каждого оператора, чтобы неуправляемый запрос не удерживал соединение пула бесконечно. Для PDO_SQLSRV:
<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();
Для SQLSRV передайте "QueryTimeout" => 30 в массиве параметров в sqlsrv_query или sqlsrv_prepare.
Установите значение, соответствующее вашей нагрузке. Для синхронного веб-запроса обычно составляет 15–30 секунд. Для фоновых пакетных работ несколько минут могут быть разумными. Никогда не устанавливайте тайм-аут на нули (неограниченно) в веб-запросе.
Время ожидания входа
LoginTimeout в строке подключения определяет, как долго драйвер ожидает установки соединения. Установите явное значение при подключении к База данных SQL Azure или Управляемый экземпляр SQL Azure, чтобы холодные запуски и failover-group не зависали клиент навсегда. Значения от 30 до 90 секунд хорошо подходят для большинства облачных нагрузок. Подробнее о подборе размера LoginTimeout по отношению к ConnectRetryCount * ConnectRetryInterval и возникающих в результате режимах отказа см. в разделе Тайм-аут соединения. Для справки с опцией см. Опции подключения.
Направляйте нагрузки, предназначенные только для чтения, в реплику
Для запросов, предназначенных только для чтения, к базе данных в группе доступности Always On, Управляемый экземпляр SQL Azure или База данных SQL Azure с функцией масштабирования чтения или георепликой добавьте ApplicationIntent=ReadOnly в строку подключения:
<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
"Encrypt=true;ApplicationIntent=ReadOnly";
Маршрутизация подключений только для чтения направляет подключение на синхронизированную вторичную реплику, снимая часть нагрузки с основной реплики. Используйте вместе с MultiSubnetFailover=true для максимально быстрого подключения к прослушивателям групп доступности с несколькими подсетями.
Наблюдайте за производительностью сервера
Тайминг на стороне клиента показывает только, сколько времени занимает запрос от начала до конца. Чтобы понять, почему это было медленно, используйте встроенную диагностику SQL Server.
Хранилище запросов
хранилище запросов фиксирует планы выполнения, статистику во время выполнения и статистику ожидания для каждого запроса в базе данных. По умолчанию она включена в База данных SQL Azure, Управляемый экземпляр SQL Azure и SQL Database in Fabric. На SQL Server включите его по базе данных:
ALTER DATABASE <database_name> SET QUERY_STORE = ON;
Затем используйте отчёты Query Store SQL Server Management Studio, чтобы найти самые медленные и часто выполняемые запросы. См. Мониторинг производительности с помощью хранилище запросов.
Аналитика производительности запросов Azure SQL
Для База данных SQL Azure портал Azure Query Performance Insight автоматически показывает самые ресурсоёмкие запросы без какой-либо конфигурации. Дополнительные сведения см. в статье Анализ производительности запросов для Базы данных SQL Azure.
SET STATISTICS для одноразового расследования
Для одного запроса, который вы хотите профилировать, запустите его в SQL Server Management Studio с включённой статистикой:
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- your query here
Высокое число логических чтений почти всегда указывает на отсутствие индекса или невозможность его использования. Большое время работы процессора с низким логическим считыванием обычно означает неудачный план (обнаружение параметров, неявное преобразование, препятствующее использованию индекса, или скалярная функция, препятствующая параллелизму).
Расширенные события для трассировки на уровне драйвера
Чтобы точно увидеть, что драйвер отправляет в SQL Server (включая фактические значения параметров, которые он подставляет), запишите сеанс Extended Events с помощью событий rpc_completed и sql_batch_completed.
Контрольный список производительности
Используйте этот чек-лист для предварительного обзора любого PHP-приложения, подключаемого к SQL Server:
| Area | Проверьте | Reference |
|---|---|---|
| Connection | Пул соединений включён и настроен для платформы | Эффективное управление соединениями |
| Connection | Приложение повторно использует соединения внутри запроса и не открывает соединения по запросу | Повторное использование соединения внутри запроса |
| Connection |
LoginTimeoutохватывает холодные запуски и отказ для Azure SQL |
Тайм-аут входа |
| Запрос | Запросы выбирают только необходимые столбцы, без SELECT * |
Выбирайте только те столбцы, которые используете |
| Запрос | Фильтрация происходит в SQL, а не в PHP array_filter |
Извлекайте только нужные строки |
| Запрос | Большие наборы результатов пагинируются с помощью OFFSET ... FETCH |
Пагинируйте большие наборы результатов |
| Запрос | Набор хранимых процедур SET NOCOUNT ON |
Предпочтение SET NOCOUNT ON |
| Вставки | Пакетные вставки используют табличные параметры, а не построчные циклы | Эффективная вставка данных |
| Statements |
PDO::ATTR_EMULATE_PREPARES установлено в false |
Предпочитайте местные блюда |
| Statements | Приложение повторно использует подготовленные выражения при многократном выполнении | Повторное использование подготовленных утверждений |
| Cursors | Приложение использует курсор только вперёд по умолчанию, если не требуется буферизация | Управление курсорами и памятью |
| Memory | Большие двоичные и символьные значения обрабатываются в потоковом режиме, а не загружаются в память целиком | Поток больших бинарных и символьных значений |
| Timeouts | Для всех пользовательских запросов задан тайм-аут выполнения. | Тайм-аут после перерыва |
| Маршрутизация | Нагрузки только для чтения размещаются ApplicationIntent=ReadOnly там, где есть реплика |
Направляйте нагрузки, предназначенные только для чтения |
| Observability | хранилище запросов активирован и регулярно проверяется | Хранилище запросов |
Связанные материалы
- Пулирование соединений (Microsoft Drivers for PHP for SQL Server)
- Параметры соединения
- Типы курсоров (драйвер PDO_SQLSRV)
- Типы курсоров (драйвер SQLSRV)
- Используйте параметры с таблицами (PHP)
- Устранение неполадок с драйверами Microsoft для PHP для SQL Server
- Отслеживайте производительность с помощью хранилище запросов