在 PostgreSQL 中,违反唯一约束的行也会被写入表中

   

在 PostgreSQL 中,违反唯一性的行也会被写入表中。这听起来很不可思议。实际上,这些行确实会被写入表中,只是不会显示在查询结果中。

 我们在PostgreSQL 10 上来验证这一点。

 首先,我们在创建表 man,代码如下。其中字段id 上有 主键约束。

     CREATE TABLE man
    (
      id integer NOT NULL,
      name character varying(32),
      gender int,
      birthday date,
      CONSTRAINT pk_man PRIMARY KEY (id)
      );

 

好的,现在我们向表中插入一条数据:

insert into man (id,name,gender,birthday) values (1,'Ronaldo', '0', '2000-01-01');

 

查看这张表的总大小,即表和索引的大小之和:

select pg_total_relation_size('man');

结果如下:

pg_total_relation_size

---------------------

24576

只有一条数据时,它的大小是24576字节。

 

我们再来从 pg_stat_user_tables 查看这张表的行的统计信息:

select relid, relname, n_tup_ins,n_tup_upd, n_tup_del, n_live_tup, n_dead_tup from pg_stat_user_tables where relname = 'man';

结果如下:

relid  | relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup

-------+---------+-----------+-----------+-----------+------------+-----------

558236 | man     |         1 |         0 |         0 |          1 |         0

 

可以看出,n_live_tup = 1, n_dead_tup = 0,这说明表中有且只有1行活着的元组,没有死亡的元组。

 

 

现在,让我们执行下面的sql语句,将这条数据重复插入1000次,看看有什么结果。

 

do

$$

begin

    for i in 1..1000

    loop

        begin

        insert into man (id,name,gender,birthday) values (1,'Ronaldo', '0', '1970-01-01');

 

        exception when unique_violation then

            raise notice 'duplicate key value violates unique constraint "pk_man"';

        end;

    end loop;

end;

$$ language plpgsql;

 

 

再来查看表man的大小:

 

 select pg_total_relation_size('man');

 

结果如下:

pg_total_relation_size

------------------------

                 98304

 

它的大小变为了98304字节。

 

我们看看在pg_stat_user_tables 中,表man 的统计信息的变化:

 

select relid, relname, n_tup_ins,n_tup_upd, n_tup_del, n_live_tup, n_dead_tup from pg_stat_user_tables where relname = 'man';

结果如下:

relid  | relname | n_tup_ins | n_tup_upd | n_tup_del | n_live_tup | n_dead_tup

-------+---------+-----------+-----------+-----------+------------+-----------

558236 | man     |         1001 |         0 |         0 |          1 |         1000

 

可以看出,表中 n_dead_tup=1001,这表示 表中有1000个死亡元组。

 

上面的实验说明:对PostgreSQL而言,即使向表中插入的数据元组违反了唯一约束,它仍然能插入到数据库中,只不过被标记为“dead”,从而不会展示。但这些死亡的元组仍然占据着磁盘空间,知道它们被自动或者手动清理掉。因此,把主键约束或唯一约束作为防止重复数据的首要的防线,不是明智的做法。我们应该尽量在上层程序中保证数据的唯一性。

 

 

附录

  1. pg_stat_user_table 介绍

pg_stat_user_table显示的是用户表的统计信息。下面是这些它的列及其含义:

 

类型

描述

relid

oid

一个表的 OID

schemaname

name

这个表所在的模式的名称

relname

name

这个表的名称

seq_scan

bigint

在这个表上发起的顺序扫描的次数

seq_tup_read

bigint

被顺序扫描取得的活着的行的数量

idx_scan

bigint

在这个表上发起的索引扫描的次数

idx_tup_fetch

bigint

被索引扫描取得的活着的行的数量

n_tup_ins

bigint

被插入的行数

n_tup_upd

bigint

被更新的行数(包括 HOT 更新的行)

n_tup_del

bigint

被删除的行数

n_tup_hot_upd

bigint

被更新的 HOT 行数(即不要求独立索引更新的行更新)

n_live_tup

bigint

活着的行的估计数量

n_dead_tup

bigint

死亡行的估计数量

n_mod_since_analyze

bigint

从这个表最后一次被分析后备修改的行的估计数量

last_vacuum

timestamp with time zone

上次这个表被手动清理的时间(不统计VACUUM FULL

last_autovacuum

timestamp with time zone

上次这个表被自动清理守护进程清理的时间

last_analyze

timestamp with time zone

上次这个表被手动分析的时间

last_autoanalyze

timestamp with time zone

上次这个表被自动清理守护进程分析的时间

vacuum_count

bigint

这个表已被手工清理的次数(不统计VACUUM FULL

autovacuum_count

bigint

这个表已被自动清理守护进程清理的次数

analyze_count

bigint

这个表已被手工分析的次数

autoanalyze_count

bigint

这个表已被自动清理守护进程分析的次数

 

     

猜你喜欢

转载自blog.csdn.net/international24/article/details/107339749