pg_buffercache

pg_buffercache模块提供了一种方法实时检查共享缓冲区。

该模块提供了一个 C 函数pg_buffercache_pages,它返回一个记录的集合,外加一个包装了该函数以便于使用的视图pg_buffercache

默认情况下,使用仅限于超级用户和pg_read_all_stats 角色的成员。可以使用GRANT给其他人授予访问权限。

pg_buffercache视图

视图显示的列的定义如表 F.15所示。

表 F.15. pg_buffercache 列

名称 类型 引用 描述
bufferid integer ID,在范围 1..shared_buffers
relfilenode oid pg_class.relfilenode 关系的文件结点编号
reltablespace oid pg_tablespace.oid 关系的表空间 OID
reldatabase oid pg_database.oid 关系的数据库 OID
relforknumber smallint 关系内的分叉数,见include/common/relpath.h
relblocknumber bigint 关系内的页面数
isdirty boolean 页面是否为脏?
usagecount smallint Clock-sweep 访问计数
pinning_backends integer 对这个缓冲区加 pin 的后端数量

共享缓存中的每一个缓冲区都有一行。没有使用的缓冲区的行中只有bufferid为非空。共享的系统目录被显示为属于数据库零。

因为缓冲是所有数据库共享的,通常会有不属于当前数据库的关系的页面。这意味着对于一些行在pg_class中可能不会有匹配的连接行,或者甚至有错误的连接。如果你试图与pg_class连接,将连接限制于reldatabase等于当前数据库 OID 或零的行是一个好主意。

当访问pg_buffercache视图时,内部缓冲区管理器会被锁住足够长时间来拷贝视图将显示的所有缓冲区状态数据。这确保了该视图会产生一个一致的结果集合,而不会不必要地长时间阻塞普通的缓冲区活动。尽管如此,如果经常读取这个视图还是会对数据库性能产生一些影响。

样例输出

regression=# SELECT c.relname, count(*) AS buffers
             FROM pg_buffercache b INNER JOIN pg_class c
             ON b.relfilenode = pg_relation_filenode(c.oid) AND
                b.reldatabase IN (0, (SELECT oid FROM pg_database
                                      WHERE datname = current_database()))
             GROUP BY c.relname
             ORDER BY 2 DESC
             LIMIT 10;
             relname             | buffers
---------------------------------+---------
 tenk2                           |     345
 tenk1                           |     141
 pg_proc                         |      46
 pg_class                        |      45
 pg_attribute                    |      43
 pg_class_relname_nsp_index      |      30
 pg_proc_proname_args_nsp_index  |      28
 pg_attribute_relid_attnam_index |      26
 pg_depend                       |      22
 pg_depend_reference_index       |      20
(10 rows)

pgcrypto模块为UDB-TX提供了密码函数。

普通哈希函数

digest()
digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea

计算一个给定*data的一个二进制哈希值。type*是要使用的算法。标准算法是md5sha1sha224sha256sha384sha512。如果使用 OpenSSL 编译了pgcrypto,如表 F.19中所述,有更多算法可用。

如果你想摘要成为一个十六进制字符串,可以在结果上使用encode()。例如:

CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
    SELECT encode(digest($1, 'sha1'), 'hex')
$$ LANGUAGE SQL STRICT IMMUTABLE;
hmac()
hmac(data text, key text, type text) returns bytea
hmac(data bytea, key bytea, type text) returns bytea

为带有密钥*keydata计算哈希过的 MAC。type*与digest()中相同。

这与digest()相似,但是该哈希只能在知晓密钥的情况下被重新计算出来。这阻止了某人修改数据且还想更改哈希以匹配之的企图。

如果该密钥大于哈希块的尺寸,它将先被哈希然后把结果用作密钥。

口令哈希函数

函数crypt()gen_salt()是特别设计用来做口令哈希的。crypt()完成哈希,而gen_salt()负责为前者准备算法参数。

crypt()中的算法在以下方面不同于通常的 MD5 或 SHA1 哈希算法:

  1. 它们很慢。由于数据量很小,这是增加蛮力口令破解难度的唯一方法。

  2. 它们使用一个随机值(称为salt),这样具有相同口令的用户将得到不同的密文口令。这也是针对逆转算法的一种额外保护。

  3. 它们会在结果中包括算法类型,这样用不同算法哈希的口令能共存。

  4. 其中一些是自适应的 — 这意味着当计算机变得更快时,你可以调整该算法变得更慢,而不会产生与现有口令的不兼容。

表 F.16列出了crypt()函数所支持的算法。

表 F.16. crypt()支持的算法

算法 最大口令长度 自适应? Salt 位数 输出长度 描述
bf 72 yes 128 60 基于 Blowfish,变体 2a
md5 unlimited no 48 34 基于 MD5 的加密
xdes 8 yes 24 20 扩展的 DES
des 8 no 12 13 原生 UNIX 加密

crypt()

crypt(password text, salt text) 返回 text

计算*password的一个 crypt(3) 风格的哈希。在存储一个新口令时,你需要使用gen_salt()产生一个新的salt值。要检查一个口令,把存储的哈希值作为salt*,并且测试结果是否匹配存储的值。

设置一个新口令的例子:

UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));

认证的例子:

SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ;

如果输入的口令正确,这会返回true

gen_salt()

gen_salt(type text [, iter_count integer ]) 返回 text

产生一个在crypt()中使用的新随机 salt 字符串。该 salt 字符串也告诉了crypt()要使用哪种算法。

*type*参数指定哈希算法。可接受的类型是:desxdesmd5以及bf

*iter_count参数让用户可以为使用迭代计数的算法指定迭代计数。计数越高,哈希口令花的时间更长并且因而需要更多时间去攻破它。不过使用太高的计数会导致计算一个哈希的时间高达数年 — 这并不使用。如果iter_count参数被忽略,将使用默认的迭代计数。允许的iter_count*值与算法相关,如表 F.17所示。

表 F.17. crypt()的迭代计数

算法 默认值 最小值 最大值
xdes 725 1 16777215
bf 6 4 31

xdes算法还有额外的限制:迭代计数必须是一个奇数。

要选取一个合适的迭代计数,考虑最初的 DES 加密被设计成在当时的硬件上每秒钟完成 4 次哈希。低于每秒 4 次哈希的速度很可能会损害可用性。而超过每秒 100 次哈希又可能太快了。

表 F.18给出了不同哈希算法的相对慢度的综述。该表展示了在假设口令只含有小写字母或者大小写字母及数字的情况下,在一个 8 字符口令中尝试所有字符组合所需要的时间。在crypt-bf项中,在一个斜线之后的数字是gen_salt的*iter_count*参数

表 F.18. 哈希算法速度

算法 次哈希/秒 对于[a-z] 对于[A-Za-z0-9] 相对于md5 hash的持续时间
crypt-bf/8 1792 4 年 3927 年 100k
crypt-bf/7 3648 2 年 1929 年 50k
crypt-bf/6 7168 1 年 982 年 25k
crypt-bf/5 13504 188 天 521 年 12.5k
crypt-md5 171584 15 天 41 年 1k
crypt-des 23221568 157.5 分 108 天 7
sha1 37774272 90 分 68 天 4
md5(hash) 150085504 22.5 分 17 天 1

注意:

  • 使用的机器是一台 Intel Mobile Core i3。

  • crypt-descrypt-md5算法的数字是取自 John the Ripper v1.6.38 -test输出。

  • md5 hash的数字来自于 mdcrack 1.2。

  • sha1的数字来自于 lcrack-20031130-beta.

  • crypt-bf的数字是采用一个在 1000 个 8 字符口令上循环的简单程序采集到的。用那种方法我能展示不同迭代次数的速度。供参考:john-test对于crypt-bf/5显示 13506 次循环/秒(结果中的微小差异符合pgcrypto中的crypt-bf实现与 John the Ripper 中的一致这一情况)。

注意“尝试所有组合”并非是现实中会采用的方式。通常口令破解都是在词典的帮助下完成的,词典中会包含常用词以及它们的多种变化。因此,甚至有些像词的口令被破解的时间可能会大大小于上面建议的数字,而一个 6 字符的不像词的口令可能会逃过破解,也可能不能逃脱。