Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini membahas cara menulis kode PHP cepat di SQL Server, Azure SQL Database, Azure SQL Managed Instance, Azure Synapse Analytics, dan database SQL di Microsoft Fabric. Panduan ini berlaku untuk SQLSRV dan PDO_SQLSRV, yang membungkus Driver Microsoft ODBC yang sama untuk SQL Server.
Mulailah dengan perubahan berdampak tertinggi
Jika Anda hanya dapat melakukan tiga perubahan, lakukan perubahan berikut:
- Aktifkan pengumpulan koneksi. Membangun koneksi TLS baru ke SQL Server memakan waktu puluhan hingga ratusan milidetik tergantung pada jalur jaringan dan negosiasi TLS. Menggunakan kembali koneksi dalam pool menghilangkan biaya tersebut untuk setiap permintaan. Lihat Kelola koneksi secara efisien.
- Ambil hanya kolom dan baris yang Anda butuhkan.
SELECT *dan kueri tak terbatas adalah penyebab paling umum dari titik akhir yang lambat. Lihat Query hanya apa yang Anda butuhkan. - Gunakan parameter bernilai tabel untuk penyisipan massal. Untuk ratusan baris atau lebih, parameter bernilai tabel (TVP) biasanya jauh lebih cepat daripada pernyataan baris demi baris
INSERTdan berskala secara linear sesuai dengan jumlah baris. Lihat Masukkan data secara efisien.
Kelola koneksi secara efisien
Pemasangan koneksi adalah operasi paling mahal yang dilakukan pengemudi. Hampir setiap investigasi kinerja PHP berakhir dengan perbaikan manajemen koneksi.
Aktifkan pengumpulan koneksi
Pooling menggunakan kembali koneksi ODBC di seluruh permintaan PHP daripada membongkarnya di akhir permintaan. Objek koneksi akan dibuang saat skrip Anda berakhir, tetapi handle ODBC yang mendasarinya tetap aktif di pool driver manager ODBC dan digunakan kembali oleh permintaan berikutnya yang meminta string koneksi yang sama.
Windows: Connection pooling aktif secara default. Untuk mengonfirmasi, jangan sertakan opsi ConnectionPooling dalam DSN Anda. Untuk menonaktifkan pooling untuk keperluan proses debug, atur ConnectionPooling=0.
Linux dan macOS: Connection pooling bukan opsi DSN di platform ini. Aktifkan di pengelola driver dengan mengatur Pooling=Yes di bagian [ODBC] pada odbcinst.ini, dan setel CPTimeout ke nilai positif di bawah stanza driver. Contohnya:
[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
Temukan jalur perpustakaan sebenarnya dengan odbcinst -q -d -n "ODBC Driver 18 for SQL Server" atau ls /opt/microsoft/msodbcsql18/lib64/. Nama file menyematkan versi driver ODBC yang terpasang dan berubah di setiap rilis.
CPTimeout (dalam detik) menentukan berapa lama koneksi yang tidak aktif tetap berada di pool sebelum ditutup. Atur nilainya cukup tinggi agar sebagian besar permintaan mendapatkan koneksi dari pool, tetapi cukup rendah agar koneksi lama ke server yang mengalami failover dapat dinonaktifkan dalam waktu yang wajar. 60 hingga 300 detik bekerja dengan baik untuk sebagian besar beban kerja web.
Untuk detailnya, lihat Pengumpulan koneksi.
Pahami biaya kueri pertama
Beberapa Set Hasil Aktif (MARS) diaktifkan secara default. Ketika MARS dan connection pooling keduanya aktif, driver akan mereset koneksi yang dikumpulkan pada query pertama , dan reset tersebut mengabaikan timeout query yang telah Anda atur untuk query pertama tersebut. Kueri berikutnya pada koneksi yang sama akan tetap mematuhi batas waktu tunggu sebagaimana mestinya. Jika Anda menetapkan batas waktu kueri pertama yang agresif pada beban kerja dalam pool, perhitungkan perilaku ini, atau nonaktifkan MARS dengan MultipleActiveResultSets=false jika Anda tidak memerlukannya. Lihat catatan tentang MARS dan pengumpulan koneksi di Connection pooling.
Koneksi PDO persisten tidak didukung
PDO_SQLSRV menolak PDO::ATTR_PERSISTENT. Mengaturnya pada lemparan konstruktor:
SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.
Gunakan pooling koneksi ODBC untuk penggunaan ulang lintas permintaan. Ini adalah mekanisme bawaan driver, berfungsi untuk PDO_SQLSRV maupun SQLSRV, dan menonaktifkan koneksi idle pada CPTimeout (yang juga memastikan penyegaran token Microsoft Entra tetap berjalan semestinya).
Gunakan kembali koneksi dalam sebuah permintaan
Bahkan dengan pooling, membuka koneksi PDO atau SQLSRV baru memerlukan perjalanan bolak-balik ODBC untuk mengambil dan memvalidasi handle yang digabungkan. Buka koneksi sekali per permintaan dan teruskan ke setiap fungsi yang membutuhkannya.
Tip
Sebuah kontainer injeksi dependensi atau pengakses lazy sudah cukup. Intinya adalah menghindari new PDO(...) di tengah handler permintaan.
Lakukan kueri hanya untuk yang Anda perlukan
Komunikasi bolak-balik melalui jaringan dan materialisasi kumpulan hasil mendominasi latensi kueri untuk sebagian besar beban kerja PHP. Perbaikannya sama dengan yang berlaku untuk setiap lapisan akses database.
Pilih hanya kolom yang Anda gunakan
SELECT * menarik setiap kolom, termasuk kolom varchar(max) dan varbinary(max) yang ukurannya jauh melebihi data yang benar-benar Anda gunakan. Beri nama kolom:
<?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");
Ambil hanya baris yang Anda butuhkan
Terapkan pemfilteran di SQL Server. Jangan pernah mengambil seluruh tabel ke PHP hanya untuk menyaringnya di dalam loop 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);
Buat paginasi untuk kumpulan hasil yang besar
Untuk tampilan daftar yang menunjukkan beberapa ratus baris dari jutaan, jangan kembalikan semua baris dan biarkan klien mengurutkannya. Gunakan paginasi sisi server dengan 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);
}
Pilih metode pengambilan yang tepat
- Gunakan
fetch(PDO::FETCH_ASSOC)di dalam loop untuk iterasi streaming saat Anda tidak perlu memuat semua baris ke memori sekaligus. - Gunakan
fetchAll(PDO::FETCH_ASSOC)ketika pemanggil benar-benar membutuhkan seluruh set (misalnya, merender respons JSON penuh). - Gunakan
fetchColumn()ketika Anda hanya peduli pada satu skalar (,COUNTSUM, atauMAX). - Gunakan
PDO::FETCH_KEY_PAIRatauPDO::FETCH_UNIQUEuntuk membuat kamus pencarian tanpa perlu melakukan pass kedua.
Mode pengambilan numerik (PDO::FETCH_NUM) sedikit lebih cepat daripada mode pengambilan asosiatif karena melewatkan pembuatan peta nama kolom. Lebih memilih kejelasan; Hanya beralih ketika profiler menandai pengambilan overhead sebagai signifikan.
Sebaiknya gunakan SET NOCOUNT ON dalam prosedur tersimpan dan batch
Setiap statement INSERT, UPDATE, dan DELETE mengembalikan token DONE_IN_PROC dengan jumlah baris yang terdampak, yang biasanya diabaikan oleh PHP. Token ini tidak menambah komunikasi bolak-balik, tetapi masing-masing tetap memakan byte pada transmisi dan sedikit kerja tambahan bagi driver. Dalam prosedur dengan banyak pernyataan atau batch yang menjalankan ratusan pernyataan dalam setiap panggilan, penghematannya akan terakumulasi. Matikan:
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;
Masukkan data secara efisien
Pilih metode sisipan yang tepat berdasarkan berapa banyak baris yang Anda pindahkan. Pilihan yang salah bisa 100 kali lebih lambat.
Kurang dari sekitar 100 baris: pernyataan yang disiapkan dalam loop
Untuk batch kecil, jalankan satu pernyataan yang telah dipersiapkan dalam perulangan:
<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
$stmt->execute([$p["name"], $p["price"]]);
}
Bungkus loop dalam transaksi sehingga semua insert berkomitmen sebagai satu unit dan log tidak perlu di-flush setelah setiap baris:
<?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;
}
Ratusan hingga jutaan baris: parameter bernilai tabel
Parameter bernilai tabel (TVP) mengirim seluruh batch ke SQL Server dalam satu perjalanan bolak-balik dan memungkinkan SQL Server memproses himpunan tersebut sebagai satu pernyataan. Untuk batch yang berisi ratusan baris atau lebih, TVP biasanya jauh lebih cepat daripada loop prepared statement, dan skalanya meningkat secara linear terhadap jumlah baris.
Pertama, buat tipe tabel di server:
CREATE TYPE dbo.ProductTableType AS TABLE (
Name NVARCHAR(100),
Price DECIMAL(10, 2)
);
PDO_SQLSRV meneruskan TVP sebagai array asosiatif yang kuncinya adalah nama tipe dan nilainya adalah sekumpulan baris. Ikatkan dengan 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();
Untuk skema non-default, berikan skema tersebut sebagai elemen berikutnya dari array: ["ProductTableType" => $rows, "Sales"]. Untuk contoh sintaks prosedural dan prosedur tersimpan SQLSRV, lihat Gunakan parameter bernilai tabel.
Jutaan baris: bcp atau BULK INSERT
Untuk operasi massal yang benar-benar (beban gudang data, migrasi awal), gunakan bcp atau BULK INSERT sebagai pengganti PHP. Tulis data Anda ke file berformat delimited atau file berformat asli, lalu jalankan bcp atau BULK INSERT dari tugas terjadwal, langkah ETL, atau skrip administrator.
Caution
Jika Anda memanggil bcp dari PHP melalui shell dengan shell_exec() atau proc_open(), jangan pernah menyisipkan masukan yang tidak tepercaya ke dalam baris perintah. Gunakan escapeshellarg() pada setiap argumen, dan lebih suka menjalankan beban di luar pita daripada dalam jalur permintaan web.
Kurangi perjalanan pulang pergi
Setiap perjalanan bolak-balik jaringan antara PHP dan SQL Server memiliki biaya tetap. Ketika Anda mengirim lima pernyataan sebagai satu batch, Anda membayar biaya itu sekali, bukan lima kali.
Gabungkan pernyataan terkait menjadi satu batch
Untuk pekerjaan terkait yang berjalan bersama, kumpulkan pernyataan tersebut ke dalam satu batch dan konsumsi setiap set hasil:
<?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);
Untuk SQLSRV, gunakan sqlsrv_next_result untuk maju antar set hasil.
Aktifkan Beberapa Set Hasil Aktif saat Anda membutuhkannya
Multiple Active Result Sets (MARS) memungkinkan satu koneksi memiliki beberapa pernyataan aktif. Tanpa MARS, Anda tidak dapat mengirim query baru pada koneksi yang masih memiliki set hasil terbuka. Kedua driver mengaktifkan MARS secara default. Untuk mematikannya, atur MultipleActiveResultSets=false di string koneksi Anda. Lihat Nonaktifkan Beberapa Set Hasil Aktif (MARS).
MARS memang praktis tapi tidak gratis. Setiap set hasil aktif mengonsumsi sumber daya sisi server. Lebih baik mengonsumsi satu set hasil secara penuh sebelum memulai yang baru. Gunakan MARS untuk membuka blokir pola kursor yang benar-benar bersarang.
Optimalkan statement yang disiapkan
Pernyataan yang disiapkan menyelamatkan driver dari penguraian ulang SQL di server, dan memungkinkan Anda mengikat input yang tidak terpercaya sebagai parameter dengan aman.
Pilih persiapan asli
PDO_SQLSRV dapat menyiapkan pernyataan dalam dua mode.
Native menyiapkan pengiriman teks SQL ke server satu kali dan menggunakan kembali pernyataan yang telah diurai untuk setiap eksekusi, dengan hanya mengirimkan nilai parameter pada setiap execute().
Emulated prepare menyimpan teks SQL di klien dan membangun ulang string SQL penuh dengan parameter yang diinterpolasi pada setiap eksekusi.
Atur PDO::ATTR_EMULATE_PREPARES => false agar driver menggunakan persiapan asli. Persiapan native memungkinkan SQL Server menyimpan cache dan menggunakan ulang rencana query, serta menghindari mengurai ulang teks SQL pada setiap eksekusi.
<?php
$conn = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
Gunakan kembali pernyataan yang telah disiapkan
Siapkan sekali, jalankan berkali-kali. Setiap panggilan prepare() memerlukan alokasi handle ODBC dan penguraian di sisi server. Dalam loop yang berjalan intensif, pertahankan objek $stmt tetap aktif dan panggil execute() di dalam loop:
<?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"]]);
}
Hati-hati terhadap TOP (?) dan IN (?, ?, ...)
TOPmembutuhkan tanda kurung di sekitar penanda parameter, SELECT TOP (?) ..., sehingga SQL Server dapat mengurai jumlah baris sebagai parameter.
IN (?, ?, ?, ?) memerlukan jumlah placeholder tetap pada waktu persiapan. Untuk ukuran daftar dinamis IN , bangun string placeholder dari jumlah bilangan bulat yang tervalidasi, atau berikan daftar sebagai parameter bernilai tabel.
Caution
Jangan pernah menginterpolasi input pengguna mentah ke dalam teks SQL (termasuk jumlah placeholder). Lakukan cast pada jumlah dengan (int) sebelum membuat string placeholder, dan selalu teruskan nilai yang sebenarnya melalui execute() sebagai parameter.
Kelola kursor dan memori
Jenis kursor default adalah PDO::CURSOR_FWDONLY, selang pemadam kebakaran yang hanya ke depan. Ia melakukan streaming baris ke PHP satu per satu dan tidak buffer, sehingga hasil yang besar dibatasi oleh memori row-buffer, bukan total jumlah baris. Biasanya itu yang kamu inginkan.
Gunakan kursor yang di-buffer hanya saat Anda perlu bergerak mundur atau menghitung jumlah baris
PDO::SQLSRV_CURSOR_BUFFERED (kursor statis yang dibuffer di sisi klien) mengambil seluruh set hasil ke memori PHP secara langsung. Pendekatan ini memungkinkan Anda memanggil rowCount(), mencari ke belakang, dan menggunakan ulang pernyataan tersebut. Secara bawaan, buffer dibatasi hingga 10.240 KB (10 MB) melalui PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE, dan kueri yang himpunan hasilnya melebihi batas tersebut akan mengembalikan false alih-alih menyebabkan memori PHP meluap. Anda bisa menaikkan batas menuju batas memori PHP, tetapi jika melakukannya, Anda akan menukar false pengembalian dengan kesalahan fatal nyata Allowed memory size exhausted ketika query melebihi batas baru tersebut. Menyetel dengan sengaja. Lihat Jenis kursor (PDO_SQLSRV).
Kursor yang dapat digulir di sisi server (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMIC, PDO::SQLSRV_CURSOR_KEYSET) disimpan dalam buffer di server, bukan di sisi klien, sehingga tidak mengonsumsi memori PHP. Namun, kursor menggunakan sumber daya di sisi server selama masa aktifnya dan lebih lambat per baris dibandingkan dengan forward-only.
Gunakan mode maju saja bawaan untuk pembacaan streaming. Gunakan buffered di sisi klien untuk kumpulan hasil berukuran kecil bila Anda memerlukan rowCount() atau menggulir ke belakang. Hindari kursor scrollable di sisi server kecuali Anda melakukan sesuatu yang spesifik.
<?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();
Untuk rincian lengkap, lihat Tipe kursor (PDO_SQLSRV) dan tipe kursor (SQLSRV).
Alirkan data biner dan karakter berukuran besar
Untuk varibinary(max), varchar(max), nvarchar(max), xml, dan tipe besar lainnya, gunakan aliran PHP daripada mematerialisasi seluruh nilai di memori:
<?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);
Untuk menyisipkan atau memperbarui dengan nilai besar, gunakan SendStreamParamsAtExec=false di SQLSRV untuk mengirim data stream dalam potongan setelah sqlsrv_execute(). Untuk detailnya, lihat Kirim data sebagai aliran.
Mengatur batas waktu yang sesuai
Batas waktu sama pentingnya sebagai pengaturan kinerja maupun pengaturan keandalan. Query yang tergantung lama menahan koneksi pool dan mengurangi permintaan lain.
Batas waktu pernyataan
Tetapkan batas waktu untuk setiap pernyataan agar kueri yang tidak terkendali tidak menahan koneksi dalam pool tanpa batas waktu. Untuk PDO_SQLSRV:
<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();
Untuk SQLSRV, teruskan "QueryTimeout" => 30 dalam array opsi ke sqlsrv_query atau sqlsrv_prepare.
Atur nilai yang sesuai dengan beban kerja Anda. Untuk permintaan web sinkron, biasanya 15 hingga 30 detik. Untuk job batch yang berjalan di latar belakang, beberapa menit mungkin masih wajar. Jangan pernah mengatur timeout ke nol (tanpa batas) dalam permintaan web.
Batas waktu masuk
LoginTimeoutDalam string koneksi, kontrol berapa lama driver menunggu untuk membangun koneksi. Tetapkan nilai eksplisit saat menyambungkan ke Azure SQL Database atau Azure SQL Managed Instance agar cold start dan failover pada grup failover tidak membuat klien menggantung tanpa batas waktu. Nilai dari 30 hingga 90 detik bekerja dengan baik untuk sebagian besar beban kerja cloud. Untuk detail tentang penentuan ukuran LoginTimeout terhadap ConnectRetryCount * ConnectRetryInterval dan mode kegagalan yang dihasilkan, lihat Batas waktu koneksi. Untuk referensi opsi, lihat Opsi Koneksi.
Arahkan beban kerja baca saja ke replika
Untuk kueri baca-saja pada database dalam grup ketersediaan Always On, Azure SQL Managed Instance, atau Azure SQL Database dengan peningkatan skala baca atau geo-replika, tambahkan ApplicationIntent=ReadOnly ke string koneksi Anda:
<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
"Encrypt=true;ApplicationIntent=ReadOnly";
Perutean baca-saja mengirim koneksi ke replika sekunder yang tersinkron, sehingga beban kerja dialihkan dari replika utama. Kombinasikan dengan MultiSubnetFailover=true untuk koneksi paling cepat ke listener grup ketersediaan multi-subnet.
Amati kinerja dari server
Penentuan waktu sisi klien hanya memberi tahu berapa lama waktu kueri berlangsung dari ujung ke ujung. Untuk mengetahui mengapa prosesnya lambat, gunakan diagnostik bawaan SQL Server.
Toko Permintaan (Query Store)
Query Store menangkap rencana eksekusi, statistik runtime, dan statistik tunggu untuk setiap query di database. Fitur ini diaktifkan secara default di Azure SQL Database, Azure SQL Managed Instance, dan database SQL di Fabric. Di SQL Server, aktifkan per database:
ALTER DATABASE <database_name> SET QUERY_STORE = ON;
Kemudian gunakan laporan Query Store dari SQL Server Management Studio untuk menemukan kueri yang paling lambat dan paling sering dieksekusi. Lihat Memantau performa dengan Query Store.
Azure SQL Query Performance Insight
Untuk Azure SQL Database, Query Performance Insight di portal Azure secara otomatis menampilkan kueri yang menggunakan sumber daya terbanyak tanpa konfigurasi apapun. Untuk mengetahui informasi selengkapnya, lihat Wawasan Performa Kueri untuk Azure SQL Database.
SET STATISTICS untuk investigasi satu kali
Untuk satu kueri yang ingin Anda profilkan, jalankan di SQL Server Management Studio dengan statistik diaktifkan:
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- your query here
Bacaan logis tinggi hampir selalu berarti indeks hilang atau tidak dapat digunakan. Waktu CPU yang tinggi dengan pembacaan logis yang rendah biasanya berarti rencana eksekusi yang buruk (parameter sniffing, konversi implisit yang mencegah penggunaan indeks, atau fungsi skalar yang mencegah terjadinya paralelisme).
Acara Diperluas untuk pelacakan tingkat pengemudi
Untuk melihat secara tepat apa yang dikirim driver ke SQL Server (termasuk nilai parameter aktual yang disubstitusikan), tangkap sesi Extended Events menggunakan event rpc_completed dan sql_batch_completed.
Daftar Periksa Kinerja
Gunakan daftar periksa ini sebagai tinjauan pra-deployment untuk aplikasi PHP apa pun yang terhubung ke SQL Server:
| Area | Periksa | Reference |
|---|---|---|
| Connection | Pooling koneksi diaktifkan dan dikonfigurasi untuk platform | Kelola koneksi secara efisien |
| Connection | Aplikasi menggunakan kembali koneksi dalam permintaan dan tidak membuka koneksi per kueri | Gunakan kembali koneksi dalam sebuah permintaan |
| Connection |
LoginTimeoutmencakup cold start dan failover untuk Azure SQL |
Waktu habis login |
| Query | Kueri hanya memilih kolom yang dibutuhkan, tanpa SELECT * |
Pilih hanya kolom yang Anda gunakan |
| Query | Penyaringan terjadi di SQL, bukan di PHP dengan array_filter |
Ambil hanya baris yang Anda butuhkan |
| Query | Set hasil besar dipaginasi dengan OFFSET ... FETCH |
Bagi kumpulan hasil yang besar menjadi beberapa halaman |
| Query | Prosedur tersimpan SET NOCOUNT ON |
Utamakan SET NOCOUNT ON |
| Sisipan | Penyisipan massal menggunakan parameter bernilai tabel, bukan perulangan per baris | Masukkan data secara efisien |
| Statements |
PDO::ATTR_EMULATE_PREPARES disetel menjadi false |
Pilih persiapan asli |
| Statements | Aplikasi menggunakan ulang pernyataan yang telah disiapkan di seluruh eksekusi | Gunakan kembali pernyataan yang telah disiapkan |
| Kursor | Aplikasi menggunakan kursor forward-only bawaan kecuali jika penyanggaan diperlukan | Kelola kursor dan memori |
| Memory | Nilai biner dan karakter besar dialirkan, bukan dimaterialisasi | Alirkan data biner dan karakter berukuran besar |
| Timeouts | Batas waktu pernyataan ditetapkan untuk semua kueri yang ditujukan kepada pengguna | Waktu tunggu pernyataan |
| Pengaturan Rute | Beban kerja baca-saja ditetapkan ApplicationIntent=ReadOnly di tempat replika tersedia |
Arahkan beban kerja baca-saja |
| Observability | Query Store diaktifkan dan ditinjau secara berkala | Query Store |