Tai*_*air 7 postgresql hibernate jpa sql-execution-plan
UPDATE无论我是否更改了列中的值,Hibernate都会生成包含所有列的语句,例如:
tx.begin();
Item i = em.find(Item.class, 12345);
i.setA("a-value");
tx.commit();
Run Code Online (Sandbox Code Playgroud)
发表此UPDATE声明:
update Item set A = $1, B = $2, C = $3, D = $4 where id = $5
Run Code Online (Sandbox Code Playgroud)
因此列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();
Run Code Online (Sandbox Code Playgroud)
最让我困惑的是,EXPLAIN"未经优化"和"优化"查询版本的计划完全相同!
Erw*_*ter 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中有重要改进.
Postgres 8.3的发行说明:
UPDATEs和DELETEs留下了死去的元组,失败了INSERT.以前只能VACUUM收回死元组占用的空间.使用HOT死元组空间可以在索引列时INSERT或者UPDATE如果没有对索引列进行更改时自动回收 .这样可以实现更一致的性能.此外,HOT避免添加重复的索引条目.
强调我的.并且"无更改"包括使用与已存在的值相同的值更新列的情况.我实际上刚刚测试过,因为我不确定.
烤柱也不妨碍HOT更新.HOT更新的元组只链接到关系的toast fork中相同的,未更改的元组.HOT更新甚至可以使用目标列表中的烘焙值(实际更改与否).如果改变了烘烤的值,显然需要写入toast关系分支.我也测试了所有这些.
你不必接受我的话.看看自己,Postgres提供了几个检查统计数据的功能.运行您的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)
Run Code Online (Sandbox Code Playgroud)
或者使用pgAdmin.选择表并检查主窗口中的"统计"选项卡.
请注意,只有在主关系分叉的同一页面上有新元组版本的空间时才可以进行HOT更新.强制该条件的一种简单方法是使用仅包含几行的小表进行测试.页面大小通常为8k,因此页面上必须有可用空间.