PostgreSQL 是通过索引来保证唯一值约束的,(包括PKEY)。
但是,如果遇到唯一约束问题,HEAP和BTREE页里的数据会不会有垃圾呢?
SESSION A:
digoal=> create table pk_test(id int primary key);
CREATE TABLE
Time: 51.559 ms
digoal=> begin;
BEGIN
Time: 0.103 ms
digoal=> insert into pk_test values (1);
INSERT 0 1
Time: 0.565 ms
SESSION B:
digoal=> begin;
BEGIN
digoal=> insert into pk_test values (1);
SESSION C:
digoal=> begin;
BEGIN
digoal=> insert into pk_test values (1);
观察有无行锁:
显然没有,因为不是靠锁来保证唯一。而且插入也不需要行锁,因为未提交的记录其他会话是看不到的,也不可能来查询或更新,没有加锁的必要。
digoal=# create extension pgrowlocks;
CREATE EXTENSION
digoal=# select * from pgrowlocks('pk_test');
locked_row | locker | multi | xids | modes | pids
------------+--------+-------+------+-------+------
(0 rows)
观察对象锁等待:
digoal=# create or replace function f_lock_level(i_mode text) returns int as $$
declare
begin
case i_mode
when 'INVALID' then return 0;
when 'AccessShareLock' then return 1;
when 'RowShareLock' then return 2;
when 'RowExclusiveLock' then return 3;
when 'ShareUpdateExclusiveLock' then return 4;
when 'ShareLock' then return 5;
when 'ShareRowExclusiveLock' then return 6;
when 'ExclusiveLock' then return 7;
when 'AccessExclusiveLock' then return 8;
else return 0;
end case;
end;
$$ language plpgsql strict;
digoal=# with t_wait as
(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,
a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start,
b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid and not a.granted),
t_run as
(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,
a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.query,b.xact_start,b.query_start,
b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted)
select r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,r.relation::regclass,r.pid r_pid,
r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,r.query_start r_query_start,
now()-r.query_start r_locktime,r.query r_query,w.mode w_mode,w.pid w_pid,w.page w_page,
w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start,
now()-w.query_start w_locktime,w.query w_query
from t_wait w,t_run r where
r.locktype is not distinct from w.locktype and
r.database is not distinct from w.database and
r.relation is not distinct from w.relation and
r.page is not distinct from w.page and
r.tuple is not distinct from w.tuple and
r.classid is not distinct from w.classid and
r.objid is not distinct from w.objid and
r.objsubid is not distinct from w.objsubid and
r.transactionid is not distinct from w.transactionid and
r.pid <> w.pid
order by f_lock_level(w.mode)+f_lock_level(r.mode) desc,r.xact_start;
可以看到B,C会话正在等待transactionid锁,是由于约束检测造成的。
-[ RECORD 1 ]-+--------------------------------
locktype | transactionid
r_mode | ExclusiveLock
r_user | digoal
r_db | digoal
relation |
r_pid | 24785
r_page |
r_tuple |
r_xact_start | 2015-06-04 12:27:53.475664+08
r_query_start | 2015-06-04 12:28:00.13671+08
r_locktime | 00:00:43.79604
r_query | insert into pk_test values (1);
w_mode | ShareLock
w_pid | 7536
w_page |
w_tuple |
w_xact_start | 2015-06-04 12:28:19.256706+08
w_query_start | 2015-06-04 12:28:21.89269+08
w_locktime | 00:00:22.04006
w_query | insert into pk_test values (1);
-[ RECORD 2 ]-+--------------------------------
locktype | transactionid
r_mode | ExclusiveLock
r_user | digoal
r_db | digoal
relation |
r_pid | 24785
r_page |
r_tuple |
r_xact_start | 2015-06-04 12:27:53.475664+08
r_query_start | 2015-06-04 12:28:00.13671+08
r_locktime | 00:00:43.79604
r_query | insert into pk_test values (1);
w_mode | ShareLock
w_pid | 7411
w_page |
w_tuple |
w_xact_start | 2015-06-04 12:28:10.211724+08
w_query_start | 2015-06-04 12:28:12.188666+08
w_locktime | 00:00:31.744084
w_query | insert into pk_test values (1);
结束会话a, B,C报错。
SESSION A:
digoal=> end;
COMMIT
Time: 0.235 ms
SESSION B:
ERROR: duplicate key value violates unique constraint "pk_test_pkey"
DETAIL: Key (id)=(1) already exists.
SESSION C:
ERROR: duplicate key value violates unique constraint "pk_test_pkey"
DETAIL: Key (id)=(1) already exists.
接下来要观察, session b,c到底有没有将记录插进去,从ctid可以看到B,C的两条未提交的垃圾记录已经插入了heap page。
所以需要垃圾回收。
digoal=> insert into pk_test values(2);
INSERT 0 1
digoal=> select ctid,* from pk_test ;
ctid | id
-------+----
(0,1) | 1
(0,4) | 2
(2 rows)
但是index page是没有被插入的,因为这个INDEX就是保证唯一性的,不可能在这里出现重复。
digoal=# create extension pageinspect;
CREATE EXTENSION
digoal=# select * from bt_page_items('digoal.pk_test_pkey',1);
itemoffset | ctid | itemlen | nulls | vars | data
------------+-------+---------+-------+------+-------------------------
1 | (0,1) | 16 | f | f | 01 00 00 00 00 00 00 00
2 | (0,4) | 16 | f | f | 02 00 00 00 00 00 00 00
(2 rows)
接下来我们看看check约束,会不会造成垃圾数据?
digoal=> create table ck_test(id int check (id>10));
CREATE TABLE
digoal=> insert into ck_test values (1);
ERROR: new row for relation "ck_test" violates check constraint "ck_test_id_check"
DETAIL: Failing row contains (1).
digoal=> insert into ck_test values (11);
INSERT 0 1
digoal=> select ctid,* from ck_test ;
ctid | id
-------+----
(0,1) | 11
(1 row)
digoal=> insert into ck_test values (1);
ERROR: new row for relation "ck_test" violates check constraint "ck_test_id_check"
DETAIL: Failing row contains (1).
digoal=> insert into ck_test values (1);
ERROR: new row for relation "ck_test" violates check constraint "ck_test_id_check"
DETAIL: Failing row contains (1).
digoal=> insert into ck_test values (11);
INSERT 0 1
digoal=> select ctid,* from ck_test ;
ctid | id
-------+----
(0,1) | 11
(0,2) | 11
(2 rows)
从ctid可以看到,check约束是在数据进入heap page前检查的,所以不会产生垃圾。
时间: 2024-10-25 01:08:02