更新语句中的冗余数据

时间:2011-10-18 10:49:30

标签: hibernate postgresql jpa sql-execution-plan

Hibernate生成UPDATE语句,其中包括所有列,无论我是否更改了该列中的值,例如:

tx.begin();
Item i = em.find(Item.class, 12345);
i.setA("a-value");
tx.commit();

发出此UPDATE声明:

update Item set A = $1, B = $2, C = $3, D = $4 where id = $5

所以列B,C,D会更新,而我没有更改它们。

说,项目经常更新,所有列都被编入索引。 问题是:将Hibernate部分优化为以下内容是否有意义:

tx.begin();
em.createQuery("update Item i set i.a = :a where i.id = :id")
    .setParameter("a", "a-value")
    .setParameter("id", 12345)
    .executeUpdate();
tx.commit();

最让我困惑的是,'未优化'和'优化'查询版本的EXPLAIN计划完全相同!

2 个答案:

答案 0 :(得分:11)

由于PostgreSQL MVCCUPDATE实际上几乎就像DELETE加上INSERT - 除了敬酒值之外。参见:

准确地说,“删除”行对于在提交删除后开始的任何事务都是不可见的,并且稍后会被清空。因此,在数据库方面,包括索引操作,两个语句之间实际上存在无差异。 (例外情况适用,请继续阅读。)它会稍微增加网络流量(取决于您的数据)并需要进行一些解析。

在@ araqnid输入之后我研究了HOT更新并进行了一些测试。就HOT更新而言,实际上不会更改值的列的更新没有任何区别。我的回答成立。请参阅下面的详细信息。

这也适用于烘烤属性,因为除非值实际更改,否则也不会触及这些属性。

然而,如果您使用每列触发器(随第9.0页引入),则可能会产生不良副作用!

我引用the manual on triggers

  

... UPDATE ... SET x = x ...之类的命令会触发一个触发器   列x,即使列的值未更改

大胆强调我的。

抽象层是为了方便起见。它们对于SQL文盲的开发人员非常有用,或者如果应用程序需要在不同的RDBMS之间移植。在不利方面,他们可以屠宰性能并引入额外的失败点。我尽可能地避开它们。

关于HOT(仅限堆元组)更新

仅使用Postgres 8.3引入仅堆元组,8.3.48.4.9有重要改进。
The release notes for Postgres 8.3:

  

UPDATEDELETE会留下死亡元组,失败的INSERT也是如此。   以前只有VACUUM可以回收死元组占用的空间。同   HOT死元组空间可以在此时自动回收   如果未对索引列进行任何更改,则INSERTUPDATE 。这个   允许更一致的性能。此外,HOT避免添加   重复的索引条目。

强调我的。并且“无更改”包括使用与已存在的值相同的值更新列的情况。我现在实际测试,因为我不确定。

烤制的柱子也不会妨碍HOT更新。 HOT更新的元组只链接到关系的toast fork中相同的,未更改的元组。 HOT更新甚至可以使用目标列表中的烘焙值(实际上已更改或未更改)。如果改变了烘烤的值,显然需要写入toast关系分支。我也测试了所有这些。

你不必接受我的话。看看自己,Postgres提供了几个functions to check statistics。使用和不使用所有列运行UPDATE并检查它是否有任何区别。

-- Number of rows HOT-updated in table:
SELECT pg_stat_get_tuples_hot_updated('table_name'::regclass::oid)

-- Number of rows HOT-updated in table, in the current transaction:
SELECT pg_stat_get_xact_tuples_hot_updated('table_name'::regclass::oid)

或使用pgAdmin。选择您的表格并检查主窗口中的“统计”选项卡。

请注意,只有在主关系分叉的同一页面上有新元组版本的空间时才可以进行HOT更新。强制该条件的一种简单方法是使用仅包含几行的小表进行测试。页面大小通常为8k,因此页面上必须有可用空间。

答案 1 :(得分:3)

您可以使用hibernate注释@Entity:

@org.hibernate.annotations.Entity(dynamicUpdate = true)
public class Item

这将仅更新已更改的字段。