SQL Server 2008 datetime dizini performans hatası


11

SQL Server 2008 R2 kullanıyoruz ve birincil kimlik dizinine sahip çok büyük (100M + satır) bir tablo ve datetimekümelenmemiş bir dizine sahip bir sütun var. Biz kullanımına dayanan bazı oldukça sıradışı istemci / sunucu davranışlarla karşılaşır order bymaddede özel olarak bir üzerinde endeksli datetime sütuna .

Aşağıdaki yazıyı okudum: /programming/1716798/sql-server-2008-ordering-by-datetime-is-too-slow ama istemci / sunucu ile olandan daha fazla şey var burada açıklanmaya başlayın.

Aşağıdaki sorguyu çalıştırırsak (bazı içeriği korumak için düzenlenir):

select * 
from [big table] 
where serial_number = [some number] 
order by test_date desc

Sorgu her seferinde zaman aşımına uğruyor. SQL Server Profiler'da yürütülen sorgu sunucuya şöyle görünür:

exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....

Şimdi sorguyu olarak değiştirirseniz şunu söyleyin:

declare @temp int;
select * from [big table] 
where serial_number = [some number] 
order by test_date desc

SQL Server Profiler, çalıştırılan sorguyu sunucuya benzer şekilde gösterir ve anında çalışır:

exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....

Nitekim kullanılmayan bir beyanname yerine boş bir yorum ('-;') bile ekleyebilir ve aynı sonucu alabilirsiniz. Bu yüzden başlangıçta sp ön işlemcisine bu sorunun temel nedeni olarak işaret ediyorduk, ancak bunu yaparsanız:

select * 
from [big table] 
where serial_number = [some number] 
order by Cast(test_date as smalldatetime) desc

Anında da çalışır (başka bir datetimetür olarak yayınlayabilirsiniz), sonucu milisaniye olarak döndürür. Profiler sunucuya isteği şu şekilde gösterir:

exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....

Böylece sp_cursorprepexecprosedürü sorunun tam nedeninden bir şekilde hariç tutar . Buna, sp_cursorprepexec'sipariş etme' kullanılmadığında da çağrılması ve sonuç anında geri dönmesi gerçeğini de ekleyin .

Bu sorun için biraz araştırdık ve başkalarından benzer sorunlar görüyorum, ancak bu seviyeye ulaşan hiçbir şey yok.

Peki diğerleri bu davranışa tanık oldu mu? Davranışı değiştirmek için select deyimi önüne anlamsız SQL koymaktan daha iyi bir çözümü var mı? Veri toplandıktan sonra SQL Server'ın siparişi çağırması gerektiği için, bu sunucuda uzun süre devam eden bir hata gibi görünüyor. Bu davranışın büyük tablolarımızın çoğunda tutarlı olduğunu gördük ve tekrarlanabilir.

Düzenlemeler:

Ben de eklemek koyarak forceseekda sorunu ortadan kaldırır eklemek gerekir .

Arama yardımcı olmak için eklemeniz gerekir, ODBC zaman aşımı hatası atıldı: [Microsoft] [ODBC SQL Server sürücüsü] İşlem iptal edildi

Eklendi 10/12/2012: Hala kök neden avlanıyor, (Microsoft'a vermek için bir örnek oluşturduktan sonra, gönderdikten sonra burada herhangi bir sonucu göndereceğim). ODBC izleme dosyasına (eklenen bir açıklama / beyan deyimi ile) bir çalışma sorgusu ve çalışmayan sorgu arasında kazma edilmiştir. Temel iz farkı aşağıda belirtilmiştir. Tüm SQLBindCol tartışmaları tamamlandıktan sonra SQLExtendedFetch çağrısında oluşur. Çağrı dönüş kodu -1 ile başarısız olur ve daha sonra üst iş parçacığı SQLCancel girer. Bunu hem Yerel İstemci hem de Eski ODBC sürücüleri ile üretebildiğimiz için, hala sunucu tarafında bazı uyumluluk sorununa işaret ediyorum.

(clip)
MSSQLODBCTester 1664-1718   EXIT  SQLBindCol  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10
        UWORD                       16 
        SWORD                        1 <SQL_C_CHAR>
        PTR                0x03259030
        SQLLEN                    51
        SQLLEN *            0x0326B820 (0)

MSSQLODBCTester 1664-1718   ENTER SQLExtendedFetch 
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

MSSQLODBCTester 1664-1fd0   ENTER SQLCancel 
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   EXIT  SQLExtendedFetch  with return code -1 (SQL_ERROR)
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

        DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0) 

MSSQLODBCTester 1664-1fd0   EXIT  SQLCancel  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 0 (SQL_SUCCESS)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C [       5] "S1008"
        SDWORD *            0x08BFFF08 (0)
        WCHAR *             0x08BFF85C [      53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
        SWORD                      511 
        SWORD *             0x08BFFEE6 (53)

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 100 (SQL_NO_DATA_FOUND)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6
(clip)

12/12/2012 Microsoft Connect davası eklendi:

https://connect.microsoft.com/SQLServer/feedback/details/767196/order-by-datetime-in-odbc-fails-for-clean-sql-statements#details

Ayrıca, hem işleyen hem de çalışmayan sorgular için sorgu planları baktım dikkat etmeliyim. Her ikisi de infaz sayısına göre uygun şekilde yeniden kullanılır. Önbelleğe alınmış planları yıkamak ve yeniden çalıştırmak, sorgunun başarısını değiştirmez.


Denerseniz ne olur select id, test_date from [big table] where serial_number = ..... order by test_date- sadece SELECT *performansınız üzerinde olumsuz bir etkisi olup olmadığını merak ediyorum . Kümelenmemiş bir dizininiz test_dateve bir kümelenmiş dizininiz varsa id(bu adın ne olduğunu varsayarak), bu sorgu bu kümelenmemiş dizin tarafından kapsanmalı ve bu nedenle oldukça hızlı bir şekilde dönmelidir
marc_s

Üzgünüm, iyi bir nokta. Ben seçili sütun alanı ('*', vb kaldırma) ağır bir şekilde çeşitli kombinasyonlar ile değiştirmeye çalıştık dahil olmalıdır. Yukarıda açıklanan davranış bu değişikliklerle devam etti.
DBtheDBA

Hesaplarımı şimdi bu siteye bağladım. Bir moderatör yayını bu siteye taşımak istiyorsa, her iki şekilde de iyiyim. Geliştiricilerimden biri, buraya gönderdikten sonra bu siteyi bana işaret etti.
DBtheDBA

Burada hangi istemci yığını kullanılıyor? İzleme metninin tamamı olmadan, sorun gibi görünüyor. Orijinal aramayı içine kaydırmayı deneyin sp_executesqlve ne olduğunu görün.
Jon Seigel

1
Yavaş uygulama planı neye benziyor? Parametre koklama?
Martin Smith

Yanıtlar:


6

Gizem yok , temelde rastgele iyi (er) veya (gerçekten) kötü bir plan elde edersiniz çünkü endeksin kullanacağı net bir seçim yoktur. ORDER BY deyimi için zorlarken ve bu nedenle sıralamayı önlerken, datetime sütunundaki kümelenmemiş dizin bu sorgu için çok kötü bir seçimdir. Bu sorgu için çok daha iyi bir dizin ne olurdu bir üzerinde (serial_number, test_date). Daha da iyisi, bu kümelenmiş bir dizin anahtarı için çok iyi bir aday olacaktır .

Genel kural olarak, zaman dizisi zaman sütunu ile kümelenmelidir, çünkü taleplerin ezici çoğunluğu belirli zaman aralıklarıyla ilgilenmektedir. Veriler ayrıca, seri_numarada olduğu gibi, düşük seçiciliğe sahip bir sütuna bölümlenmişse, bu sütun kümelenmiş anahtar tanımında en soldaki sütun olarak eklenmelidir.


Burada biraz kafam karıştı. Plan neden the ordermaddeye dayanıyor ? Plan where, yalnızca sıralar getirildikten sonra gerçekleşmesi gerektiği için kendisini koşullarla sınırlamamalı mı? Sunucu neden sonuç kümesinin tamamını ayarlamadan önce kayıtları sıralamaya çalışır?
DBtheDBA

5
Bu ayrıca, sorgunun başına bir yorum eklemenin neden çalışma süresini etkilediğini açıklamaz.
cfradenburg

Ayrıca, tablolarımız test_date değil, neredeyse her zaman seri numarasıyla sorgulanır. Her ikisinde de kümelenmemiş dizinlerimiz ve yalnızca tablodaki kimlik sütununda kümelenmiş dizinlerimiz var. Bu işlevsel bir veri deposudur ve diğer sütunlara kümelenmiş dizinler eklemek yalnızca sayfa bölünmelerini ve daha düşük performans sağlar.
DBtheDBA

1
@DBtheDBA: Eğer bir 'hata' için talepte bulunmak istiyorsanız, uygun bir araştırma ve açıklama yapmanız gerekir. Kesin masanızdaki ve ihraç istatistikler şema takip Server 2008, SQL Server 2005 ve SQL bir istatistik sadece veri tabanı oluşturmak için gerekli veritabanı meta verilerinin bir komut dosyası nasıl oluşturulur özellikle tüm önemli, Senaryo İstatistikleri : Senaryo İstatistik ve histogramlar . Sorunu yeniden oluşturma adımlarıyla birlikte bunları yazı bilgilerine ekleyin.
Remus Rusanu

1
Bunu daha önce aramalarımız sırasında okuduk ve ne dediğini anlıyorum, ancak sunucunun burada yaptığı bir şeyde temel bir kusur var. Tabloyu ve dizinleri yeniden oluşturduk ve yeni bir tabloda yeniden oluşturduk. Yeniden derleme seçeneği sorunu çözmez, bu da bir şeyin yanlış olduğuna dair büyük bir ipucu. Kümelenmiş dizinleri her şeye koymanın bu sorunu çözebileceğinden şüphe etmiyorum, ancak bunun temel nedeni için bir çözüm değil, bir geçici çözüm ve büyük bir tabloda pahalı bir çözüm.
DBtheDBA

0

Hatanın nasıl yeniden oluşturulacağıyla ilgili ayrıntıları belgeleyin ve connect.microsoft.com adresine gönderin. Kontrol ettim ve zaten bununla ilgili bir şey göremedim.


DBA'mın, çoğaltılacak bir ortam oluşturmak için yarın bir komut dosyası yazmasını sağlayacağım. Bunun zor olduğunu düşünmüyorum. Birisi bunu denemekle ilgilenirse, ben de buraya göndereceğim.
DBtheDBA

Bağlantı öğesini açıldığında da gönderin. Bu şekilde bir başkası bu sorunu yaşarsa, ona doğru yönelir. Ve bu soruyu izleyen herkes öğeyi oylamak isteyebilir, böylece Microsoft'un buna daha fazla dikkat etmesi daha olasıdır.
cfradenburg

0

Benim hipotezim, sorgu planı önbelleği üzerinde çalıştığınızdır. (Remus benimle aynı şeyi söylüyor olabilir, ama farklı bir şekilde.)

SQL'in önbelleğe almayı nasıl planladığına dair bir ton ayrıntı .

Detaylara göz atma: Birisi bu sorguyu daha önce belirli bir [sayı] için çalıştırdı. SQL, sağlanan değere, ilgili tablo / sütunlara ilişkin dizinlere ve istatistiklere vb. Baktı ve bu belirli [bazı sayı] için iyi çalışan bir plan oluşturdu. Daha sonra planı önbelleğe aldı, çalıştırdı ve sonuçları arayana geri verdi.

Daha sonra, bir başkası aynı sorguyu farklı bir [bazı sayı] değeri için çalıştırıyor. Bu belirli değer, çok farklı sayıda sonuç satırı ile sonuçlanır ve motor, sorgunun bu örneği için farklı bir plan oluşturmalıdır. Ancak bu şekilde çalışmaz. Bunun yerine, SQL sorguyu alır ve (daha fazla veya daha az) sorgunun önceden var olan bir sürümünü arayarak sorgu önbelleğinde büyük / küçük harfe duyarlı bir arama yapar. Öncekinden birini bulduğunda, sadece bu planı kullanır.

Fikir, plana karar vermek ve inşa etmek için gereken zamandan tasarruf etmesidir. Fikirdeki delik, aynı sorgu çılgınca farklı sonuçlar üreten değerlerle çalıştırıldığında ortaya çıkar. Farklı planları olmalı, ama yok. Sorguyu ilk kim çalıştırırsa, sorguyu daha sonra çalıştıran herkes için davranışı ayarlamanıza yardımcı olur.

Kısa bir örnek: soyad = 'SMITH' olan [insanlar] 'dan * seçin.

BONAPARTE sorgusu çalıştırıldığında, SMITH için oluşturulan plan yeniden kullanılır. SMITH bir tablo taramasına neden olduysa (tablodaki satırlar% 99 SMITH ise iyi olabilir ), BONAPARTE bir tablo taraması da alır. BONAPARTE SMITH'ten önce çalıştırıldıysa, bir indeks kullanan bir plan oluşturulabilir ve kullanılabilir ve daha sonra tekrar SMITH için kullanılabilir (tablo taramasıyla daha iyi olabilir). İnsanlar SMITH performansının kötü olduğunu fark etmeyebilir, çünkü tüm tablo okunmalı ve dizini okumalı ve tabloya atlamalı doğrudan fark edilmemelidir.

Herhangi bir değişiklik-bu-değiştirmek-bir şey ile ilgili olarak, SQL sadece tamamen farklı bir sorgu olarak görüyor ve [bazı sayı] değerine özgü yeni bir plan inşa şüpheli.

Bunu test etmek için, FOR ile tablo adı arasına boşluk eklemek gibi sorguda anlamsız bir değişiklik yapın veya sonuna bir yorum yazın. Hızlı mı? Öyleyse, bu sorgu önbellekdekinden biraz farklı olduğu için SQL, "yeni" sorgular için yaptığı şeyi yaptı.

Bir çözüm için üç şeye bakardım. İlk olarak, istatistiklerinizin güncel olduğundan emin olun. Bu, bir sorgu garip veya rasgele gibi göründüğünde yaptığınız ilk şey olmalıdır. DBA'nız bunu yapıyor olmalı, ancak bir şeyler oluyor. Güncel istatistikleri sağlamanın genel yolu, tablolarınızı yeniden dizine eklemektir; bu, yapılması gereken hafif bir şey değildir, ancak istatistikleri güncellemek için seçenekler de vardır.

Düşünülmesi gereken ikinci şey Remus'un önerileri doğrultusunda dizinler eklemektir. Daha iyi / farklı bir indeksle, bir değer diğerine karşı daha kararlı olabilir ve çok çılgınca değişmeyebilir.

Bu işe yaramazsa, denemek için üçüncü şey, ifadeyi her çalıştırdığınızda RECOMPILE anahtar sözcüğünü kullanarak yeni bir planı zorlamaktır:

test_date desc tarafından seri_number = [bazı sayı] sıralaması * arasından [büyük tablo] seçin * SEÇENEK (RECOMPILE)

Burada benzer bir durumu anlatan bir makale var . Açıkçası, daha önce sadece saklı yordamlar uygulanan RECOMPILE görmüştü, ama "düzenli" SELECT ifadeleri ile çalışıyor gibi görünüyor. Kimberly Tripp beni hiç yanlış yönlendirmedi.

" Plan kılavuzları " adlı özelliğe de bakabilirsiniz , ancak daha karmaşıktır ve aşırı olabilir.


Bu endişelerin bir kısmını karşılamak için: 1. İstatistikler güncellendi, güncelleniyor. 2. Dizine eklemeyi çeşitli yollarla denedik (dizinleri vb. Kapsayan), ancak sorun order byözellikle bir datetime dizinine yönelik kullanıma daha fazla bağlı görünüyor . 3. Sadece fikrinizi TAVSİYE seçeneği ile denedim, yine de başarısız oldu, bu beni biraz şaşırttı, üretim için bir çözüm olup olmadığını bilmememe rağmen işe yarayacağını umuyordum.
DBtheDBA
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.