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
计划完全相同!
答案 0 :(得分:11)
由于PostgreSQL MVCC,UPDATE
实际上几乎就像DELETE
加上INSERT
- 除了敬酒值之外。参见:
准确地说,“删除”行对于在提交删除后开始的任何事务都是不可见的,并且稍后会被清空。因此,在数据库方面,包括索引操作,两个语句之间实际上存在无差异。 (例外情况适用,请继续阅读。)它会稍微增加网络流量(取决于您的数据)并需要进行一些解析。
在@ araqnid输入之后我研究了HOT更新并进行了一些测试。就HOT更新而言,实际上不会更改值的列的更新没有任何区别。我的回答成立。请参阅下面的详细信息。
这也适用于烘烤属性,因为除非值实际更改,否则也不会触及这些属性。
然而,如果您使用每列触发器(随第9.0页引入),则可能会产生不良副作用!
...
UPDATE ... SET x = x ...
之类的命令会触发一个触发器 列x
,,即使列的值未更改。
大胆强调我的。
抽象层是为了方便起见。它们对于SQL文盲的开发人员非常有用,或者如果应用程序需要在不同的RDBMS之间移植。在不利方面,他们可以屠宰性能并引入额外的失败点。我尽可能地避开它们。
仅使用Postgres 8.3引入仅堆元组,8.3.4和8.4.9有重要改进。
The release notes for Postgres 8.3:
UPDATE
和DELETE
会留下死亡元组,失败的INSERT
也是如此。 以前只有VACUUM
可以回收死元组占用的空间。同 HOT死元组空间可以在此时自动回收 如果未对索引列进行任何更改,则INSERT
或UPDATE
。这个 允许更一致的性能。此外,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
这将仅更新已更改的字段。