2013-03-14 41 views
10

EDIT: Dựa trên một số lần gỡ lỗi và ghi nhật ký của tôi, tôi nghĩ câu hỏi sẽ giảm xuống tại sao là DELETE FROM table WHERE id = x nhanh hơn nhiều so với DELETE FROM table WHERE id IN (x) trong đó x chỉ là một ID duy nhất.Xóa hàng loạt Hibernate và xóa một lần

Gần đây tôi đã thử nghiệm xóa hàng loạt so với xóa từng hàng một và nhận thấy rằng xóa hàng loạt chậm hơn nhiều. Bảng đã kích hoạt để xóa, cập nhật và chèn nhưng tôi đã thử nghiệm có và không có trình kích hoạt và mỗi lần xóa hàng loạt chậm hơn. Bất cứ ai có thể làm sáng tỏ lý do tại sao đây là trường hợp hoặc chia sẻ các mẹo về cách tôi có thể gỡ lỗi này? Từ những gì tôi hiểu, tôi thực sự không thể giảm số lần kích hoạt kích hoạt nhưng ban đầu tôi đã tìm ra rằng việc giảm số lượng truy vấn "xóa" sẽ giúp ích cho hiệu suất.

Tôi đã bao gồm một số thông tin bên dưới, vui lòng cho tôi biết nếu tôi đã loại bỏ bất kỳ điều gì có liên quan.

xóa được thực hiện theo lô 10.000 và mã nhìn cái gì đó như:

private void batchDeletion(Collection<Long> ids) { 
    StringBuilder sb = new StringBuilder(); 
    sb.append("DELETE FROM ObjImpl WHERE id IN (:ids)"); 

    Query sql = getSession().createQuery(sb.toString()); 
    sql.setParameterList("ids", ids); 

    sql.executeUpdate(); 
} 

Mã xóa chỉ một hàng duy nhất về cơ bản là:

SessionFactory.getCurrentSession().delete(obj); 

Bảng này có hai chỉ số đó là không được sử dụng trong bất kỳ việc xóa nào. Sẽ không có hoạt động xếp tầng nào xảy ra.

Dưới đây là một mẫu của các GIẢI THÍCH PHÂN TÍCH của DELETE FROM table where id IN (1, 2, 3);:

Delete on table (cost=12.82..24.68 rows=3 width=6) (actual time=0.143..0.143 rows=0 loops=1) 
    -> Bitmap Heap Scan on table (cost=12.82..24.68 rows=3 width=6) (actual time=0.138..0.138 rows=0 loops=1) 
     Recheck Cond: (id = ANY ('{1,2,3}'::bigint[])) 
     -> Bitmap Index Scan on pk_table (cost=0.00..12.82 rows=3 width=0) (actual time=0.114..0.114 rows=0 loops=1) 
       Index Cond: (id = ANY ('{1,2,3}'::bigint[])) 
Total runtime: 3.926 ms 

Tôi đã hút bụi và lập chỉ mục mỗi khi tôi tải lại dữ liệu của tôi để thử nghiệm và kiểm tra dữ liệu của tôi chứa 386.660 hàng.

Thử nghiệm là xóa tất cả các hàng và tôi không sử dụng TRUNCATE vì thường có tiêu chí lựa chọn nhưng cho mục đích thử nghiệm, tôi đã thực hiện tiêu chí bao gồm tất cả các hàng. Với kích hoạt được kích hoạt, xóa từng hàng một từng mất 193.616ms trong khi hàng loạt-xóa mất 285.558ms. Sau đó, tôi vô hiệu hóa trình kích hoạt và nhận 93.793ms cho các lần xóa hàng đơn và 181.537ms cho xóa hàng loạt. Kích hoạt đi và tổng kết các giá trị và cập nhật một bảng khác - về cơ bản là sổ sách kế toán.

Tôi đã chơi xung quanh với kích thước lô thấp hơn (100 và 1) và tất cả dường như có hiệu suất kém hơn.

EDIT: bật Hibernate khai thác gỗ và hàng duy nhất bởi hàng delete, nó về cơ bản thực hiện: delete from table where id=? và GIẢI THÍCH PHÂN TÍCH:

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.042..0.042 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.037..0.037 rows=0 loops=1) 
     Index Cond: (id = 3874904) 
Total runtime: 0.130 ms 

EDIT: Đã tò mò nếu danh sách thực sự chứa 10.000 ID, nếu Postgres sẽ làm điều gì đó khác: không.

Delete on table (cost=6842.01..138509.15 rows=9872 width=6) (actual time=17.170..17.170 rows=0 loops=1) 
    -> Bitmap Heap Scan on table (cost=6842.01..138509.15 rows=9872 width=6) (actual time=17.160..17.160 rows=0 loops=1) 
     Recheck Cond: (id = ANY ('{NUMBERS 1 THROUGH 10,000}'::bigint[])) 
     -> Bitmap Index Scan on pk_table (cost=0.00..6839.54 rows=9872 width=0) (actual time=17.139..17.139 rows=0 loops=1) 
       Index Cond: (id = ANY ('{NUMBERS 1 THROUGH 10,000}'::bigint[])) 
Total runtime: 17.391 ms 

CHỈNH SỬA: Dựa trên GIẢI THÍCH GIẢI THÍCH ở trên, tôi đã lấy một số lần ghi lại từ các thao tác xóa thực tế. Dưới đây là ghi nhật ký hai biến thể của hàng đơn bằng xóa hàng.

Dưới đây là một số xóa duy nhất:

2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 
2013-03-14 13:09:25,424:delete from table where id=? 

Đây là biến thể khác của xóa đơn (danh sách chỉ là 1 item)

2013-03-14 13:49:59,858:delete from table where id in (?) 
2013-03-14 13:50:01,460:delete from table where id in (?) 
2013-03-14 13:50:03,040:delete from table where id in (?) 
2013-03-14 13:50:04,544:delete from table where id in (?) 
2013-03-14 13:50:06,125:delete from table where id in (?) 
2013-03-14 13:50:07,707:delete from table where id in (?) 
2013-03-14 13:50:09,275:delete from table where id in (?) 
2013-03-14 13:50:10,833:delete from table where id in (?) 
2013-03-14 13:50:12,369:delete from table where id in (?) 
2013-03-14 13:50:13,873:delete from table where id in (?) 

Cả hai đều là ID mà tồn tại trong bảng và nên được tuần tự.


GIẢI THÍCH PHÂN TÍCH của DELETE FROM table WHERE id = 3774887;

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.097..0.097 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.055..0.058 rows=1 loops=1) 
     Index Cond: (id = 3774887) 
Total runtime: 0.162 ms 

giải thích PHÂN TÍCH của DELETE FROM table WHERE id IN (3774887);

Delete on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.279..0.279 rows=0 loops=1) 
    -> Index Scan using pk_table on table (cost=0.00..8.31 rows=1 width=6) (actual time=0.210..0.213 rows=1 loops=1) 
     Index Cond: (id = 3774887) 
Total runtime: 0.452 ms 

0,162 vs 0,452 khác biệt đáng kể coi?

EDIT:

Set hàng loạt kích thước đến 50.000 và Hibernate không thích ý tưởng:

java.lang.StackOverflowError 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:40) 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:41) 
     at org.hibernate.hql.ast.util.NodeTraverser.visitDepthFirst(NodeTraverser.java:42) 
.... 
+0

Dường như khi bạn xóa một hàng, nó sẽ sử dụng chỉ mục khác. Bạn có thể kiểm tra nó? – ntalbs

+0

Tôi đã thêm thông tin về hàng đơn theo xóa hàng. MAX (id) của bảng là 3774904, vì vậy tôi chỉ chọn một số lớn hơn. Tôi đã thử cùng với một ID dưới 3774904 nhưng không có trong bảng và có kết quả tương tự (kết quả tương tự cho một ID trong bảng). – nevets1219

+1

có, 0,162 so với 0,452 * là * có ý nghĩa vì 0,452 dài hơn 3 lần so với 0,152. Điều đặc biệt thú vị là giải thích cho thấy rằng họ cũng đang làm điều tương tự. Tôi sẽ nâng cao điều này với nhóm postgresql. [Pgsql-general] (http://www.postgresql.org/community/lists/) có lẽ là một nơi tốt –

Trả lời

5

Ok, điều đầu tiên bạn cần phải lưu ý là SQL đã được chuyển đổi thành một kế hoạch một cách nào đó. Kết quả EXPLAIN của bạn cho thấy rằng logic ở đây về cơ bản khác nhau đối với sự bình đẳng so với cấu trúc IN (vals).

WHERE id = 1; 

Được chuyển thành bộ lọc bình đẳng đơn giản.

WHERE id IN (1); 

được chuyển thành một trận đấu mảng:

WHERE id = ANY(ARRAY[1]); 

Rõ ràng các nhà quy hoạch là không đủ thông minh để nhận thấy rằng đây là những mặt toán học giống hệt nhau nơi một mảng có chính xác một thành viên. Vì vậy, những gì nó đang làm là lập kế hoạch cho một mảng của bất kỳ kích thước đó là lý do tại sao bạn có được quét vòng lặp chỉ mục bitmap lồng nhau.

Điều thú vị ở đây không chỉ là nó chậm hơn mà hiệu suất đó còn tốt hơn cho hầu hết các phần. Vì vậy, với một thành viên trong mệnh đề in(), nó chậm hơn 40 lần, và với 10000 thành viên, nó chỉ chậm hơn 170 lần, nhưng điều đó cũng có nghĩa là phiên bản 10000 thành viên cũng nhanh hơn 50 lần nhanh hơn. trên id. Vì vậy, những gì đang xảy ra ở đây là lập kế hoạch đang chọn một kế hoạch thực hiện tốt hơn khi có một số lượng lớn id được kiểm tra nhưng hoạt động kém hơn khi chỉ có một vài.

+0

Tôi sẽ cố gắng tăng kích thước mảng và báo cáo lại những gì sẽ xảy ra! Có nguyên tắc chung để chọn kích thước lô không? – nevets1219

+0

Có vẻ Hibernate không thích 50.000 (đã cho lỗi tràn ngăn xếp) – nevets1219

+0

Đọc lại những gì bạn đã viết - Tôi đã thử cả hai "WHERE id = 1;' (chúng tôi sẽ gọi đây là A) và 'WHERE id IN (1);' (chúng tôi sẽ gọi B này) và sau này là chậm hơn. Nếu tôi hiểu bạn một cách chính xác khi thực thi A thì giống như thực thi B nhưng khi thực hiện B, một kế hoạch khác (có thể sử dụng danh sách ID lớn) có thể được sử dụng để thực thi (nói cách khác B không bằng A). Tôi đã thực hiện cả A và B và họ tạo ra cùng một kế hoạch, có thể Hibernate đang làm gì đó với nó? – nevets1219

4

Nếu sự cố ở đây thực sự rơi xuống "làm cách nào để xóa nhiều bản ghi nhanh nhất có thể?" thì phương thức DELETE ... IN() sẽ vượt trội hơn xóa bỏ cho mỗi hàng riêng lẻ, vì vậy theo đuổi các lý do tại sao IN (?) với một thành viên đơn lẻ có vẻ chậm hơn =? sẽ không giúp bạn.

Có thể đáng khám phá việc sử dụng bảng tạm thời để giữ tất cả các id mà bạn muốn xóa, sau đó chạy một lần xóa.

Nếu không quá tốn kém, sắp xếp các id trong danh sách theo thứ tự tăng dần có thể hữu ích cho hiệu suất xóa rất lớn.Đừng bận tâm nếu bạn phải phân loại chúng, nhưng nếu có một cách để đảm bảo rằng mỗi lô xóa địa chỉ của id được nhóm trong cùng một khu vực của chỉ mục có thể hơi có lợi.

Trong mọi trường hợp, có vẻ như tôi đang sử dụng các chỉ mục và cùng một kế hoạch được tạo trong cả hai trường hợp, vì vậy tôi tự hỏi liệu có vấn đề về phân tích cú pháp và tối ưu hóa truy vấn tại đây hay không. . Tôi không biết đủ về những người bên trong để chắc chắn rằng tôi sợ.

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