Tôi muốn chèn một số lượng lớn các thực thể vào bảng chéo. Để làm điều đó, tôi muốn kích hoạt tùy chọn chèn hàng loạt của hibernate, để mỗi chèn sẽ thêm không 1, nhưng 20 hoặc 50 hàng tại một thời điểm.Làm thế nào để cho phép chèn hàng loạt với Hibernate và Spring Boot
Tôi đang cố gắng tạo lại kết quả từ hibernate's tutorial. Tôi đã tạo một chức năng thử nghiệm sẽ cố gắng chèn 30 khách hàng, giống như trong ví dụ:
//Batch inserting test entities
//This code is inside transaction already, so I'm not creating a new one
entityManager.flush();
for (int i = 0; i < 30; ++i) {
Customer customer = new Customer("Customer" + i, (i + 5) * 1000, "Position " + i);
entityManager.persist(customer);
if (i % 20 == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
Lớp khách hàng không có id được tạo.
@Entity
public class Customer {
@Id
private String id;
@Column
private String name;
@Column
private long salary;
@Column
private String position;
public Customer() {
}
public Customer(String name, long salary, String position) {
this.id = UUID.randomUUID().toString();
this.name = name;
this.salary = salary;
this.position = position;
}
}
Điều tôi mong đợi là 2 câu lệnh chèn, một cho 20 bản ghi và một cho 10 bản ghi. Tuy nhiên, khi tôi mở nhật ký postgres, tôi thấy 30 câu lệnh chèn, mỗi câu lệnh chỉ chèn 1 hàng. Tôi đã kiểm tra lại rằng có thể chèn nhiều hàng bằng cách sử dụng postgres.
Tôi tin rằng sự cố là do tham số hibernate.jdbc.batch_size 20
, mà tôi phải chuyển sang chế độ ngủ đông. Tuy nhiên, vì tôi đang sử dụng khởi động mùa xuân, tệp cấu hình duy nhất tôi có là application.properties. Vì vậy, tôi đã cố gắng chèn vào đó:
hibernate.jdbc.batch_size=20
hibernate.order_inserts=true
hibernate.order_updates=true
hibernate.jdbc.batch_versioned_data=true
spring.jpa.hibernate.jdbc.batch_size=20
spring.jpa.hibernate.order_inserts=true
spring.jpa.hibernate.order_updates=true
spring.jpa.hibernate.jdbc.batch_versioned_data=true
spring.jpa.properties.hibernate.jdbc.batch_size=20
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
Theo answer này, điều này sẽ là đủ, nhưng Tôi cũng đã cố gắng sao chép các thông số cho hibernate.properties
tập tin bên trong jar tôi và cung cấp các tham số thông qua dòng lệnh : -Dhibernate.jdbc.batch_size=20
như đã nêu trong documentation này.
Không điều nào trong số này hữu ích.
Tôi cũng không thể tìm cách đọc thuộc tính kích thước lô trong mã của mình. Dường như hầu hết các đối tượng được đề cập trong tài liệu hibernate không tồn tại trong ứng dụng khởi động mùa xuân.
Làm cách nào để kích hoạt chèn hàng loạt của hibernate trong ứng dụng khởi động mùa xuân?
tôi đã tạo ra một ví dụ làm việc tối thiểu mà tái tạo vấn đề: https://github.com/Alexey-/spring-boot-batch
Sau khi nhận được câu trả lời từ Vlad Mihalcea tôi nhận ra rằng có, trên thực tế báo cáo hàng loạt đang làm việc, ít nhất là trên mức hibernate và jdbc.
Tuy nhiên, nhật ký postgres tự thể hiện hành vi rất thú vị: chúng hầu như giống hệt nhau đối với trường hợp chèn hàng loạt và chèn thông thường.
gì ban đầu tôi đã mong đợi để xem là Hibernate sẽ sử dụng báo cáo như thế này:
test=# INSERT INTO customer (name, position, salary, id) values ('CustomerX', 'PositionX', '1000', 'idX'), ('CUSTOMERY', 'POSITIONY', '1000', 'idY');
nào sẽ tạo ra log hồ sơ tương tự như sau:
2015-12-15 11:43:33.238 MSK LOG: statement: INSERT INTO customer (name, position, salary, id) values ('CustomerX', 'PositionX', '1000', 'idX'), ('CUSTOMERY', 'POSITIONY', '1000', 'idY');
Tuy nhiên, đó không phải là trường hợp .
Khi chèn hàng loạt được kích hoạt (p6spy cho thấy báo cáo được, trên thực tế, batched), postgres sẽ sản xuất bản ghi tương tự như sau:
2015-12-15 12:07:00.638 MSK LOG: execute <unnamed>: BEGIN
2015-12-15 12:07:00.638 MSK LOG: duration: 0.000 ms
2015-12-15 12:07:00.638 MSK LOG: duration: 0.000 ms parse <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.638 MSK LOG: duration: 0.000 ms bind <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.638 MSK DETAIL: parameters: $1 = 'Customer0', $2 = 'Position 0', $3 = '0', $4 = '9c6a86fb-c991-4e98-aa65-fa736ef67dd7'
2015-12-15 12:07:00.638 MSK LOG: execute <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.638 MSK DETAIL: parameters: $1 = 'Customer0', $2 = 'Position 0', $3 = '0', $4 = '9c6a86fb-c991-4e98-aa65-fa736ef67dd7'
2015-12-15 12:07:00.639 MSK LOG: duration: 1.000 ms
2015-12-15 12:07:00.648 MSK LOG: duration: 0.000 ms parse S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.648 MSK LOG: duration: 0.000 ms bind S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.648 MSK DETAIL: parameters: $1 = 'Customer1', $2 = 'Position 1', $3 = '10', $4 = 'c8b2669c-044a-4a4d-acbd-31c3bcd9a783'
2015-12-15 12:07:00.648 MSK LOG: execute S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.648 MSK DETAIL: parameters: $1 = 'Customer1', $2 = 'Position 1', $3 = '10', $4 = 'c8b2669c-044a-4a4d-acbd-31c3bcd9a783'
2015-12-15 12:07:00.648 MSK LOG: duration: 0.000 ms
2015-12-15 12:07:00.648 MSK LOG: duration: 0.000 ms bind S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.648 MSK DETAIL: parameters: $1 = 'Customer2', $2 = 'Position 2', $3 = '20', $4 = '1c694f41-2ce7-4ee2-a0c0-f359690506f0'
2015-12-15 12:07:00.649 MSK LOG: execute S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.649 MSK DETAIL: parameters: $1 = 'Customer2', $2 = 'Position 2', $3 = '20', $4 = '1c694f41-2ce7-4ee2-a0c0-f359690506f0'
2015-12-15 12:07:00.649 MSK LOG: duration: 0.000 ms
2015-12-15 12:07:00.649 MSK LOG: duration: 0.000 ms bind S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.649 MSK DETAIL: parameters: $1 = 'Customer3', $2 = 'Position 3', $3 = '30', $4 = '1815947d-2604-48d4-a6be-43f6905130cf'
2015-12-15 12:07:00.649 MSK LOG: execute S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.649 MSK DETAIL: parameters: $1 = 'Customer3', $2 = 'Position 3', $3 = '30', $4 = '1815947d-2604-48d4-a6be-43f6905130cf'
2015-12-15 12:07:00.649 MSK LOG: duration: 0.000 ms
2015-12-15 12:07:00.649 MSK LOG: duration: 0.000 ms bind S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.649 MSK DETAIL: parameters: $1 = 'Customer4', $2 = 'Position 4', $3 = '40', $4 = 'cc521007-820f-4d58-8e1a-16a166aa91cf'
2015-12-15 12:07:00.649 MSK LOG: execute S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:07:00.649 MSK DETAIL: parameters: $1 = 'Customer4', $2 = 'Position 4', $3 = '40', $4 = 'cc521007-820f-4d58-8e1a-16a166aa91cf'
2015-12-15 12:07:00.649 MSK LOG: duration: 0.000 ms
... the rest of the logs is identical and do not provide any valuable information...
Và khi báo cáo hàng loạt bị vô hiệu hóa (p6spy cho thấy rằng không có trạm trộn được thực hiện) , nhật ký sẽ trông giống như sau:
2015-12-15 12:09:00.246 MSK LOG: execute <unnamed>: BEGIN
2015-12-15 12:09:00.246 MSK LOG: duration: 0.000 ms
2015-12-15 12:09:00.246 MSK LOG: duration: 0.000 ms parse <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.246 MSK LOG: duration: 0.000 ms bind <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.246 MSK DETAIL: parameters: $1 = 'Customer0', $2 = 'Position 0', $3 = '0', $4 = '9e085ad0-437f-4d7d-afaa-e342e031cbee'
2015-12-15 12:09:00.246 MSK LOG: execute <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.246 MSK DETAIL: parameters: $1 = 'Customer0', $2 = 'Position 0', $3 = '0', $4 = '9e085ad0-437f-4d7d-afaa-e342e031cbee'
2015-12-15 12:09:00.246 MSK LOG: duration: 0.000 ms
2015-12-15 12:09:00.248 MSK LOG: duration: 0.000 ms parse <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.248 MSK LOG: duration: 0.000 ms bind <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.248 MSK DETAIL: parameters: $1 = 'Customer1', $2 = 'Position 1', $3 = '10', $4 = 'f29cfa40-7d24-49a6-ae5d-2a2021932d80'
2015-12-15 12:09:00.248 MSK LOG: execute <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.248 MSK DETAIL: parameters: $1 = 'Customer1', $2 = 'Position 1', $3 = '10', $4 = 'f29cfa40-7d24-49a6-ae5d-2a2021932d80'
2015-12-15 12:09:00.249 MSK LOG: duration: 1.000 ms
2015-12-15 12:09:00.250 MSK LOG: duration: 0.000 ms parse <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.250 MSK LOG: duration: 0.000 ms bind <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.250 MSK DETAIL: parameters: $1 = 'Customer2', $2 = 'Position 2', $3 = '20', $4 = '067dd6d4-5060-467f-b533-75994ecbaedc'
2015-12-15 12:09:00.250 MSK LOG: execute <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.250 MSK DETAIL: parameters: $1 = 'Customer2', $2 = 'Position 2', $3 = '20', $4 = '067dd6d4-5060-467f-b533-75994ecbaedc'
2015-12-15 12:09:00.250 MSK LOG: duration: 0.000 ms
2015-12-15 12:09:00.250 MSK LOG: duration: 0.000 ms parse <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.250 MSK LOG: duration: 0.000 ms bind <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.251 MSK DETAIL: parameters: $1 = 'Customer3', $2 = 'Position 3', $3 = '30', $4 = '7df32327-f2f5-4011-848d-55aafb3f09fa'
2015-12-15 12:09:00.251 MSK LOG: execute <unnamed>: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.251 MSK DETAIL: parameters: $1 = 'Customer3', $2 = 'Position 3', $3 = '30', $4 = '7df32327-f2f5-4011-848d-55aafb3f09fa'
2015-12-15 12:09:00.251 MSK LOG: duration: 0.000 ms
2015-12-15 12:09:00.251 MSK LOG: duration: 0.000 ms parse S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.251 MSK LOG: duration: 0.000 ms bind S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.251 MSK DETAIL: parameters: $1 = 'Customer4', $2 = 'Position 4', $3 = '40', $4 = '1e55ab6a-8780-4c8f-8af2-2886d954f819'
2015-12-15 12:09:00.251 MSK LOG: execute S_1: insert into customer (name, position, salary, id) values ($1, $2, $3, $4)
2015-12-15 12:09:00.251 MSK DETAIL: parameters: $1 = 'Customer4', $2 = 'Position 4', $3 = '40', $4 = '1e55ab6a-8780-4c8f-8af2-2886d954f819'
2015-12-15 12:09:00.251 MSK LOG: duration: 0.000 ms
... the rest of the logs is identical and do not provide any valuable information...
Vì vậy, sự khác biệt duy nhất giữa hai lô là khi lô bị vô hiệu, postgres sẽ mất nhiều thời gian hơn để nhận ra rằng nên sử dụng lại câu lệnh đã chuẩn bị.
Vì vậy, tôi quyết định chạy thử nghiệm hiệu suất để xác nhận điều này.
Tôi đã thử chèn 30.000 bản ghi vào cơ sở dữ liệu trống.
Với chức năng xử lý hàng loạt bị vô hiệu hóa, phải mất 334ms.
Với chức năng nhóm được kích hoạt, nó mất 4650ms!
Vì vậy, sau đó tôi đã xóa tất cả các cuộc gọi đến entityManager.flush (chỉ để lại entityManager.clear) và thời gian giảm xuống 320ms. Tôi không biết tại sao hướng dẫn của hibernate khuyên dùng flush. Tôi đoán đó chỉ là một sai lầm.
Vì vậy, tôi đoán điểm mấu chốt là: việc xử lý theo lô đang hoạt động, nhưng nó không cung cấp bất kỳ lợi ích thực sự nào (ít nhất là cho bưu phí). Hơn nữa, sử dụng nó không đúng (như trong hướng dẫn) có thể dẫn đến một kết quả khủng khiếp, khủng khiếp. Sử dụng rủi ro của riêng bạn và luôn đo lường sự tăng hiệu suất thực tế.
Bạn đã xác nhận rằng trình điều khiển JDBC của bạn hỗ trợ các hoạt động DDL hàng loạt?Trong khi cơ sở dữ liệu có thể hỗ trợ nó, nếu phiên bản của trình điều khiển của bạn không, nó sẽ không có bất kỳ ảnh hưởng nào. – Naros
Có, trình điều khiển jdbc hỗ trợ hàng loạt. – Alexey
Thêm lớp 'Customer' của bạn. Bạn đang sử dụng Spring boot để 'spring.jpa.properties' của bạn là cái bạn nên sử dụng, những cái khác thì khá vô dụng. –