2012-01-20 21 views
5

uwzględniając dziwny problem z wydajnością przy użyciu hibernacji 3.3.2GA za JPA (i reszta pakietów Hibernate zawartych w JBoss 5)JPA (Hibernate) Język zapytań dla przygotowanym oświadczeniu SLOW

używam Native kwerendy i złożenie SQL w przygotowaną instrukcję.

EntityManager em = getEntityManager(MY_DS); 
final Query query = em.createNativeQuery(fullSql, entity.getClass()); 

SQL ma wiele sprzężeń, ale w rzeczywistości jest bardzo prosty, z jednym parametrem. Podobnych:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ? 

i kwerenda trwa mniej niż sekundę w MSSQL Studio.

Jeśli dodać

query.setParameter(0, "ABC123%"); 

Zapytanie zostanie wstrzymane do 9 sekund

2012-01-20 14:36:21 - TRACE: - AbstractBatcher.getPreparedStatement:(484) | preparing statement 
2012-01-20 14:36:21 - TRACE: - StringType.nullSafeSet:(133) | binding 'ABC123%' to parameter: 1 
2012-01-20 14:36:30 - DEBUG: - AbstractBatcher.logOpenResults:(382) | about to open ResultSet (open ResultSets: 0, globally: 0) 

Jednakże, jeśli po prostu zastąpić "?" z wartością (co nie przygotowane oświadczenie, ale tylko kwerendy SQL prosto.

fullSql = fullSql.replace("?", "'ABC123%'"); 

kwerenda zakończy się w mniej niż sekundę.

bym naprawdę wolą nas przygotowane oświadczenie (the dane wejściowe dla parametrów są pobierane z danych użytkownika), aby zapobiec atakom wtrysku

Śledząc powolny punkt kodu, dotarłem głęboko do pakietu jtds-1.2.2 Linia naruszająca wydaje się być linią SharedSocket 841 "getIn(). readFully (hdrBuf);" Nic tak naprawdę nie jest oczywiste ...

private byte[] readPacket(byte buffer[]) 
     throws IOException { 
    // 
    // Read rest of header 
    try { 
     getIn().readFully(hdrBuf); 
    } catch (EOFException e) { 
     throw new IOException("DB server closed connection."); 
    } 

przybył przez ten stos ...

at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:841) 
    at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:722) 
    at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466) 
    at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103) 
    at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88) 
    at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3928) 
    at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1045) 
    at net.sourceforge.jtds.jdbc.TdsCore.microsoftPrepare(TdsCore.java:1178) 
    at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareSQL(ConnectionJDBC2.java:657) 
    at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:776) 
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208) 
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1808) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:697) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) 
    at org.hibernate.loader.Loader.doList(Loader.java:2228) 
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125) 
    at org.hibernate.loader.Loader.list(Loader.java:2120) 
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:312) 
    at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1722) 
    at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165) 
    at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:175) 
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:67) 
+0

Pozwól mi dodać jtds-1.2.2 do stosu technologii. Debugowałem przez Hibernate w JTDS – javatestcase

+0

wypróbowałem jtds-1.2.4, ale bez radości ... – javatestcase

+0

Przejście na com.microsoft.sqlserver.jdbc.SQLServerDriver faktycznie daje identyczne wyniki ... Muszę rzucić okiem na SQL Server, może coś tam ... – javatestcase

Odpowiedz

8

Zostawię to pytanie i udzielę odpowiedzi tutaj, na wypadek gdyby ktoś miał ten sam problem w przyszłości.

Problem dotyczy sposobu, w jaki sterowniki JTDS wysyłają łańcuchy parametrów do MSSQL. Wygląda na to, że Java spróbuje przesłać domyślnie parametry Unicode, a MSSQL przetłumaczy ją na Ascii. Dlaczego to zajmuje 9 sekund, nie wiem.

Wiele odniesień do tego tam, ale nic, co pomogło mi do tego, że byłem w stanie wyodrębnić, że był problem ze sterownikiem połączenia MSSQL.

Ten link był pomocny:

[http://server.pramati.com/blog/2010/06/02/perfissues-jdbcdrivers-mssqlserver/]

Jest to ciąg przy użyciu Microsoft kierowca.

jdbc:sqlserver://localhost\SQLEXPRESS; 
    DatabaseName=TESTDB; 
    sendStringParametersAsUnicode=false 

Wystarczy dostać sendStringParametersAsUnicode = false przekazywane do konfiguracji URL kierowcy i są dobre.

+8

Zajmuje 9 sekund, ponieważ nie tłumaczy twojego parametru na ascii (ponieważ może to spowodować utratę danych w ten sposób). Zmienia wartości każdej kolumny na unicode przed porównaniem z twoim parametrem. Oznacza to, że nie może w pełni wykorzystać żadnego indeksu na polu "stringId", co prowadzi do znacznie wolniejszej wydajności (wydaje mi się, że miałem podobny problem w przeszłości). – Gareth

+0

BTW, cieszę się, że naprawiłeś swój problem. – Gareth

+0

dzięki. twoje wyjaśnienie naprawdę pomaga zrozumieć problem. – javatestcase

3

Sprawdź zapytanie planuje, że serwer SQL jest produkujących. Przygotowane oświadczenia mogą być szczególnie problematyczne.

Pozwól mi wyjaśnić ...

Jeśli to zrobić:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like 'ABC123%'; 

i masz indeksu na "stringID" Serwer SQL wie, że można go używać.

Jednak jeśli to zrobić:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ?; 

serwer SQL nie wie, to może skorzystać z indeksu, gdy tworzy przygotowaną instrukcję (jak można wypełnić parametr z „% abc123” zamiast " ABC123% "), a tym samym może wybrać zupełnie inny plan zapytania.

+0

Dzięki Gareth. To, co mówisz, jest w 100% prawdziwe. W tym przypadku podstawowym problemem jest połączenie sterownika MSSQL. Myślę, że MSSQL może buforować plany zapytań, a istnieje tylko kilka możliwych iteracji SP, z różnymi pod-zapytaniami dla relacji jeden-do-wielu. Po naprawieniu tego problemu, 100 zapytań powraca w 47 sekund, co stanowi ogromną poprawę w stosunku do 9 sekund! – javatestcase

1

I kolejna odpowiedź dla ludzi, potencjalnie korzystających z Oracle z podobnym problemem Unicode ...

Sprawdź, czy ktoś nie ustawił właściwość oracle.jdbc.defaultNChar = true

To jest czasami zrobić aby rozwiązać problemy z unikodem, ale oznacza to, że wszystkie kolumny są traktowane jako nvarchary. Jeśli masz indeks w kolumnie varchar, nie będzie on używany, ponieważ oracle musi użyć funkcji do konwersji kodowania znaków.

Powiązane problemy