Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Bu makale, SQL Server, Azure SQL Veritabanı, Azure SQL Yönetilen Örneği, Azure Synapse Analytics ve Microsoft Fabric'te SQL veritabanı ile hızlı PHP kodu nasıl yazılacağını ele alır. Bu yönergeler, temelde aynı Microsoft ODBC Driver for SQL Server'ı kullanan hem SQLSRV hem de PDO_SQLSRV için geçerlidir.
En yüksek etkili değişikliklerle başlayın
Sadece üç değişiklik yapabiliyorsanız, şu değişiklikleri yapın:
- Bağlantı havuzunu etkinleştirin. SQL Server'a yeni bir TLS bağlantısı kurmak, ağ yolu ve TLS müzakere sürecine bağlı olarak onlarca ila yüzlerce milisaniye sürer. Bağlantı havuzundaki bağlantıların yeniden kullanılması, her istek için oluşan bu maliyeti ortadan kaldırır. Bağlantıları verimli yönetin bkz.
- Sadece ihtiyacınız olan sütunları ve satırları getirin.
SELECT *ve sınırsız sorgular, yavaş uç noktaların en yaygın nedenleridir. Sadece ihtiyacınız olanı sorgulayın. - Toplu ekleme işlemleri için tablo değerli parametreleri kullanın. Yüzlerce veya daha fazla satır söz konusu olduğunda, tablo değerli parametreler (TVP'ler) genellikle satır satır
INSERTifadelerinden çok daha hızlıdır ve satır sayısıyla doğrusal olarak ölçeklenir. Verileri verimli şekilde ekle sayfasına bakınız.
Bağlantıları verimli yönetin
Bağlantı kurmak, sürücünün yaptığı en pahalı operasyondur. Neredeyse her PHP performans incelemesi bağlantı yönetimi düzeltmesiyle sonuçlanır.
Bağlantı havuzunu etkinleştirme
Havuzlama, ODBC bağlantılarını her isteğin sonunda kapatmak yerine PHP istekleri arasında yeniden kullanır. Bağlantı nesnesi scriptiniz bittiğinde atılır, ancak altta yatan ODBC handle ODBC sürücü yöneticisinin havuzunda canlı kalır ve aynı bağlantı dizesi'i isteyen sonraki istekte tekrar kullanılır.
Windows: Bağlantı havuzu varsayılan olarak açık. Doğrulamak için, bu ConnectionPooling seçeneği DSN'inizden çıkarın. Hata ayıklama için havuzlamayı devre dışı bırakmak üzere ConnectionPooling=0 olarak ayarlayın.
Linux ve macOS: Bu platformlarda bağlantı havuzu seçeneği DSN seçeneği değildir. Bunu, sürücü yöneticisinde odbcinst.ini içindeki [ODBC] bölümünde Pooling=Yes ayarlayarak etkinleştirin ve sürücünün bloğu altında CPTimeout için pozitif bir değer ayarlayın. Örneğin:
[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
Gerçek kütüphane yolunu veya odbcinst -q -d -n "ODBC Driver 18 for SQL Server"ls /opt/microsoft/msodbcsql18/lib64/ile bulun. Dosya adı, yüklü ODBC sürücüsü sürümünü içerir ve her sürümde değişir.
CPTimeout (saniye cinsinden), boştaki bağlantıların kapatılmadan önce havuzda ne kadar süre kalacağını denetler. Bunu, isteklerin çoğunun havuzdaki bir bağlantıyı bulabileceği kadar yüksek, ancak yük devretmesi yapılmış bir sunucuya olan eski bağlantıların makul bir süre içinde devreden çıkarılacağı kadar düşük olacak şekilde ayarlayın. 60 ila 300 saniye çoğu web iş yükü için iyi çalışıyor.
Detaylar için bkz. Bağlantı havuzlama.
İlk sorgu maliyetini anlayın
Birden çok Etkin Sonuç Kümesi (MARS) varsayılan olarak etkindir. MARS ve bağlantı havuzu aktifse, sürücü ilk sorguda havuzlu bağlantıyı sıfırlar ve bu sıfırlama, o ilk sorgu için ayarladığınız sorgu zaman aşımını görmezden gelir. Aynı bağlantıdaki sonraki sorgularda zaman aşımına normal şekilde saygı gösteriliyor. Havuzlu bir iş yükünde agresif ilk sorgu zaman aşımları ayarlarsanız, bu davranışı göz önünde bulundurun veya gerekmiyorsa MARS'ı MultipleActiveResultSets=false devre dışı bırakın.
Bağlantı havuzlama bölümündeki MARS ve havuzlamaya ilişkin nota bakın.
Kalıcı PDO bağlantıları desteklenmiyor
PDO_SQLSRV, PDO::ATTR_PERSISTENT öğesini geri çeviriyor. Bunu constructor üzerinde ayarlamak şu hatayı fırlatır:
SQLSTATE[IMSSP]: An unsupported attribute was designated on the PDO object.
Çapraz istek yeniden kullanımı için ODBC bağlantı havuzunu kullanın. Bu, sürücünün kendi yerel mekanizmasıdır; hem PDO_SQLSRV hem de SQLSRV için çalışır ve boşta olan bağlantıları CPTimeout devreden çıkarır (bu da Microsoft Entra belirtecinin yenilenmesinin doğru şekilde işlemesini sağlar).
Bağlantıyı bir istek içinde yeniden kullanın
Havuzlama sırasında bile, yeni bir PDO veya SQLSRV bağlantısı açmak, havuzlu bir handle almak ve doğrulamak için ODBC gidiş-dönüş süreci gerektirir. Her istek için bir kez bağlantı açın ve ihtiyacı olan her fonksiyona ilet.
Tip
Bir bağımlılık enjeksiyonu kapsayıcısı veya gecikmeli erişim sağlayıcısı yeterlidir. Amaç, bir istek işleyicisinin ortasında new PDO(...) kullanmaktan kaçınmaktır.
Sadece ihtiyacınız olanı sorgulayın
Ağ gidiş-dönüşleri ve sonuç kümesinin somutlaştırılması, çoğu PHP iş yükünde sorgu gecikmesini belirler. Düzeltmeler, her veritabanı erişim katmanına uygulanan aynı düzeltmelerdir.
Sadece kullandığınız sütunları seçin
SELECT *, varchar(max) ve varbinary(max) sütunları da dahil olmak üzere, gerçekte kullandığınız veriden çok daha büyük olan her sütunu çeker. Sütunlara isimler verin:
<?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");
Sadece ihtiyacın olan sıraları getir
Filtrelemeyi SQL Server'a aktar.
foreach döngüsünde filtrelemek için tablonun tamamını PHP’ye asla çekmeyin.
<?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);
Büyük sonuç kümelerini sayfalandır
Milyonlarca satırdan birkaç yüz satırı gösteren bir liste görünümü için, tüm satırları geri göndermeyin ve müşterinin çözmesine izin verin. Sunucu tarafı sayfalamayı şu OFFSET ... FETCHile kullanın:
<?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);
}
Doğru getirme yöntemini seçin
- Tüm satırları tek seferde bellekte tutmanız gerekmiyorsa, akışlı yineleme için bir döngü içinde
fetch(PDO::FETCH_ASSOC)kullanın. -
fetchAll(PDO::FETCH_ASSOC)Arayan gerçekten tüm seti kullanmak istediğinde (örneğin, tam bir JSON yanıtı oluşturmak) kullanın. - Yalnızca tek bir skalerle (
COUNT,SUMveyaMAX) ilgileniyorsanızfetchColumn()kullanın. -
PDO::FETCH_KEY_PAIRveyaPDO::FETCH_UNIQUEkullanarak ikinci bir geçişe gerek kalmadan arama sözlükleri oluşturabilirsin.
Sayısal getirme modları (PDO::FETCH_NUM) ilişkilendirilebilir getirme modlarından biraz daha hızlıdır çünkü sütun-adı haritasını oluşturmayı atlarlar. Açıklığı tercih edin; yalnızca bir profilleyici getirme ek yükünü önemli olarak işaretlediğinde geçiş yapın.
Saklı yordamlarda ve toplu işlerde SET NOCOUNT ON tercih edin
Her INSERT, UPDATE, ve DELETE ifadesi etkilenen sıra sayısına sahip bir DONE_IN_PROC token döndürür, PHP genellikle bu tokenı atar. Token ek bir gidiş dönüşe neden olmaz, ancak her biri yine de hat üzerinde bayt tüketir ve sürücü tarafında az da olsa ek işlem yükü oluşturur. Her çağrıda yüzlerce ifade çalıştıran çoklu ifade prosedürü veya toplu bir grupta, tasarruflar biriktirilir. Kapatın:
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;
Veriyi verimli şekilde ekle
Kaç sıra hareket ettirdiğinize göre doğru yerleştirme yöntemini seçin. Yanlış seçim 100 kat daha yavaş olabilir.
Yaklaşık 100 satırdan az: bir döngüde hazırlanmış ifade
Küçük gruplar için, bir döngü içinde tek bir hazırlanmış deyimi çalıştırın:
<?php
$stmt = $conn->prepare("INSERT INTO dbo.Products (Name, Price) VALUES (?, ?)");
foreach ($products as $p) {
$stmt->execute([$p["name"], $p["price"]]);
}
Döngüyü bir işlem içine alın; böylece tüm ekleme işlemleri tek bir birim olarak commit edilir ve logun her satırdan sonra diske yazılması gerekmez:
<?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;
}
Yüzlerce hatta milyonlarca satır: tablo değerli parametreler
Tablo değerli parametreler (TVP'ler) tüm grubu tek bir dönüşte SQL Server'a gönderir ve SQL Server'ın kümeyi tek bir ifade olarak işlemesini sağlar. Yüzlerce veya daha fazla satır içeren partiler için, TVP'ler genellikle hazırlanmış bir ifade döngüsünden çok daha hızlıdır ve satır sayısıyla doğrusal ölçeklenir.
İlk olarak, sunucuda bir tablo türü oluşturun:
CREATE TYPE dbo.ProductTableType AS TABLE (
Name NVARCHAR(100),
Price DECIMAL(10, 2)
);
PDO_SQLSRV, TVP’yi anahtarı tür adı, değeri ise satır kümesi olan ilişkisel bir dizi olarak geçirir.
PDO::PARAM_LOB ile bağlayın:
<?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();
Varsayılan olmayan bir şema için, dizinin bir sonraki elemanı olarak şemayı iletin: ["ProductTableType" => $rows, "Sales"]. SQLSRV prosedürel sözdizimi ve saklanan prosedür örnekleri için bkz. Tablo değerli parametreler kullanın.
Milyonlarca satır: bcp veya BULK INSERT
Gerçekten toplu işlemler için (veri deposu yükleri, ilk göçler) bcp veya BULK INSERT PHP yerine kullanın. Verilerinizi ayraçla ayrılmış veya yerel biçimli bir dosyaya yazın, ardından zamanlanmış bir işten, ETL adımından veya yönetici betiğinden bcp ya da BULK INSERT çalıştırın.
Caution
PHP'den shell_exec() veya proc_open() ile bcp'yi çağırıyorsanız, güvenilmeyen girdileri asla komut satırına doğrudan eklemeyin.
escapeshellarg() ifadesini her bağımsız değişkende kullanın ve iş yükünü bir web isteği akışında çalıştırmak yerine bant dışı olarak çalıştırmayı tercih edin.
Gidiş-dönüş sayısını azaltın
PHP ile SQL Server arasındaki her ağ gidiş-dönüş yolculuğunun sabit bir maliyeti vardır. Beş statement’ı tek bir batch olarak gönderdiğinizde, bu maliyeti beş kez ödemek yerine yalnızca bir kez ödersiniz.
İlgili ifadeleri tek bir toplu olarak birleştirin
İlgili ve birlikte çalışan çalışmalar için, ifadeleri tek bir grupta toplayıp tüm sonuç kümesini tüketin:
<?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 için, sonuç kümeleri arasında ilerlemek için kullanılır sqlsrv_next_result .
İhtiyacınız olduğunda birden fazla aktif sonuç setini etkinleştirin
Çoklu Aktif Sonuç Setleri (MARS), tek bir bağlantının birden fazla aktif ifadeye sahip olmasını sağlar. MARS olmadan, hala açık sonuç kümesi olan bir bağlantıda yeni bir sorgu gönderemezsiniz. Her iki sürücü de varsayılan olarak MARS'ı etkinleştiriyor. Kapatmak için bağlantı dizenizde MultipleActiveResultSets=false değerini ayarlayın.
Bkz. Birden Fazla Aktif Sonuç Seti (MARS) Devre Dışı Bırak.
MARS kullanışlıdır ama ücretsiz değildir. Her aktif sonuç kümesi, sunucu tarafı kaynakları tüketir. Bir sonuç setini tam tüketmeyi tercih edin, sonra bir başka bir tane başlatın. Gerçek anlamda iç içe geçmiş imleç kalıplarının önündeki engeli kaldırmak için MARS kullanın.
Tune, hazırladığı açıklamalar
Hazırlanmış ifadeler, sürücünün sunucuda SQL'i yeniden ayrıştırmasını engeller ve güvenilmez girdileri parametre olarak güvenle bağlamanıza olanak tanır.
Yerel hazırlıkları tercih edin
PDO_SQLSRV iki modda ifade hazırlayabilir. Native prepares, SQL metnini sunucuya bir kez gönderir ve her yürütmede yalnızca parametre değerlerini göndererek ayrıştırılmış deyimi yeniden kullanır. Emülle edilen hazırlıklar, SQL metnini istemcide tutar ve her çalıştırmada parametrelerle tam bir SQL dizisi yeniden oluşturur.
PDO::ATTR_EMULATE_PREPARES => false değerini, sürücünün yerel prepare özelliğini kullanacağı şekilde ayarlayın. Yerel prepare ifadeleri, SQL Server’ın sorgu planını önbelleğe alıp yeniden kullanmasını sağlar ve SQL metninin her çalıştırmada yeniden ayrıştırılmasını önler.
<?php
$conn = new PDO($dsn, null, null, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
Hazırlanmış ifadeleri yeniden kullanın
Bir kez hazırlan, birçok kişiyi idam et. Her prepare() çağrı bir ODBC handle tahsisi ve sunucu tarafı ayrıştırma gerektirir. Yoğun kullanılan bir döngüde, $stmt nesnesini etkin tutun ve döngü içinde execute()'i çağırın:
<?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 (?) ve IN (?, ?, ...) için dikkat edin
TOPparametre işaretçisi etrafında parantez gerektirir, SELECT TOP (?) ...böylece SQL Server satır sayısını parametre olarak ayrıştırabilir.
IN (?, ?, ?, ?) hazırlama sırasında sabit bir yer tutucu sayısı gerektirir. Dinamik IN liste boyutları için, ya doğrulanmış bir tam sayı sayından yer tutucu diziyi oluşturun ya da listeyi tablo değerli bir parametre olarak aktarın.
Caution
Ham kullanıcı girdisini SQL metnine (yer tutucu sayısı dahil) asla interpolasyon yapmayın. Yer tutucu dizesini oluşturmadan önce sayıyı (int) ile dönüştürün ve gerçek değerleri her zaman execute() üzerinden parametre olarak geçirin.
İmleci ve belleği yönetin
Varsayılan imleç türü PDO::CURSOR_FWDONLY olup yalnızca ileri yönlü bir firehose imlecidir. Satırları PHP’ye tek tek iletir ve arabelleğe almaz; bu nedenle büyük bir sonuç kümesi, toplam satır sayısı yerine satır arabelleği belleğiyle sınırlıdır. Genellikle istediğin şey bu.
Tamponlu imleçleri sadece geriye doğru hareket etmeniz veya satır saymanız gerektiğinde kullanın
PDO::SQLSRV_CURSOR_BUFFERED (istemci tarafı tamponlu statik imleci) tüm sonuç setini önceden PHP belleğine getirir. Bu yaklaşım, rowCount() çağırmanıza, geriye gitmenize ve ifadeyi yeniden kullanmanıza olanak tanır. Varsayılan olarak arabellek, PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE aracılığıyla 10.240 KB (10 MB) ile sınırlandırılır ve sonuç kümesi bu üst sınırı aşan bir sorgu, PHP belleğinin taşmasına neden olmak yerine false döndürür. Üst sınırı PHP bellek sınırına kadar artırabilirsiniz, ancak bunu yapmak, bir sorgu yeni üst sınırı aştığında false dönüşünü gerçek bir Allowed memory size exhausted ölümcül hatayla değiştirmek anlamına gelir. Bilinçli şekilde ayarlayın.
Bkz. İmleç tipleri (PDO_SQLSRV).
Sunucu tarafında kaydırılabilir imleçler (PDO::SQLSRV_CURSOR_STATIC, PDO::SQLSRV_CURSOR_DYNAMIC, PDO::SQLSRV_CURSOR_KEYSET) istemci yerine sunucuda buffer yapar, böylece PHP bellek tüketmezler. Ancak, imleç ömrü boyunca sunucu tarafı kaynaklarını kullanırlar ve satır başına yalnızca ileri yönlü olana göre daha yavaştırlar.
Akış okuma işlemleri için varsayılan yalnızca ileri seçeneğini kullanın. Küçük sonuç kümeleri için, rowCount() veya geri kaydırma gerektiğinde istemci tarafında arabelleğe alınanı kullanın. Belirli bir şey yapmıyorsanız, sunucu tarafında kaydırılabilir imleçlerden kaçının.
<?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();
Tam bir açıklama için bkz. Cursor types (PDO_SQLSRV) ve Cursor types (SQLSRV).
Büyük ikili ve karakter değerlerini akış olarak iletin
Varbinary(max), varchar(max), nvarchar(max), xml ve diğer büyük türler için, tüm değeri bellekte maddileştirmek yerine PHP akışları kullanın:
<?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);
Büyük değerlerle ekleme veya güncelleme yapmak için, akış verilerini sqlsrv_execute() sonrasında parçalar hâlinde göndermek üzere SQLSRV'de SendStreamParamsAtExec=false kullanın. Detaylar için bkz. Veri akışı olarak gönder.
Uygun zaman aşımlarını ayarlama
Zaman aşımları da güvenilirlik ayarları kadar performans ayarlarıdır. Uzun süren sorgular bağlantı havuzundaki bağlantıları meşgul eder ve diğer isteklerin kaynaklara erişmesini engeller.
Deyim zaman aşımı
Her ifade için bir zaman aşımı süresi belirleyin; böylece kontrolden çıkan bir sorgu bağlantı havuzundaki bir bağlantıyı süresiz olarak elinde tutmaz. PDO_SQLSRV için:
<?php
$stmt = $conn->prepare("SELECT ... FROM dbo.HugeTable ...");
$stmt->setAttribute(PDO::SQLSRV_ATTR_QUERY_TIMEOUT, 30); // seconds
$stmt->execute();
SQLSRV için, seçenekler dizisinde "QueryTimeout" => 30 öğesini sqlsrv_query veya sqlsrv_prepare öğesine geçirin.
İş yükünüze uygun bir değer belirleyin. Senkron web isteği için genellikle 15 ila 30 saniye olur. Arka planda çalışan bir toplu iş için birkaç dakika makul olabilir. Web isteğinde zaman aşımını sıfıra (sınırsız) asla ayarlamayın.
Oturum açma zaman aşımı
LoginTimeoutbağlantı dizesi'te sürücünün bağlantı kurmak için ne kadar beklediğini kontrol eder. Azure SQL Veritabanı veya Azure SQL Yönetilen Örneği’a bağlanırken, soğuk başlatmaların ve yük devretme grubu geçişlerinin istemcinin süresiz olarak askıda kalmasına neden olmasını önlemek için açıkça tanımlanmış bir değer ayarlayın. 30 ila 90 saniye arasındaki değerler çoğu bulut iş yükü için iyi çalışır. Karşılaştırma LoginTimeoutConnectRetryCount * ConnectRetryInterval boyutları ve bunun sonucunda oluşan arıza modları hakkında detaylar için Bağlantı zaman aşımına bakınız. Seçenek referansı için Bağlantı seçenekleri sayfasına bakınız.
Yalnızca okunan iş yüklerini bir replikaya yönlendirme
Always On kullanılabilirlik grubundaki bir veritabanına, Azure SQL Yönetilen Örneği’a veya okuma ölçeğini genişletme ya da coğrafi çoğaltma özelliğine sahip Azure SQL Veritabanı’e yönelik salt okunur sorgular için, bağlantı dizenize ApplicationIntent=ReadOnly ekleyin:
<?php
$dsn = "sqlsrv:Server=<listener>;Database=<database>;" .
"Encrypt=true;ApplicationIntent=ReadOnly";
Yalnızca okunan yönlendirme, bağlantıyı senkronize ikincil bir replikaya gönderir ve birincil sistemden işleri boşaltır. Çok alt ağlı kullanılabilirlik grubu dinleyicilerine en hızlı bağlantı için MultiSubnetFailover=true ile birleştirin.
Sunucudan performansı gözlemleyin
İstemci tarafı zamanlama, sadece bir sorgunun uçtan uca ne kadar sürdüğünü gösterir. Neden yavaş olduğunu öğrenmek için SQL Server'ın yerleşik tanı sistemini kullanın.
Sorgu Mağazası
Query Store, veritabanındaki her sorgu için yürütme planlarını, çalışma zamanı istatistiklerini ve bekleme istatistiklerini yakalar. Azure SQL Veritabanı, Azure SQL Yönetilen Örneği ve Fabric'teki SQL veritabanında varsayılan olarak etkinleştirilmiştir. SQL Server'da, veritabanı başına etkinleştirin:
ALTER DATABASE <database_name> SET QUERY_STORE = ON;
Sonra SQL Server Management Studio'nun Query Store raporlarını kullanarak en yavaş ve en sık yapılan sorgularınızı bulabilirsiniz. Bkz. Performansı Query Store ile İzle.
Azure SQL Sorgu Performansı İçgörüleri
Azure SQL Veritabanı için Azure portalındaki Query Performance Insight, herhangi bir yapılandırma gerektirmeden en çok kaynak tüketen sorguları otomatik olarak gösterir. Daha fazla bilgi için Azure SQL Veritabanı için Sorgu Performansı İçgörüleri’ni inceleyin.
SET STATISTICS Tek seferlik soruşturma için
Profil vermek istediğiniz tek bir sorgu için, istatistikler açık şekilde SQL Server Management Studio'da çalıştırın:
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- your query here
Yüksek mantıklı okumalar neredeyse her zaman eksik veya kullanılamaz bir indeks anlamına gelir. Yüksek CPU süresi ve düşük mantıksal okumalar genellikle kötü bir plan anlamına gelir (parametre koklama, indeks kullanımını engelleyen örtük dönüşüm veya paralelliği engelleyen skaler fonksiyon).
Sürücü düzeyinde takip için Genişletilmiş Etkinlikler
Sürücünün SQL Server'a tam olarak ne gönderdiğini (içine yerleştirdiği gerçek parametre değerleri dahil) görmek için, rpc_completed ve sql_batch_completed olaylarını kullanarak bir Extended Events oturumu yakalayın.
Performans denetim listesi
Bu kontrol listesini, SQL Server'a bağlanan herhangi bir PHP uygulamasının dağıtım öncesi incelemesi olarak kullanın:
| Alan | İşaretli | Reference |
|---|---|---|
| Connection | Platform için bağlantı havuzu etkinleştirilmiş ve yapılandırılmıştır | Bağlantıları verimli yönetin |
| Connection | Uygulama, bir istek içindeki bağlantıları tekrar kullanır ve her sorgu başına bağlantıları açmaz | Bağlantıyı bir istek içinde yeniden kullanın |
| Connection |
LoginTimeout Azure SQL için soğuk başlatma ve yük devretmeyi kapsar |
Giriş süresi |
| Query | Sorgular sadece gerekli sütunları seçer, hayır SELECT * |
Sadece kullandığınız sütunları seçin |
| Query | Filtreleme SQL'de gerçekleşir, PHP'de değil array_filter |
Sadece ihtiyacın olan sıraları getir |
| Query | Büyük sonuç kümeleri şu şekilde sayfalanır OFFSET ... FETCH |
Büyük sonuç kümelerini sayfalayın |
| Query | Saklanan prosedürler kümesi SET NOCOUNT ON |
Tercih et SET NOCOUNT ON |
| Eklemeler | Toplu ekleme işlemleri, satır başına döngüler yerine tablo değerli parametreleri kullanır | Veriyi verimli şekilde ekle |
| Statements |
PDO::ATTR_EMULATE_PREPARES
false olarak ayarlanır |
Yerel hazırlıkları tercih edin |
| Statements | Uygulama, hazırlanmış ifadeleri tüm işlemler arasında yeniden kullanır | Hazırlanmış ifadeleri yeniden kullanın |
| Cursors | Uygulama, buffer gerekmedikçe varsayılan ileri imleci kullanır | İmleci ve belleği yönetin |
| Memory | Büyük ikili ve karakter değerleri akış olarak iletilir, bellekte oluşturulmaz | Büyük ikili ve karakter değerlerini akış olarak iletin |
| Zaman aşımları | Kullanıcı tarafından yönlendirilen tüm sorgularda ifade zamanlaması ayarlanır | Komut zaman aşımı |
| Routing | Salt okunur iş yükleri, bir çoğaltmanın bulunduğu yerde ApplicationIntent=ReadOnly olarak ayarlanır |
Salt okunur iş yüklerini yönlendirin |
| Observability | Query Store etkinleştirilmiş ve düzenli olarak inceleniyor | Query Store |