2014-10-10 18 views
7

Pracuję nad Coldfusion/MS SQL od lat i jest to jeden z najdziwniejszych problemów, jakie widziałem. Sam problem został rozwiązany, ale tak naprawdę nie rozumiem, co się stało; to pytanie jest próbą uzyskania jasnego obrazu prawdopodobnej przyczyny.Powolne działanie zapytania, CF 9 i MSSQL 2008; skorumpowany plan wykonania?

Problem

w stabilnym środowisku produkcyjnym bez powodu, jeden zapytanie rozpoczyna powrót w około 1000-1500 MS (około 10 razy mniejsza niż zwykle). Udało mi się wyizolować go do tego:

<cfquery datasource="#ds#" name="query"> 
    select 1 
    from eLearning.v_courseCompletion cc 
    where 
     cc.memberIncId = <cfqueryparam value="3" cfsqltype="cf_sql_integer"> and 
     cc.courseId = <cfqueryparam value="25" cfsqltype="cf_sql_integer"> and 
     cc.currentCourseCompleted = 1 
</cfquery> 

Co dziwne jest to zachowanie jest poważniejsza, gdy wewnątrz pętli, nawet gdy pojawia się pojedyncza iteracja, jak w poniższym przykładzie:

<cfloop from="1" to="1" index="i"> 
    <cfquery datasource="#ds#" name="query"> 
     select 1 
     from eLearning.v_courseCompletion cc 
     where 
     cc.memberIncId = <cfqueryparam value="3" cfsqltype="cf_sql_integer"> and 
     cc.courseId = <cfqueryparam value="25" cfsqltype="cf_sql_integer"> and 
     cc.currentCourseCompleted = 1 
    </cfquery> 
</cfloop> 

Powinno to być dokładnie takie samo jak powyżej, prawda? Pętla nie powinna wywoływać żadnego efektu, ale zamiast tego test ten działa około 10 razy wolniej, powracając pomiędzy 7 000-16 000 ms. W ten sposób wykryto problem; kwerenda (zakopana w metodzie obiektowej) była wywoływana z treści pętli, jeśli pętla iterowała więcej niż 5 lub 6 razy, po przekroczeniu limitu czasu żądania.

Dla mnie oznaczało to problem po stronie Coldfusion, ale ponowne uruchomienie usługi, a nawet maszyny, nic nie dało.

Tymczasem, gdy zostałem odizolowany, zauważyłem, że wprowadzenie jakiejkolwiek zmiany w samym zapytaniu spowodowało powrót wydajności do oczekiwanego poziomu, około 150-190 ms. Np

  • Zmiana wybrane obszary (tj. select *)
  • Usuwanie alias tabeli (cc)
  • Wymiana albo <cfqueryparam> z wartością inline
  • usuwając wszelkie warunków

Każda z tych zmian "naprawiła" problem, ale przy uruchomieniu pierwotnego zapytania powrócił problem z wydajnością.

Rozwiązanie

W tym momencie domyśliłem plan wykonania kwerendy użytkownika została uszkodzona lub coś zrobił some Googling i pobiegł DBCC FREEPROCCACHE przeciwko serwera DB. Ten naprawił problem natychmiast. Wielki, problem rozwiązany ....

Pytanie

Od tego czasu jednak, zrobiłem trochę więcej badań i konsensusu wydaje się, że plany wykonawcze „nie zostaną uszkodzone”. Istnieje some talk z similar problems występujące w procedurach składowanych i parameter sniffing, ale nie używam tutaj żadnych sp-sów. Wybieramy jednak dość skomplikowany widok (eLearning.v_courseCompletion) z zagnieżdżonymi złączeniami. Czy to jest problem?

W zasadzie, co właściwie się tutaj wydarzyło? Jak mogę to powtórzyć?

.. a czym do diabła jest połączenie z pętlami w CF?!?

Wersje

  • Coldfusion 9.0.2.282541 (64 bit)
  • SQL Server Express 10.50.4297 (64 bit)
  • Oba serwery są Win Server 2008 R2 Datacenter (64 bit)

Odpowiedz

6

Podczas korzystania z cfqueryparam używasz procedury przechowywanej pod maską. Kiedy nie używasz cfqueryparam, twoje zapytanie jest po prostu wysyłane jako zapytanie wsadowe "darmowy tekst". Kiedy używasz cfqueryparam, wysyłasz zapytanie do wykonania przy użyciu sp_executeSQL(), które samo wysyła ciało zapytania jako parametr. Pozwala to na buforowanie planu kwerend. Jeśli zobaczy to samo zapytanie, użyje statystyk zapisanych dla tego konkretnego planu. Oznacza to, że jeśli działał z pewnymi naprawdę dziwacznymi danymi i miał zły pomysł na wykonanie zapytania, następne iteracje użyłyby tego samego planu, który jest "złym planem" dla 99% przypadków użycia tego zapytania, ale może dobry plan dla tej jednej dziwacznej instancji.

Każde zapytanie, które jest wykonywane za pomocą sp_execute SQL, również zwraca uchwyt numeryczny, który sterownik JDBC może wykorzystać do prostego wskazania SQL, który plan może użyć, w zasadzie skrótu. Nazywa się to "zbiorczymi instrukcjami" w ustawieniach DSN w aplikacji CFadmin. Ustawienie tego na 0 lub 1000 nie ma wpływu na fakt, że będziesz korzystał z pamięci podręcznej planów z sp_executeSQL.

http://blogs.msdn.com/b/turgays/archive/2013/09/18/exec-vs-sp-executesql.aspx

StackOverflow miał dobrą demonstrację tego, czy jeden konkretny użytkownik moc by załadować swoją stronę konta z ich miliony odznaki i punkty przed statystyki zapytań zostały zbudowane, że będzie bałagan statystyki dla każdego innego użytkownik, który ma zaledwie kilkaset punktów i garść odznak, co powoduje, że strona jest dla niego wolna.

+0

Ah fantastycznie, to ma wiele sensu. Każdy wgląd w to, dlaczego uruchamianie go z pętli (nawet 1 iteracja) tak mocno wpłynęłoby na wydajność? Dzięki – Molomby

+0

Nie, pętla nie brzmi jak sprawca. Chciałbym obwiniać inne okoliczności, które zapewnia pętla, takie jak zmienna indeksowa lub coś, co sprawia, że ​​pętla wydaje się być winna. Ponadto prawie zawsze można uciec, nie uruchamiając cfquery w pętli. Bardzo rzadko kiedy to ma sens. –

+0

Dzięki za kontynuację, ale zapewniam was, że nie ma nic więcej w pętli, która mogłaby (powinna?) Być przyczyną tego; bloki kodu zamieszczone powyżej są prosto z moich przypadków testowych (plus 'getTickCount()' przed i po). Zmienna indeksu pętli (lub elementu) nie jest w ogóle odwoływana przez zapytanie i nie ma w nim nic więcej. Prawdopodobnie coś idzie w szał podczas kompilacji pętli do Java? Jeśli uda mi się odtworzyć problem, wykopię się nieco głębiej. – Molomby

Powiązane problemy