2012-01-04 22 views
5

Tôi cố gắng giải quyết vấn đề hiệu suất của ứng dụng của mình. Truy vấn Hibernate tạo ra, có dạng:Truy vấn từ ứng dụng Hibernate không sử dụng chỉ mục DB

select * 
from ( 
    select this.a, this.b, this.state, this.id 
    from view_user this 
    where this.state=:1 order by this.a asc, this.b 
) 
where rownum <= :2 

nơi

  • id là khóa chính
  • có một kết hợp, chỉ số duy nhất trên (a, b, id).
  • view_user có ~ 2 triệu mục
  • view_user thực hiện một số tiếp tục tham gia vào các bảng khác

Issue

Các truy vấn trên thực - nhanh từ SQLDeveloper - nhanh chóng từ một ứng dụng Java nhỏ với hibernate - cực kỳ chậm (> 100x chậm hơn) từ ứng dụng với hibernate - giá trị cho các biến liên kết là 2 tương ứng 30 (nguồn gốc rownum từ phân trang) - truy vấn ngủ đông là "của biểu mẫu" ở trên. Thực tế có khoảng 20 cột trong chế độ xem.

trạng thái hiện tại của phân tích

  • kế hoạch truy vấn cho thấy chỉ số được sử dụng khi truy vấn đến từ SQlDeveloper hoặc "ứng dụng java nhỏ".
  • kế hoạch truy vấn cho thấy quét toàn bộ bảng được thực hiện nếu truy vấn đến từ ứng dụng ngủ đông
  • Theo dõi DB chỉ hiển thị hai khác biệt: Cài đặt NLS (từ SQLDeveloper) và định dạng hơi khác (khoảng trắng). Mọi thứ khác có vẻ là như nhau ...

phiên bản

  • ngủ đông: 2.1.8
  • lái xe jdbc: sử dụng ojdbc14, 5 và 6. Làm cho có sự khác biệt
  • Oracle : 10.2 và 11. Không tạo sự khác biệt

=> Tôi rất vui vì mọi gợi ý có thể liên quan đến vấn đề này. Điều rắc rối của tôi là việc truy tìm DB không cho thấy bất kỳ sự khác biệt nào ... Vâng, có vẻ như đó là điều gì đó về ngủ đông. Nhưng cái gì? Làm thế nào để phát hiện?


Vì lợi ích của sự hoàn chỉnh, đây truy vấn ngủ đông (từ nhật ký):

Select * from ( 
    select this.USER_ID as USER_ID0_, this.CLIENT_ID as CLIENT_ID0_, 
    this.USER_NAME as USER_NAME0_, this.USER_FIRST_NAME as USER_FIR5_0_, this.USER_REMARKS as 
    USER_REM6_0_, this.USER_LOGIN_ID as USER_LOG7_0_, this.USER_TITLE as USER_TITLE0_, 
    this.user_language_code as user_lan9_0_, this.USER_SEX as USER_SEX0_, 
    this.USER_BIRTH_DATE as USER_BI11_0_, this.USER_TELEPHONE as USER_TE12_0_, 
    this.USER_TELEFAX as USER_TE13_0_, this.USER_MOBILE as USER_MO14_0_, 
    this.USER_EMAIL as USER_EMAIL0_, this.USER_ADDRESSLINE1 as USER_AD16_0_, 
    this.USER_ADDRESSLINE2 as USER_AD17_0_, this.USER_POSTALCODE as USER_PO18_0_, 
    this.USER_CITY as USER_CITY0_, this.USER_COUNTRY_CD as USER_CO20_0_, 
    this.USER_COUNTRY_NAME as USER_CO21_0_, this.USER_STATE_ID as USER_ST24_0_, 
    this.USER_STATE as USER_STATE0_, this.USER_TEMP_COLL_ID as USER_TE26_0_, 
    this.USER_TEMP_COLL_NAME as USER_TE27_0_, this.UNIT_ID as UNIT_ID0_, 
    this.CLIENT_NAME as CLIENT_38_0_, this.PROFILE_EXTID as PROFILE39_0_ 
    from VIEW_USER this 
    where this.USER_STATE_ID=:1 order by this.USER_NAME asc, this.USER_FIRST_NAME asc 
) 
where rownum <= :2 

chỉ số duy nhất là qua user_name, user_first_name, user_id.

+0

Tôi nghi ngờ bạn có lỗi đánh máy trong truy vấn bạn đã nhập ở đây, vì lựa chọn bên trong có hai 'vị trí' trong đó. Có phải mệnh đề 'where rownum' thực sự ở bên ngoài được chọn không? – rejj

+1

Bạn nói rằng "view_user có ~ 2 mục nhập mio". Vui lòng xác định "mio". Ngoài ra, các giá trị có thể có của view_user.state là gì và có bao nhiêu hàng cho mỗi giá trị có thể? Cuối cùng, những giá trị nào đang được cung cấp cho: 1 và: 2? Cảm ơn. –

+0

sry, mio ​​là triệu. Bộ hoàn trả của lựa chọn bên trong có thể là khoảng 80% của chế độ xem hoàn chỉnh (view_user) vì "this.state =: 1" với giá trị 2 cho: 1. –

Trả lời

1

SQL bạn cung cấp không giống như truy vấn được tạo Hibernate. Bạn có chắc đó không phải là một truy vấn viết tay?

Nếu bạn muốn sử dụng Hibernate, bạn có thể sử dụng setMaxResults() để giới hạn số hàng được trả về.

Nếu bạn muốn sử dụng một truy vấn viết tay Tôi nghĩ rằng bạn muốn một cái gì đó như thế này:

select * 

từ ( chọn this.a, this.b, this.state, this.id từ view_user này nơi this.state =: 1 trật tự bởi this.a asc, this.b ), nơi rownum < =: 2

+0

Sry cho lỗi đánh máy. Truy vấn đã được đơn giản hóa, nhưng vẫn giống nhau về mặt logic. Các hibernate trông phức tạp hơn một chút vì danh sách cột trong lựa chọn. –

+0

np. Có chỉ mục trên VIEW_USER.USER_STATE_ID không? –

+0

có, có. state_id là một khoá ngoại cho một bảng trong đó state_id là khóa chính. –

0

Sau rất nhiều cố gắng gỡ lỗi và xung quanh, tôi có một câu trả lời làm thế nào để khắc phục vấn đề. Tuy nhiên, tôi không thể giải thích được vấn đề chính xác là gì.

Sự cố có vẻ là ở cấp kết nối kết nối JDBC. cấu hình của tôi là:

  • initialSize = 0
  • minIdle = 10

Sử dụng cấu hình trên, chúng tôi đã có các triệu chứng như mô tả trong descritpion vấn đề. Các giải pháp cho đến nay có vẻ là để thiết lập minIdle bằng initialSize. Nếu không, kết nối dường như được khởi tạo bằng cách nào đó khác và chúng tôi thấy hiệu suất giảm. Vì vậy, chúng tôi đặt cả hai thành '10'.

Tôi hiện đang phân tích nhật ký. Trên máy chủ DB, các tệp theo dõi không hiển thị bất kỳ sự khác biệt nào. Truy tìm JDBC không hiển thị bất kỳ thông tin thú vị nào cho đến nay.

Googling, tôi thấy rằng nhiều cấu hình mẫu được đặt minIdle = initialSize. Hơn nữa, tôi tìm thấy hai trích dẫn (spring docu, vmware docu) liên quan đến các cài đặt JDBC:

minIdle: Số lượng tối thiểu các kết nối được thiết lập phải được giữ trong hồ bơi mọi lúc. Hồ bơi kết nối có thể thu nhỏ bên dưới số này nếu truy vấn xác thực không thành công. Giá trị mặc định có nguồn gốc từ initialSize.

Lưu ý rằng câu cuối cùng "bắt nguồn từ initialSize" không được tìm thấy trong tất cả tài liệu cho minIdle. Hầu hết các tài liệu đề cập đến '0' là mặc định.

+0

ok, công việc xung quanh là nok. Tất cả các kết nối được khởi tạo ngay từ đầu (do initialSize) là ok và sẽ dẫn đến các truy vấn nhanh, tức là, đúng kế hoạch truy vấn được thực hiện. Nhưng ngay sau khi một kết nối mới được tạo ra bởi vì không có đủ tạo lúc khởi động, những kết nối mới được tạo ra sẽ có vấn đề hiệu suất. –

1

Giải pháp cuối cùng của vấn đề là c3p0. Chúng tôi không thể theo dõi lý do tại sao các kế hoạch truy vấn sai được chọn. Giả thiết cuối cùng của chúng ta là nó có liên quan đến việc khởi tạo kết nối của dbcp. Nhưng vì hồ thời gian, chúng tôi đã thử c3p0. Những kinh nghiệm đầu tiên:

  • vấn đề không xuất hiện
  • nhanh hơn bởi vì nhiều tích cực hơn trong các điều khoản của việc sử dụng MaxConnections cấu hình cho các hồ bơi
  • và vì điểm trên, ứng dụng hoàn chỉnh chúng tôi bây giờ là nhanh hơn. Điều này dựa trên ấn tượng chung khi sử dụng ứng dụng cũng như các thử nghiệm tải khác nhau của chúng tôi

Vì vậy, hiện tại chúng tôi đang háo hức thử nghiệm các kịch bản khác nhau với c3p0 để sẵn sàng cho các hệ thống sản xuất.

Cảm ơn tất cả các yếu tố đầu vào!

4

Tôi đã gặp một tình huống tương tự để điều này có thể liên quan đến bạn.

Trình điều khiển JDBC đang thay đổi các tham số của bạn thành mã unicode để varchar trở thành một nvarchar khi nó truy cập cơ sở dữ liệu. Nếu bạn may mắn, SQL 2008 SP1 sẽ bắt được và chuyển đổi nó trở lại. Nhưng SQL 2000 và SQL 2005 sẽ không và trình tối ưu hóa truy vấn sẽ thực hiện quét toàn bộ bảng để bỏ qua các chỉ mục của bạn.

Bạn có thể sửa lỗi này ở lớp JDBC bằng cách thêm tham số kết nối sendStringParametersAsUnicode=FALSE vào chuỗi kết nối của bạn.

+0

Cảm ơn điều này đã làm việc một điều trị. Tôi đang sử dụng hibernate 4.2.16 và SQL Server 2008 SP4 và sqlJdbc4 trình điều khiển Microsoft. Đã có cùng một vấn đề mà tôi đã làm chính xác cùng một truy vấn trên một khách hàng sql và nó sẽ trở lại ngay lập tức và thông qua java + hibernate nó sẽ mất 4-5 giây. Đã thêm ở trên và BAM :) – kohlerfc

0

Có một câu trả lời ở trên/dưới đây về nvarchars với các thiết lập cho SQL Server - đây là một thiết lập tương tự cho Oracle ...

Kiểm tra để chắc chắn rằng ai đó đã không thiết lập thuộc tính oracle.jdbc.defaultNChar = true

Điều này đôi khi được thực hiện để giải quyết vấn đề unicode nhưng nó có nghĩa là tất cả các cột được coi là nvarchars. Nếu bạn có một chỉ mục trên một cột varchar, nó sẽ không được sử dụng bởi vì oracle phải sử dụng một hàm để chuyển đổi mã hóa ký tự.

-1

Chúng tôi có cùng sự cố: Khi chạy truy vấn từ Nhà phát triển SQL, kế hoạch giải thích cho thấy chỉ mục đã được sử dụng, nhưng khi chạy truy vấn trong ứng dụng thông qua Hibernate, chỉ mục bị bỏ qua.

Chúng tôi đã giải quyết nó bằng cách thêm gợi ý trong truy vấn vào trình tối ưu hóa của Oracle để sử dụng chỉ mục được đề cập.

Các vấn đề liên quan