nev*_*219 10 java postgresql hibernate
编辑:根据我的一些调试和记录的,我觉得这个问题归结到为什么DELETE FROM table WHERE id = x比快得多DELETE FROM table WHERE id IN (x)这里x只是一个单一的ID.
我最近测试了批量删除与逐行删除每一行,并注意到批量删除速度要慢得多.该表有删除,更新和插入的触发器,但我已经测试了有没有触发器,每次批量删除都比较慢.任何人都可以解释为什么会出现这种情况或分享我如何调试这个的提示?根据我的理解,我无法真正减少触发器激活的次数,但我原先认为降低"删除"查询的数量将有助于提高性能.
我在下面提供了一些信息,如果我遗漏了任何相关信息,请告诉我.
删除分批完成10,000次,代码如下:
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();
}
Run Code Online (Sandbox Code Playgroud)
删除一行的代码基本上是:
SessionFactory.getCurrentSession().delete(obj);
Run Code Online (Sandbox Code Playgroud)
该表有两个索引,不用于任何删除.不会发生级联操作.
以下是EXPLAIN ANALYZE的示例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
Run Code Online (Sandbox Code Playgroud)
每次重新加载数据进行测试时,我都会进行清理并重新编制索引,而我的测试数据包含386,660行.
测试是删除所有行,我没有使用,TRUNCATE因为通常有一个选择标准,但出于测试目的,我已经使标准包括所有行.启用触发器后,逐行删除每行占用193,616ms,而批量删除占用285,558ms.然后我禁用了触发器,单行删除为93,793ms,批量删除为181,537ms.触发器进行并总结值并更新另一个表 - 基本上是簿记.
我玩过较低的批量(100和1)并且它们似乎都表现得更差.
编辑:打开Hibernate日志记录和逐行删除,它基本上做:delete from table where id=?和EXPLAIN ANALYZE:
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
Run Code Online (Sandbox Code Playgroud)
编辑:好奇如果列表实际上包含10,000个ID,如果Postgres会做一些不同的事情:不.
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
Run Code Online (Sandbox Code Playgroud)
编辑:基于以上的EXPLAIN ANALYZE,我从实际的删除操作中检索了一些日志记录.下面是逐行删除的两个变体的记录.
以下是一些删除:
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=?
Run Code Online (Sandbox Code Playgroud)
这是单个删除的其他变体(列表只有1个项目)
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 (?)
Run Code Online (Sandbox Code Playgroud)
两者都是表中存在的ID,应该是顺序的.
解析分析 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
Run Code Online (Sandbox Code Playgroud)
解析分析 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
Run Code Online (Sandbox Code Playgroud)
0.162 vs 0.452被认为有显着差异?
编辑:
将批量大小设置为50,000,Hibernate不喜欢这个想法:
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)
....
Run Code Online (Sandbox Code Playgroud)
好的,首先要注意的是SQL必须以某种方式转换为计划.你的EXPLAIN结果表明,与IN(vals)构造相比,这里的逻辑与平等根本不同.
WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)
被转换为简单的相等过滤器.
WHERE id IN (1);
Run Code Online (Sandbox Code Playgroud)
转换成数组匹配:
WHERE id = ANY(ARRAY[1]);
Run Code Online (Sandbox Code Playgroud)
显然,规划人员并不聪明,没有注意到这些在数学上只有一个成员的数学上是相同的.所以它正在做的是计划任何大小的数组,这就是为什么你得到嵌套循环位图索引扫描.
这里有趣的不仅仅是速度较慢,而且大多数情况下性能更好.因此,在in()子句中有一个成员,它慢了40倍,并且有10000个成员,它只慢了170倍,但这也意味着10000成员版本也比id上10000个单独的索引扫描快 50倍.
所以这里发生的事情是规划人员正在选择一个计划,当有大量的id被检查时它会表现得更好,但是当只有少数id时执行得更差.