Postgresql Postgres:将列值更新为相同的值是否会将页面标记为脏页面?

Postgresql Postgres:将列值更新为相同的值是否会将页面标记为脏页面?,postgresql,Postgresql,在PostgreSQL 10+的任何版本中考虑以下场景: 创建表用户 id串行主键, 名称文本不为空且唯一, 上次看到的时间戳 ; 插入到usersname中,最后一次看到 值“Alice”、“2019-05-01”, “鲍勃”,“2019-04-29”, “Dorian”,“2019-05-11”; 创建表非活动用户 user_id int主键引用usersid, 上次看到的时间戳不为空; 插入到非活动用户SUSER\U id中,最后一次看到 选择id作为用户的最后一个id 上次看到用户的地方

在PostgreSQL 10+的任何版本中考虑以下场景:

创建表用户 id串行主键, 名称文本不为空且唯一, 上次看到的时间戳 ; 插入到usersname中,最后一次看到 值“Alice”、“2019-05-01”, “鲍勃”,“2019-04-29”, “Dorian”,“2019-05-11”; 创建表非活动用户 user_id int主键引用usersid, 上次看到的时间戳不为空; 插入到非活动用户SUSER\U id中,最后一次看到 选择id作为用户的最后一个id 上次看到用户的地方<'2019-05-04' 在发生冲突时,用户\u id DO UPDATE SET last\u seen=excluded.last\u seen; 现在让我们假设我想插入相同的值,并不时多次执行上一条语句。实际上,从数据库的角度来看,在冲突的值上,上次看到列的90%的时间将更新为它已经拥有的相同值。行的值保持不变,因此没有理由进行I/O写入,对吗?但这是真的吗?或者,即使实际值没有改变,postgres会执行相应的更新吗

在我的例子中,目标表有数以千万计的行,但在每次insert调用中只有几百行/数千行真正发生变化。

对一行的任何更新实际上都会创建一个新行,标记旧行已删除/脏,而不考虑before/after值:

[root@497ba0eaf137 /]# psql
psql (12.1)
Type "help" for help.

postgres=# create table foo (id int, name text);
CREATE TABLE
postgres=# insert into foo values (1,'a');
INSERT 0 1
postgres=# select ctid,* from foo;
 ctid  | id | name 
-------+----+------
 (0,1) |  1 | a
(1 row)

postgres=# update foo set name = 'a' where id = 1;
UPDATE 1
postgres=# select ctid,* from foo;
 ctid  | id | name 
-------+----+------
 (0,2) |  1 | a
(1 row)

postgres=# update foo set id = 1 where id = 1;
UPDATE 1
postgres=# select ctid,* from foo;
 ctid  | id | name 
-------+----+------
 (0,3) |  1 | a
(1 row)

postgres=# select * from pg_stat_user_tables where relname = 'foo';
-[ RECORD 1 ]-------+-------
relid               | 16384
schemaname          | public
relname             | foo
seq_scan            | 5
seq_tup_read        | 5
idx_scan            | 
idx_tup_fetch       | 
n_tup_ins           | 1
n_tup_upd           | 2
n_tup_del           | 0
n_tup_hot_upd       | 2
n_live_tup          | 1
n_dead_tup          | 2
<...>
根据你的例子:

postgres=# select ctid,* FROM inactive_users ;
 ctid  | user_id |      last_seen      
-------+---------+---------------------
 (0,1) |       1 | 2019-05-01 00:00:00
 (0,2) |       2 | 2019-04-29 00:00:00
(2 rows)

postgres=# INSERT INTO inactive_users(user_id, last_seen)
postgres-# SELECT id as user_id, last_seen FROM users 
postgres-# WHERE users.last_seen < '2019-05-04' 
postgres-# ON CONFLICT (user_id) DO UPDATE SET last_seen = excluded.last_seen;
INSERT 0 2
postgres=# select ctid,* FROM inactive_users ;
 ctid  | user_id |      last_seen      
-------+---------+---------------------
 (0,3) |       1 | 2019-05-01 00:00:00
 (0,4) |       2 | 2019-04-29 00:00:00
(2 rows)
Postgres不会对列值进行任何数据验证-如果您希望防止不必要的写入活动,则需要通过外科手术精心设计WHERE子句


披露:我为

工作,如果更改WHERE子句不是一个选项,另一个选项可能是使用SUPPRES\u REDUNDY\u updates\u触发器使用触发器