2009-11-24 21 views
9

Tôi đang sử dụng Hibernate EntityManager và đang gặp sự cố chậm chạp trong các truy vấn Hibernate của tôi. Hãy xem mã này:Truy vấn Hibernate chậm lại đáng kể sau khi một thực thể được tải trong phiên

public void testQuerySpeed() { 
    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getResultList(); 
    } 
} 

Điều này chạy trong khoảng 750ms trên máy tính của tôi. Không rực nhanh chóng xem xét nó chỉ là lựa chọn một số nguyên không đổi, nhưng chấp nhận được. Vấn đề của tôi phát sinh thời điểm bất kỳ thực thể được nạp trong phiên EntityManager tôi trước khi tôi khởi động truy vấn của tôi:

public void testQuerySpeed() { 
    CommercialContact contact = em.find(CommercialContact.class, 1890871l); 

    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getSingleResult(); 
    } 
} 

Các em.find() là nhanh, nhưng các truy vấn runtime 1000 tăng hơn mười lần, để khoảng 10 giây. Nếu tôi đặt một số em.clear() sau em.find(), sự cố sẽ biến mất một lần nữa và thời gian chạy sẽ quay lại 750ms.

Tôi đã sử dụng truy vấn gốc tại đây, nhưng sự cố vẫn tồn tại với truy vấn HQL. Dường như TẤT CẢ các truy vấn mất ít nhất 70ms bất cứ khi nào một thực thể nằm trong phiên EntityManager.

Việc giảm hiệu suất này thực sự gây tổn hại cho chúng tôi khi tạo danh sách cần có các truy vấn n + 1.

Tôi đã thử nghiệm phiên bản Hibernate 3.5 beta mới nhất và có cùng vấn đề chính xác. Có ai nhìn thấy vấn đề này, hoặc bất kỳ ý tưởng về cách sửa chữa nó?

Tôi đang sử dụng PostgreSQL 8.3, sử dụng các giao dịch tài nguyên cục bộ (chạy trong Tomcat). Sử dụng hồ bơi kết nối tích hợp, nhưng sử dụng C3P0 không có sự khác biệt.

+0

Bạn có hồ sơ không? Phần lớn thời gian được chi tiêu ở đâu? Bạn có bất kỳ trình lắng nghe/kẻ đánh chặn/phương thức gọi lại nào được xác định không? – ChssPly76

Trả lời

10

Tôi cũng phải khuyên bạn nên sử dụng trình thu thập thông tin JVM để xem thời gian sẽ diễn ra ở đâu. Nó cũng có thể không làm tổn thương để bật khai báo câu lệnh SQL cho phiên Hibernate chỉ để đảm bảo rằng bạn không chạy SQL nhiều hơn bạn nghĩ.

Điều đầu tiên xuất hiện trong tâm trí ở đây là hành vi "xả" của phiên Hibernate. Bạn có thiết lập rõ ràng một chế độ xả cụ thể trên Phiên không? Nếu không, sau đó bạn nhận được "tự động" xả mà sẽ làm một số lượng kiểm tra các đối tượng bạn có trong phiên của bạn để xác định có hay không có những thay đổi trong bộ nhớ mà cần phải được "đỏ" trở lại cơ sở dữ liệu (bên trong của một giao dịch , tất nhiên).

Tôi đoán điều dễ nhất để thử đầu tiên để xem nếu nó có bất kỳ tác dụng là thay đổi mã kiểm tra bạn trình bày ở trên để xác định rằng bạn muốn xả nước để xảy ra cách thủ công khi bạn cam kết giao dịch cơ sở dữ liệu của bạn:

public void testQuerySpeed() { 
    em.setFlushMode(FlushModeType.COMMIT); // assuming you're using JPA annotations 
    CommercialContact contact = em.find(CommercialContact.class, 1890871l); 

    for(int i = 0; i < 1000; i++) { 
     em.createNativeQuery("SELECT 1").getSingleResult(); 
    } 
} 

Một suy nghĩ khác mà tôi cho là sẽ hỏi bạn có thể thực hiện các tác vụ hàng loạt của bạn trong một EntityManager riêng biệt, điều này có thể hoạt động nếu bạn chỉ đang thực hiện các CẬP NHẬT hoặc INSERT.

+1

Thật vậy, chế độ tuôn ra được đặt thành AUTO trong mã của tôi. Tôi đã đặt chế độ tuôn ra thành COMMIT trên mã báo cáo của chúng tôi, chúng tôi không thực hiện bất kỳ việc tạo thực thể nào bằng cách nào đó. Điều này có hiệu quả khắc phục vấn đề hiệu suất. Nó chỉ là lẻ mà kiểm tra cho các đối tượng bẩn mất khoảng 60ms cho chỉ một thực thể. Cảm ơn rất nhiều vì thông tin chi tiết của bạn! –

+0

OMG, cảm ơn rất nhiều, Alexander và BryandD! Cả hai đều đã cứu mạng tôi! :) Tôi đã có cùng một vấn đề chính xác ở đây, với một giao dịch có chứa một vòng lặp với một số tìm kiếm và chèn số lượng lớn/cập nhật bên trong. Sau một tuần thay đổi thuật toán, googling và lược tả, vấn đề hiệu suất của tôi đã được giải quyết một cách đơn giản: em.setFlushMode (FlushModeType.COMMIT); Trước đó, toàn bộ quá trình mất khoảng 28 phút, 95% chi cho getResultList(). Sau đó, mọi thứ diễn ra tốt đẹp chỉ với khoảng 2 phút. Tôi tự hỏi tại sao jpa/hibernate không chọn FlushModeType.COMMIT FlushMode mặc định cho EntityManager ... – hbobenicio

1

Thực thi truy vấn gốc không nói gì về những gì nó sẽ được chạm và do đó Hibernate sẽ phải (để nhất quán) flush() đối với tất cả dữ liệu từ tất cả các bảng mà nó biết (và tìm duy nhất của bạn() có thể đã tìm nạp nhiều hơn chỉ một đối tượng để nó có thể không phải là một hoạt động tầm thường).

Để tối ưu hóa điều này, hãy đảm bảo rằng bạn sử dụng các phương thức SQLQuery.add * để xác định truy vấn thực sự đang thực hiện. Trong trường hợp này query.addSynchronizedQuerySpace ("bogustablename") nên làm các thủ thuật về việc nói với Hibernate rằng truy vấn này chỉ là vô hướng dữ liệu từ không có bảng cụ thể.

3

Tôi đã có cùng một vấn đề (truy vấn bên trong một vòng lặp). Tôi đã tiến hành xác minh hồ sơ bằng JProfiler ...việc thực hiện phương pháp quan tâm của tôi đã dành 572 giây và kiểm tra bẩn hibernate mất 457 giây của thời gian này (khoảng 80%). Thật tuyệt phải không? Tôi phải nói rằng tôi đã có rất nhiều thực thể được quản lý bởi EntityManager. Nếu tôi giới thiệu em.flush()/em.clear() hoặc em.setFlushMode (FlushModeType.COMMIT) trong mã vi phạm, vấn đề hiệu suất sẽ biến mất.

kết quả hồ sơ có sẵn tại http://img1.imagilive.com/0110/hibernate_dirty_checking_bad_perfomances0ce.png

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