PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Spring + Hibernate:Sử dụng bộ nhớ cache của gói truy vấn

Tôi cũng gặp phải vấn đề này. Về cơ bản, nó dẫn đến việc có số lượng giá trị thay đổi trong mệnh đề IN của bạn và Hibernate cố gắng lưu vào bộ nhớ cache các kế hoạch truy vấn đó.

Có hai bài đăng trên blog tuyệt vời về chủ đề này. Đầu tiên:

Sử dụng Hibernate 4.2 và MySQL trong một dự án với truy vấn trong mệnh đề như:select t from Thing t where t.id in (?)

Hibernate lưu trữ các truy vấn HQL được phân tích cú pháp này. Cụ thể là Hibernate SessionFactoryImplQueryPlanCache với queryPlanCacheparameterMetadataCache . Nhưng điều này được chứng minh là một vấn đề khi số lượng tham số cho mệnh đề trong lớn và thay đổi.

Các bộ nhớ đệm này phát triển cho mọi truy vấn riêng biệt. Vì vậy, truy vấn này với 6000parameters không giống với 6001.

Truy vấn trong mệnh đề được mở rộng thành số lượng tham số trong bộ sưu tập. Siêu dữ liệu được bao gồm trong kế hoạch truy vấn cho mỗi tham số trong truy vấn, bao gồm tên được tạo như x10_, x11_, v.v.

Hãy tưởng tượng 4000 biến thể khác nhau về số lượng tham số trong mệnh đề, mỗi biến thể này có trung bình là 4000 tham số. Dữ liệu truy vấn cho mỗi tham số sẽ nhanh chóng được bổ sung vào bộ nhớ, lấp đầy bộ nhớ, vì nó không thể được thu thập rác.

Điều này tiếp tục cho đến khi tất cả các biến thể khác nhau trong số lượng tham số truy vấn được lưu vào bộ nhớ đệm hoặc JVM hết bộ nhớ heap và bắt đầu némjava.lang.OutOfMemoryError:Java heap space.

Tránh các mệnh đề trong là một tùy chọn, cũng như sử dụng kích thước tập hợp cố định cho tham số (hoặc ít nhất là kích thước nhỏ hơn).

Để định cấu hình kích thước tối đa cho bộ nhớ cache của gói truy vấn, hãy xem thuộc tính hibernate.query.plan_cache_max_size , mặc định là 2048 (dễ dàng tạo công cụ cho các truy vấn có nhiều tham số).

Và thứ hai (cũng được tham chiếu từ đầu tiên):

Hibernate nội bộ sử dụng bộ đệm ẩn ánh xạ các câu lệnh HQL (chuỗi) để truy vấn các kế hoạch. Bộ nhớ cache bao gồm một bản đồ giới hạn theo mặc định là 2048 phần tử (có thể định cấu hình). Tất cả các truy vấn HQL đều được tải qua bộ nhớ cache này. Trong trường hợp bỏ lỡ, mục nhập sẽ tự động được thêm vào bộ nhớ cache. Điều này làm cho nó rất dễ bị tấn công - ascenario trong đó chúng tôi liên tục đặt các mục nhập mới vào bộ nhớ cache mà không bao giờ sử dụng lại chúng và do đó ngăn bộ nhớ cache mang lại bất kỳ lợi ích nào (thậm chí nó còn thêm một số chi phí quản lý bộ nhớ cache). Còn tệ hơn nữa, thật khó để phát hiện ra tình huống này một cách tình cờ - bạn phải định cấu hình bộ nhớ cache một cách rõ ràng để nhận thấy rằng bạn gặp sự cố ở đó. Tôi sẽ nói một vài lời về cách có thể làm được điều này.

Vì vậy, việc xóa bộ nhớ cache là kết quả từ các truy vấn mới được tạo ra với tốc độ cao. Điều này có thể do vô số vấn đề gây ra. Hai lỗi phổ biến nhất mà tôi đã thấy là - lỗi trong chế độ ngủ đông khiến các tham số được hiển thị trong câu lệnh JPQL thay vì được truyền asparameters và việc sử dụng mệnh đề "in" -.

Do một số lỗi khó hiểu trong chế độ ngủ đông, có những tình huống khi các thông số không được xử lý chính xác và được kết xuất vào JPQLquery (như ví dụ, hãy xem HHH-6280). Nếu bạn có một truy vấn bị ảnh hưởng bởi những khiếm khuyết như vậy và nó được thực thi với tốc độ cao, nó sẽ phá hủy bộ nhớ cache kế hoạch truy vấn của bạn vì mỗi truy vấn JPQL được tạo là duy nhất (ví dụ:chứa ID của các thực thể của bạn).

Vấn đề thứ hai nằm ở cách chế độ ngủ đông xử lý các truy vấn với mệnh đề "in" (ví dụ:cung cấp cho tôi tất cả các thực thể cá nhân có idfield công ty là một trong 1, 2, 10, 18). Đối với mỗi số tham số riêng biệt trong điều khoản "in", hibernate sẽ tạo ra một truy vấn khác - ví dụ:select x from Person x where x.company.id in (:id0_) cho 1 tham số, select x from Person x where x.company.id in (:id0_, :id1_) cho 2parameters và như vậy. Tất cả các truy vấn này được coi là khác nhau, càng xa càng có liên quan đến bộ nhớ cache của kế hoạch truy vấn, dẫn đến việc xóa bộ nhớ cache một lần nữa. Bạn có thể giải quyết vấn đề này bằng cách viết lớp autility để chỉ tạo ra một số tham số nhất định - ví dụ:1,10, 100, 200, 500, 1000. Ví dụ:nếu bạn chuyển 22 tham số, nó sẽ trả về danh sách 100 phần tử với 22 tham số bao gồm init và 78 tham số còn lại được đặt thành giá trị không thể (ví dụ:-1 cho ID dùng cho khóa ngoại). Tôi đồng ý rằng đây là một vụ hack xấu xí nhưng sẽ hoàn thành công việc. Do đó, bạn sẽ chỉ có tối đa 6 truy vấn đơn vị trong bộ nhớ cache của mình và do đó giảm bớt sự cố.

Vì vậy, làm thế nào để bạn phát hiện ra rằng bạn có vấn đề? Bạn có thể viết một số mã bổ sung và hiển thị các số liệu với số lượng mục nhập trong bộ nhớ cache, ví dụ:qua JMX, điều chỉnh ghi nhật ký và phân tích nhật ký, v.v. Nếu bạn không muốn (hoặc không thể) sửa đổi ứng dụng, bạn có thể chỉ cần dumpthe heap và chạy truy vấn OQL này đối với nó (ví dụ:sử dụng mat):SELECT l.query.toString() FROM INSTANCEOF org.hibernate.engine.query.spi.QueryPlanCache$HQLQueryPlanKey l . It sẽ xuất ra tất cả các truy vấn hiện nằm trong bất kỳ bộ nhớ cache kế hoạch truy vấn nào trên heap của bạn. Sẽ khá dễ dàng để phát hiện xem bạn có bị ảnh hưởng bởi bất kỳ vấn đề nào đã nói ở trên hay không.

Về tác động của hiệu suất, thật khó để nói vì nó phụ thuộc vào quá nhiều yếu tố. Tôi đã thấy một truy vấn rất nhỏ gây ra chi phí 10-20 msof dành cho việc tạo một kế hoạch truy vấn HQL mới. Nói chung, nếu có một bộ nhớ cache ở đâu đó, thì phải có lý do chính đáng cho điều đó - thiếu sót có thể là đắt tiền vì vậy bạn nên cố gắng tránh bỏ lỡ càng nhiều càng tốt. Cuối cùng nhưng không kém phần quan trọng, cơ sở dữ liệu của bạn cũng sẽ phải xử lý số lượng câu lệnh SQL duy nhất - khiến nó phân tích cú pháp chúng và có thể tạo các kế hoạch thực thi khác nhau cho mỗi câu lệnh.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. So sánh các mảng cho bằng nhau, bỏ qua thứ tự của các phần tử

  2. xóa cột không tồn tại

  3. danh sách lược đồ với các kích thước (tương đối và tuyệt đối) trong cơ sở dữ liệu PostgreSQL

  4. Lấy số tuần từ một ngày trong PostgreSQL

  5. Cách cấu hình PostgreSQL để chấp nhận tất cả các kết nối đến