Saklı yordamlar ve satır içi SQL


27

Saklı yordamların yürütme yolunda daha etkili olduğunu biliyorum (uygulamalardaki satır içi sql'den). Ancak, basıldığında neden hakkında çok fazla bilgiliyim.

Bunun için teknik sebepleri bilmek istiyorum (daha sonra birisine açıklayacağım şekilde).

Biri iyi bir cevap formüle etmeme yardımcı olabilir mi?


1
Bir uygun şekilde parametrize sorgu bakış performans açısından, sadece bir saklı bir prosedür olarak iyi gibidir. Her ikisi de ilk kullanımdan önce derlenir, her ikisi de sonraki uygulamalarda önbelleğe alınmış yürütme planını yeniden kullanır, her iki plan da aynı plan önbelleğinde saklanır ve her ikisi de aynı adı alır. Artık SQL Server'da saklı bir prosedür için performans avantajı yoktur.
marc_s

@marc_s, eğer sorgular aynı ise doğru. Ancak cevabımda belirttiğim gibi, aynı görünen sorgular için bile performans sorunu olabilecek geçici sorguların bazı özellikleri var .
Aaron Bertrand

Yanıtlar:


42

Bu duyarlılığın bir noktada doğru olduğuna inanıyorum, ancak SQL Server'ın şu anki sürümlerinde değil. Bütün sorun, eski günlerde özel SQL ifadelerinin düzgün bir şekilde optimize edilememesiydi çünkü SQL Server yalnızca toplu iş düzeyinde optimize edebildi / derleyebildi. Artık ifade düzeyinde optimizasyona sahibiz, bu nedenle bir uygulamadan gelen doğru parametreleştirilmiş bir sorgu, saklı bir prosedüre gömülü olan sorgu ile aynı yürütme planından faydalanabilir.

Hala aşağıdaki nedenlerle DBA tarafındaki saklı prosedürleri tercih ediyorum (ve bunların birçoğunun performans üzerinde büyük etkisi olabilir):

  • Aynı sorguları yeniden kullanan birden fazla uygulamam varsa, saklı yordam, aynı geçici sorguyu farklı kod tabanlarında birden çok kez toplamak yerine, bu mantığı kapsar. Aynı sorguları yeniden kullanan uygulamalar aynı zamanda kopyalanmadıkça plan önbellek boşluğuna da tabi olabilir. Durum ve beyaz alandaki farklılıklar bile aynı planın birden fazla versiyonunun depolanmasına (israf) yol açabilir.
  • Uygulamanın tam olarak ne yaptığını görmek için uygulama kaynak koduna erişmeden veya pahalı izler çalıştırmadan bir sorgunun ne yaptığını inceleyebilir ve sorun giderebilirim.
  • Ayrıca, uygulamanın hangi sorguları çalıştırabileceğini, hangi tablolara erişebileceğini ve hangi bağlamda vb. Olduğunu kontrol edebilir (ve önceden bilirim). Geliştiriciler, uygulamalarında geçici sorgular yazıyorsa, ya da yapmak zorunda kalacaklardır. bilmediğim veya tahmin edemediğim bir masaya her ihtiyaç duyduklarında ya da daha az sorumlu / coşkulu ve / veya güvenlik bilincindeysem gömlek gömleğimin kolunu çekin Dbo kullanıcı bu yüzden beni rahatsız etmeyi bıraktılar. Bu genellikle geliştiriciler DBA'ları veya DBA'ları inatçı saydıklarında yapılır. Bu son nokta bizim kötümüz ve ihtiyaç duyduğunuz soruları sağlama konusunda daha iyi olmamız gerekiyor.
  • İlgili bir notta, bir dizi saklı yordam, sistemimde hangi sorguların çalıştığını envanterlemenin çok kolay bir yoludur. Bir başvurunun prosedürleri atlamasına ve kendi geçici sorgusunu göndermesine izin verilir vermez, onları bulmak için, tüm bir iş döngüsünü kapsayan bir izlemeyi çalıştırmak zorundayım veya tüm başvuru kodunu incelemek zorundayım (yine, Bir sorgu gibi görünen bir şey bulmak için)))). Saklı yordamların listesini görebilme (ve sys.sql_modulesbelirli nesnelere referanslar için tek bir kaynağı tanıma ) herkesin hayatını çok kolaylaştırır.
  • SQL enjeksiyonunu önlemek için daha büyük uzunluklara gidebilirim; Girdi alıp dinamik SQL ile çalıştırsam bile, olmasına izin verilenleri kontrol edebiliyorum. Satır içi SQL ifadeleri oluştururken bir geliştiricinin ne yaptığı üzerinde hiçbir kontrolüm yok.
  • Sorgu (veya sorguları) uygulama kaynak koduna erişmeden, değişiklik yapma yeteneği, uygulama dilinin bu kadar etkili bir şekilde yapması için bilgi birikimi, yeniden derleme ve yeniden dağıtma yetkisi (hiçbir zaman sakıncası olmadan) optimize edebilirim Uygulamanın, vb Uygulama dağıtılırsa, özellikle sorunludur.
  • Tek tek sorguların uygulamadaki Yavaşlığın bir kısmına maruz kalmamasını , SSMS'de hızlı olmasını önlemek için saklı yordamdaki belirli ayar seçeneklerini zorlayabilirim ? sorunları. Bir ad hoc sorgusu çağıran iki farklı uygulama için birinin birinin SET ANSI_WARNINGS ON, diğerinin de olabilir SET ANSI_WARNINGS OFFve her birinin planın kendi kopyasına sahip olacağı anlamına gelir. Elde ettikleri plan, kullanılan parametrelere, yerinde istatistiklere vb. Bağlı olarak her sorguda ilk defa çağrıldığında farklı planlara ve dolayısıyla çok farklı performanslara yol açabilir.
  • Bazı ORM'lerin aksine veri tipleri ve parametrelerin nasıl kullanılacağını kontrol edebilirim - EF gibi daha önceki sürümlerin bazıları, bir parametrenin uzunluğuna dayanarak bir sorguyu parametreleştirir, eğer bir N'mith 've başka bir N' parametresi olsaydı Johnson 'Planın iki farklı versiyonunu alırdım. Bunu düzelttiler. Bunu düzelttiler ama başka ne hala kırıldı?
  • ORM'lerin ve diğer "faydalı" çerçevelerin ve kütüphanelerin henüz desteklemediği şeyleri yapabilirim.

Bütün bunların söylediği gibi, bu sorunun teknik tartışmadan ziyade dini argümanları uyandıracağı muhtemel. Bunu olduğunu görürsek muhtemelen kapatırız.


2
Saklı yordamlar için başka bir neden? Uzun, karmaşık sorgular için, sorguyu sunucuya her seferinde zorlamanız gerekir, eğer bir sproc değilse, o zaman sadece "exec sprocname" ve birkaç parametreyi bastırıyorsunuzdur. Bu, yavaş (veya meşgul) bir ağda bir fark yaratabilir.
David Crowell

0

Başvurana saygı duyurken, "dini nedenlerle" değil, verilen cevaba alçakgönüllülükle katılmıyorum. Başka bir deyişle, Microsoft'un sağladığı ve rehberlik için saklı yordamları kullanma ihtiyacını azaltan bir olanak bulunmadığını düşünüyorum.

Ham metin SQL sorgularının kullanılmasını destekleyen bir geliştiriciye sağlanan herhangi bir rehberlik, en tedbirli tavsiyenin Saklı Prosedürlerin kullanımını büyük ölçüde teşvik etmek olduğunu ve geliştirici ekiplerin pratiğe girmesini engellemek olduğunu düşündüğü şekilde birçok uyarı ile doldurulmalıdır. SQL deyimlerini koda gömme veya ham, düz eski metin tabanlı SQL isteklerini SQL SPROC'lerin dışında (saklı yordamlar) gönderme.

Bir SPROC kullanımının neden sunulduğu gibi olduğu sorusuna verilen basit cevabı düşünüyorum: SPROC'lar ayrıştırıldı, optimize edildi ve derlendi. Bu nedenle, sorgu / yürütme planları önbelleğe alınır, çünkü bir sorgunun statik bir gösterimini kaydettiniz ve normalde, yalnızca kopyalanan / yapıştırılan SQL ifadeleri durumunda doğru olmayan parametreler tarafından değişiklik göstereceksiniz. sayfadan sayfaya ve bileşen / katmandan sık sık farklı tabloların, hatta veritabanı adlarının bile, çağrı yapmak için belirtilebileceği ölçüde değişkenlik gösterir. Bu tür dinamik özel amaçlı izin vermeSQL gönderimi, DB Engine'in geçici ifadeleriniz için sorgu planını yeniden kullanma olasılığını, bazı çok katı kurallara göre büyük ölçüde azaltır. Burada, etkili Sistem SPROC sp_executesql kullanımı ile dinamik geçici sorgular (sorulanın ruhuna göre) arasında ayrım yapıyorum.

Daha spesifik olarak, aşağıdaki bileşenler vardır:

  • Kullanıcı bağlamını taşımayan ve DB altyapısı tarafından yeniden kullanımına izin veren seri ve paralel sorgu planları.
  • Bir sorgu planının farklı veri parametreleriyle yeni bir kullanıcı tarafından tekrar kullanılmasına izin veren yürütme bağlamı.
  • DB motorunun aradığımız etkinlikleri yaratmak için sorguladığı işlem prosedürü.

Bir web sayfasından bir SQL ifadesi verildiğinde, "ad hoc deyimi" olarak adlandırılan motor, isteği işlemek için mevcut bir yürütme planını arar. Bu bir kullanıcıdan gönderilen metin olduğundan, geçerli olması halinde yutulur, ayrıştırılır, derlenir ve yürütülür. Şu anda sıfır bir sorgu maliyeti alacaksınız. Sorgu maliyeti, DB altyapısı hangi yürütmenin önbellekten çıkarılacağını belirlemek için algoritmasını kullandığında kullanılır.

Özel sorgular varsayılan olarak sıfır bir orijinal sorgu maliyet değeri alır. Aynı aynı geçici sorgu metninin ardından başka bir kullanıcı işlemi (veya aynı) tarafından yürütülmesinin ardından, geçerli sorgu maliyeti orijinal derleme maliyetine sıfırlanır. Geçici sorgu derleme maliyetimiz sıfır olduğundan, bu yeniden kullanım olasılığı için iyi bir işaret değildir. Açıkçası, sıfır en az değer verilen tam sayıdır, fakat neden tahliye edildi?

Bellek basınçları ortaya çıktığında ve sık kullanılan bir siteniz varsa, DB altyapısı Prosedür önbelleğinin kullandığı belleği nasıl geri alabileceğini belirlemek için bir temizleme algoritması kullanır. Hangi sorguların tahliye edileceğine karar vermek için mevcut sorgu maliyetini kullanır. Tahmin edebileceğiniz gibi, sıfır maliyete sahip olan planlar önbellekten ilk çıkarılandır, çünkü sıfır temel olarak "bu planın mevcut kullanıcıları veya referansları yoktur" anlamına gelir.

  • Not: Özel uygulama planları - Geçerli maliyet, her kullanıcı işleminde, planın orijinal derleme maliyetinde artırılır. Bununla birlikte, hiçbir planın maksimum maliyeti, geçici sorgular durumunda ... sıfır olan orijinal derleme maliyetinden daha fazla olamaz. Bu nedenle, bu değerle "artırılacak" ... sıfır - bu aslında en düşük maliyet planı olarak kalacağı anlamına gelir.

Bu nedenle, böyle bir planın ilk önce bellek baskıları ortaya çıktığında çıkarılması muhtemeldir.

Bu nedenle, "gereksinimlerinizin ötesinde" çok fazla bellek içeren bir sunucunuz varsa, bu sorunu iş yükünü işlemek için yalnızca "yeterli" belleğe sahip yoğun bir sunucu kadar sık ​​karşılaşmayabilirsiniz. (Üzgünüm, sunucu belleği kapasitesi ve kullanımı algoritma olmasa da, biraz öznel / akrabadır.)

Şimdi, bir ya da daha fazla nokta hakkında aslında yanılmıyorsam, kesinlikle düzeltilmeye açığım.

Son olarak, yazar şunu yazdı:

"Şimdi ifade düzeyinde optimizasyona sahibiz, bu nedenle bir uygulamadan gelen doğru parametreleştirilmiş bir sorgu, saklı bir prosedüre gömülü olan sorgu ile aynı yürütme planından faydalanabilir."

Yazarın "geçici iş yükleri için optimize et" seçeneğine değindiğine inanıyorum.

Öyleyse, bu seçenek tam sorgu planını Prosedür önbelleğine göndermekten hemen kaçınan iki aşamalı bir işleme izin verir. Sadece orada daha küçük bir sorgu saplaması gönderir. Sorgu saplaması hala Prosedür önbelleğindeyken tam bir sorgu çağrısı sunucuya geri gönderilirse, tam sorgu yürütme planı o zaman Prosedür önbelleğine kaydedilir. Bu bellek basınç olayları sırasında, bellek, tasarruf edebilir tahliye algoritması önbelleğe edildi daha büyük bir sorgu planı daha az sık saplama tahliye için izin verir. Yine, bu sunucu hafızasına ve kullanımına bağlıdır.

Ancak, varsayılan olarak kapalı olduğundan bu seçeneği açmanız gerekir.

Son olarak, çoğu zaman, geliştiricilerin SQL'i sayfalara, bileşenlere ve diğer yerlere yerleştirmesinin nedeni, esnek olmaları ve veritabanı motoruna dinamik SQL sorgusu göndermek istedikleridir. Bu nedenle, gerçek dünyadaki bir Kullanım Durumunda, aynı metni göndermek için, çağrı yönlendirme çağrısı, SQL Server'a geçici sorgular gönderirken aradığımız önbellekleme / verimlilik gibi gerçekleşmesi pek mümkün değildir.

Ek bilgi için lütfen bakınız:

https://technet.microsoft.com/en-us/library/ms181055(v=sql.105).aspx
http://sqlmag.com/database-performance-tuning/don-t-f--ynamic-sql

En iyisi
Henry


4
Yazınızın birkaç paragrafını dikkatlice okudum, iki ya da üç kez okudum ve hala hangi düşünceleri aktarmaya çalıştığınız hakkında hiçbir fikrim yok. Bazı durumlarda cümlenin sonunda, cümlenin söylemeye çalıştığı şeyin tam tersi olduğunu söyleyerek çıkıyorsunuz. Bu gönderiyi dikkatlice okumanız ve düzenlemeniz gerekiyor.
Pieter Geerkens

Pieter, geri bildiriminiz için teşekkür ederiz. Bu durumda, noktayı daha net hale getirmek için cümlelerimi kısaltmam gerekebilir. Lütfen orijinal düşüncenin tersini belirttiğim yerin bir örneğini verebilir misiniz? Çok takdir.
Henry,

Hayır, Ad Hoc İş Yükleri İçin Optimize Etmeyi kastetmedim, ifade düzeyinde optimizasyon demek istedim. Örneğin, SQL Server 2000'de, saklı bir prosedür bir bütün olarak derlenecekti, bu nedenle uygulamanın prosedürde bir şeyle eşleşen kendi geçici sorgusu için bir planı tekrar kullanmasının bir yolu yoktu. Pieter ile hemfikir olduğumu söyleyeceğim - söylediğiniz şeylerin çoğu takip etmek zor. "Microsoft'un sağladığı, rehberlik için saklı yordamları kullanma ihtiyacını azaltan bir olanak bulunmadığını düşünüyorum" gibi şeyler. gereksiz yere karmaşıktır ve anlamak için çok fazla ayrıştırma gerektirir. BENİM NACİZANE FİKRİME GÖRE.
Aaron Bertrand

1
"ad hoc" sql'ye olan isteksizliğin, sql'nin bir şekilde yürütmeler arasında değiştiği fikrine dayanıyor gibi görünüyor ... parametre belirleme söz konusu olduğunda bu tamamen yanlış.
b_levitt

0

TLDR: Satır içi sql'inizin parametreleştirildiği sürece ikisi arasında kayda değer bir performans farkı yoktur.

Saklı yordamları yavaşça kapatmamın nedeni bu:

  • Üretim veritabanını paylaşan üretime paralel bir ortam olan 'beta' uygulama ortamını çalıştırıyoruz. Db kodu uygulama düzeyinde olduğundan ve db yapısı değişikliklerinin nadir olduğu için, insanların QA dışındaki yeni işlevleri onaylamalarına ve üretim dağıtım penceresinin dışındaki dağıtımları yapmalarına izin verebilir, ancak yine de üretim işlevselliği ve kritik olmayan düzeltmeler sunabiliriz. Uygulama kodunun yarısı DB’de olsaydı bu mümkün olmazdı.

  • Veri tabanı seviyesinde saptamalar uyguluyoruz (ahtapot + dacpaclar). Bununla birlikte, iş katmanı ve üstü temelde temizlenir ve değiştirilir ve geri kazanılırken, veritabanlarına gitmesi gereken artan ve potansiyel olarak yıkıcı değişiklikler için bu doğru değildir. Sonuç olarak, DB dağıtımlarımızı daha hafif ve daha sık tutmayı tercih ediyoruz.

  • İsteğe bağlı parametreler için aynı kodun tam kopyalarının yakınından kaçınmak için, sık sık bir 'where @ var ya da @ var = table.field' kalıbı kullanırız. Depolanan bir işlemle, farklı amaçlara rağmen aynı uygulama planını elde etmeniz ve dolayısıyla performans problemleri yaşamanız veya 'yeniden derleme' ipuçlarıyla önbelleğe alınmış planları kaldırmanız olasıdır. Bununla birlikte, sql'nin sonuna "imza" yorumu ekleyen basit bir kod biti ile, hangi değişkenlerin null olduğuna bağlı olarak farklı planları zorlayabiliriz (tüm değişken kombinasyonları için farklı bir plan olarak yorumlanmayacak - sadece null geçersiz değil).

  • Sql anında sadece ufak değişikliklerle sonuçlarda çarpıcı değişiklikler yapabilirim. Örneğin, iki "CTE", "Raw" ve "ReportReady" ile kapatılan bir bildiri alabilirim. Her iki CTE'nin de kullanılması gerektiğini söyleyen hiçbir şey yok. My sql deyimi şöyle olabilir:

    ...

    {(biçim)} "arasından * seçin

Bu, hem düzenli bir api çağrısı hem de karmaşık mantığı çoğaltmamamı sağlamak için daha ayrıntılı olması gereken bir rapor için aynı iş mantığı yöntemini kullanmamı sağlıyor.

  • "sadece procs" kuralına sahipseniz, sql'nizin büyük çoğunluğunda CRUD olmakla sonuçlanan bir ton fazlalık elde edersiniz - tüm parametreleri bağlarsınız, tüm bu parametreleri proc imzasında listelersiniz (ve şimdi farklı bir projedeki farklı bir dosyadasınız), bu basit parametreleri sütunlarıyla eşleştirin. Bu oldukça ayrık bir gelişme deneyimi yaratıyor.

Procs'u kullanmak için geçerli nedenler var:

  • Güvenlik - Burada uygulamanın kullanması gereken başka bir katman daha var. Uygulama hizmeti hesabının tablolara dokunmasına izin verilmezse, ancak procs üzerinde yalnızca 'yürütme' iznine sahipseniz, bazı ekstra korumalara sahip olursunuz. Bu bir bedeli olduğu için verilen bir şey yapmaz, ancak bir olasılıktır.

  • Yeniden kullanma - db'ye bağlı olmayan iş kurallarını atlamaktan kaçındığınızdan emin olmak için yeniden iş katmanında gerçekleşmesi gerektiğini söylesem de, hâlâ okullar için kullanılan düşük seviyeli "her yerde kullanılan" yardımcı program işlemlerine ve işlevlerine sahibiz.

Gerçekten procs desteklemeyen veya IMO'yu kolayca hafifleten bazı argümanlar var:

  • Yeniden kullanma - Yukarıda "artı" olarak bahsettim, ancak burada yeniden kullanımın büyük ölçüde iş katmanında gerçekleşmesi gerektiğini de belirtmek istedim. İş katmanı diğer db dışındaki hizmetleri de kontrol ediyor olabilirken, kayıt ekleme işlemi "yeniden kullanılabilir" olarak değerlendirilmemelidir.

  • Önbellek planı şişmesi - bunun bir sorun haline gelmesinin tek yolu, parametreleştirmek yerine değerleri birleştiriyor olmanızdır. Proc başına nadiren birden fazla plan elde etmeniz, genellikle bir sorguda 'veya' varken sizi incitir.

  • İfade boyutu - proc adının üzerine fazladan bir kb sql ifadesi geri gelen verilere göre genellikle ihmal edilebilir hale gelir. Varlıklar için uygunsa, benim için de uygun.

  • Tam soruyu görmek - Sorguları kodda bulmayı kolaylaştırmak, arayan konumu koda yorum olarak eklemek kadar kolaydır. Kodun c # kodundan ssms'ye kopyalanmasını sağlamak, bazı yaratıcı enterpolasyon ve yorum kullanımları kadar kolaydır:

        //Usage /*{SSMSOnly_}*/Pure Sql To run in SSMS/*{_SSMSOnly}*/
        const string SSMSOnly_ = "*//*<SSMSOnly>/*";
        const string _SSMSOnly = "*/</SSMSOnly>";
        //Usage /*{NetOnly_}{InterpolationVariable}{_NetOnly}*/
        const string NetOnly_ = "*/";
        const string _NetOnly = "/*";
  • SQL Enjeksiyon - Sorgularınızı parametreleştirin. Bitti. Proc, bunun yerine dinamik sql kullanıyorsa, bu gerçekten geri alınabilir.

  • Konuşlandırmayı atlatma - Veri tabanı düzeyinde saptamalar yapıyoruz, bu bizim için bir seçenek değil.

  • "Uygulamada yavaş, SSMS'de hızlı" - Bu, her iki tarafı da etkileyen plan önbelleğe alma sorunudur. Ayar seçenekleri yalnızca THE SET SET değişkenleri için problemi çözen yeni bir planın oluşturulmasına neden olur. Bu sadece neden farklı sonuçlar gördüğünüzü cevaplar - set seçeneklerinin kendileri koklama parametrelerinin problemini DEĞİLDİR.

  • Satır içi sql yürütme planları önbelleğe alınmaz - Sadece yanlış. Proc adı gibi hızlı bir şekilde özetlenir ve sonra bu karma tarafından bir plan aranır. % 100 aynı.

  • Açıkçası, bir ORM'den kod oluşturulmamış ham satır içi sql'den bahsediyorum - biz sadece en iyi ihtimalle bir mikro ORM olan Dapper kullanıyoruz.

https://weblogs.asp.net/fbouma/38178

https://stackoverflow.com/a/15277/852208

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.