《A Tour of PostgreSQL Internals》学习笔记——系统表和数据类型

时间:2022-06-29 19:00:18

上周末学习了《A Tour of PostgreSQL Internals》的第一部分(View 1),今天我们继续打开书本,继续View 2 部分。

View 2 Postgresql的系统表和数据类型

和其他大多数DBMS相比,postgresql在更大程度上使用了“ 数据驱动 ”。

为什么这么说呢?

按照Tom Lane的解释,原因如下:

  • postgresql使用一套系统表描述数据库表,表中的每一列,每个索引等等;
  • 除此以外,postgresql同样使用系统表存储数据类型、函数、操作符和索引的访问方法等等信息;
  • 通过增加新的数据字典,postgresql可以很方便的扩展。

我们知道,在关系数据库中,为了数据库系统的控制,必须提供数据字典的功能。数据字典不仅存储各种对象的描述,而且存储这些数据对象的细节信息。其中系统表是Postgresql数据库存放结构元数据的地方,表现为存放有系统信息的普通表和视图。

在postgresql每一个数据库中都有单独的一套系统表,这些表大多是在数据库创建时从template数据库拷贝过来的,描述该数据库的属性。

如果想看源码的话,可以看看src/include/catalog目录下的"pg_xxx.h"文件,这些文件相应地定义了"pg_xxx"系统表的数据结构。另外,indexing.h文件定义了所有的系统表索引,toasting.h定义了所有系统表的TOAST表。


2.1 基本系统表

pg_class表

pg_class存储表以及与表结构类似的数据库对象的信息,包括索引、序列、试图、复合数据类型、TOAST表等等。每一个对象在pg_class表中都表现为一个元组,并且分配一个OID作为唯一标识(该属性作为隐藏属性,不能直接访问)。

pg_attribute表

pg_attribute表存储数据库表中的每个属性的信息,对于表的每一个属性,都有一个对应的元组描述其属性,例如属性名、属性的数据类型、默认值、非NULL约束等等。

pg_index表

pg_index表存储索引的具体信息,把索引和索引对应的表关联起来。这里需要说明的是,由于索引也是表,所以索引也在pg_class表和pg_attribute表中有对应的元组。因此,pg_index表中的每个元组包含索引在pg_class表中对应的元组的OID和基础表(即索引建立在该表上)的OID,同时,也包含了计算索引所使用的表的列的信息以及索引的运算表达式。

以上是postgresql数据库中最重要的三张系统表,还有很多其他的表就不在此一一列出了,比如pg_tablespace、pg_database等等。这些表的信息可以通过select语句来获取。

2.2 与函数相关的系统表

pg_proc表

pg_proc表对函数进行了定义。每一个函数在该表中都对应一个元组,包含函数名。输入参数类型,返回类型以及对函数的定义(可能是文本,可能是一段编译型语句,也可能是对可执行代码的引用)。编译过的函数可以静态地链接到服务器上,或者在存储在共享库内,当第一次使用该库时动态的载入。这些编译过的函数一般都使用C编写,当然理论上你可以有别的选择。

pg_language表

pg_language表定义了函数实现所使用的语言。主要支持了C语言和SQL语句。一些可选的语言包括pl/pgsql、tcl和perl。这些语言是由动态链接的处理程序提供支持的,服务器内核并不关注,只负责调用。

以下是一些示例函数:

C:

int4
square_int4 (int4 x)
{
return x * x;
}

把上面的函数编译成共享库文件,这样声明:

CREATE FUNCTION square(int4) RETURNS int4
AS ’/path/to/square.so’, ’square_int4’
LANGUAGE ’C’;

PL/PGSQL:

CREATE FUNCTION square(int4) RETURNS int4 AS ’begin
return $1 * $1;
end;’ LANGUAGE ’plpgsql’;

2.3 与聚集函数相关的系统表

pg_aggregate表

pg_aggregate表定义了我们熟悉的这些聚集函数,比如:min()、max()、count()等。每个聚集函数涉及一个运行时的数据类型,一个更新函数,一个最终输出函数,共同完成了一个聚集函数的功能。

-- The working state is a 2-element integer array, sum and count.
-- We use split(n) as a quick-and-dirty way of parsing the input array
-- value, which comes in as a string like ’{1,2}’. There are better ways... create function tcl_int4_accum(int4[], int4) returns int4[] as ’
set state [split $1 "{,}"]
set newsum [expr {[lindex $state 1] + $2}]
set newcnt [expr {[lindex $state 2] + 1}]
return "{$newsum,$newcnt}"
’ language ’pltcl’;
create function tcl_int4_avg(int4[]) returns int4 as ’
set state [split $1 "{,}"]
if {[lindex $state 2] == 0} { return_null }
return [expr {[lindex $state 1] / [lindex $state 2]}]
’ language ’pltcl’;
create aggregate tcl_avg (
basetype = int4, -- input datatype
sfunc = tcl_int4_accum, -- update function name
stype = int4[], -- working-state datatype
finalfunc = tcl_int4_avg, -- final-output function name
initcond = ’{0,0}’ -- initial value of working state
);

2.4 与操作符相关的系统表

pg_operator表

很显然,该表定义了表达式中能使用的运算符。这种运算符更多的是一种“语法糖”,将只有一两个参数的函数变成操作符,简化了书写和操作。

例子如下:

CREATE FUNCTION mypower(float8, float8) RETURNS float8 AS ’begin
return exp(ln($1) * $2);
end;’ LANGUAGE ’plpgsql’;
CREATE OPERATOR ** (
procedure = mypower,
leftarg = float8,
rightarg = float8 );
SELECT 44 ** 2, 81 ** 0.5;
?column? | ?column?
----------+----------
1936 | 9
(1 row)

2.5 与数据类型相关的表

pg_type表

该表定义了表中属性存储、操作符和函数接收和返回所使用的基本数据类型。如果想增加自己需要的数据类型,可以通过在pg_type表中增加相应的元组实现。

例如你要一种能存储3维空间点的数据类型,而postgresql内置的类型只支持到2维的点,那么你可以自己定义一种3维点的数据类型。对于一个数据类型而言,你至少需要定义两个相关函数:

1.把外部输入文本转换为你的数据类型

2.把你的数据类型转换为外部文本

只有定义了这两个函数,你的数据类型才能够被正常的使用。

CREATE TYPE point3 (
input = point3in,
output = point3out,
internallength = 24, -- space for three float8’s
alignment = double ); -- ensure storage will be aligned properly

一般来说,你定义了一种新的数据类型之后,不仅仅只访问和存储它们,可能还需要其他一些有用的操作符和函数,对上面的例子来说,计算两个三维点之间的距离是很常见和必要的操作。

同时,如果你想让你的数据类型可以被索引,你还需要在pg_amop表和pg_amproc中增加相应的元组,以此告诉索引程序一系列复杂的操作符和函数操作你的数据类型后的行为结果,比如< <= = > >= 这样的操作符。同样,你需要在pg_am表中增加元组来支持对索引的访问。


小结

优点:

  • 优点显而易见,你可以很方便的扩展你需要的函数,数据类型,尤其对于你的某些特别的业务需求的时候,这是很方便和有利的。

缺点:

  • 正如你所看到的,增加一个数据类型,你需要做很多工作,在很多系统表中插入元祖,编写函数和数据类型定义等等,这需要一定的业务水平;
  • 对于函数而言,你需要了解数据的类型、访问方式以及优化方法,牵扯到系统的方方面面,你很容易将本来不相关的模块关联了起来,影响了系统的稳定性。

好的《A Tour of PostgreSQL Internals》的View 2 部分就是这样,还剩View 3部分,加油,争取这周搞定!也谢谢大家支持,欢迎讨论,有哪里理解不对的地方还请大家批评指正~