PHP实战中知识总结 / PgSQL - 索引之ctid解析

一、ctid

ctid表示数据行在它所处的表内的物理位置,ctid字段的类型是tid。

虽然ctid可以快速定位到数据行,但是在每次执行vaccum full命令之后,数据行在块内的物理位置就会发生变化,所以ctid并不能作为长期的数据行标识符,应该使用主键来标识一个逻辑行

// 查看ctid
// 格式(blockid,itemid):如(0,1);0表示块id;1表示在这块第一条记录
postgres=# select ctid,id from test limit 10;
 ctid | id
--------+----
(0,1) | 1
(0,2) | 2
(0,3) | 3
(0,4) | 4
(0,5) | 5
(0,6) | 6
(0,7) | 7
(0,8) | 8
(0,9) | 9
(0,10) | 10
(10 rows)

二、ctid 的作用:

1、用于去重,相当于oracle中的伪列 rowid

postgres=# select ctid,* from test;
ctid | id | name | remark
-------+----+-------+-------
(0,1) | 2 | xx  | ss
(0,4) | 3 | dd  | ss
(0,5) | 4 | xs  | aa
(3 rows)
postgres=# delete from test where ctid not in (select min(ctid) from test group by remark);
DELETE 1
postgres=# select ctid,* from test;
ctid | id | name | remark
-------+----+-------+-------
(0,1) | 2 | xx  | ss
(0,5) | 4 | xs  | aa
(2 rows)
// 删除成功后,重新插入一条数据
postgres=# insert into test values(3,'aa','asd');
INSERT 0 1
postgres=# select ctid,* from test;
ctid | id | name | remark
-------+----+-------+-------
(0,1) | 2 | xx  | ss
(0,5) | 4 | xs  | aa
(0,6) | 3 | aa  | asd
(3 rows)

可以看到,在删除ctid为(0,4)后重新插入一条数据,新数据的ctid为(0,6),而不是刚刚删除的(0,4)。这是因为pgsql的特性:删除元祖占据的存储空间并没有被回收,直到执行vaccum命令之后(postgresql里面有AUTOVACUUM进程;也可以手动回收)

2、使用系统表pg_class,查看表占用的总块数,其中relpages,reltuples分别代表块数,记录数!

postgres=# select relpages,reltuples from pg_class where relname = 'test';
relpages | reltuples
----------+-----------
   637 |  100000
(1 row)

PHP实战中知识总结