安全、加密与访问控制
安全、加密与访问控制
本文件包含模块:passwordcheck(密码强度)、pg_buffercache(缓冲区共享缓存)、pgcrypto(加密函数)、pg_freespacemap(空闲空间映射)、pg_prewarm(预热)、pgrowlocks(行锁信息)。
passwordcheck
只要通过CREATE ROLE或ALTER ROLE设置用户, pass word check模块会检查用户的口令。如果一个口令被认为太弱,它将被拒绝并且该命令将带着一个错误终止。
要启用这个模块,把'$libdir/pass word check'加入到postgresql.conf中
的shared_preload_libraries,然后重启服务器。
你可以通过修改源代码来按你的需要修改这个模块。例如,你可以使用CrackLib2来检查口令 --- 这只需要在Makefile中取消两行的注释并且重新编译该模块(由于授权原因,我们不能默认包括CrackLib)。如果没有CrackLib,该模块会对口令强度强制一些简单的规则,可以自行修改和扩充。
小心:要阻止未加密的口令被通过网络传送、写入到服务器日志或者被一个数据库管理员窃取,Halo允许用户提供预加密的口令。很多客户端程序利用这种功能并且在把口令发送给服务器之前加密它。这限制了pass word check模块的有用性,因为这种情况下它只能尝试猜测口令。由于这个原因,如果你的安全性需求很高,我们不推荐pass word check。此外,你可以修改pass word check来拒绝预加密的口令,但是强制用户将口令设置为明文带来了它的安全风险。
pg_buffercache
pg_buffercache模块提供了一种方法实时检查共享缓冲区。
该模块提供了一个 C 函数pg_buffercache_pages,它返回一个记录的集合,外加一个包装了该函数以便于使用的视图pg_buffercache。
默认情况下,使用仅限于超级用户和pg_monitor 角色的成员。可以使用GRANT给其他人授予访问权限。
pg_buffercache视图
视图显示的列的定义如表 D.15所示。
表 D.15. pg_buffercache 列
列类型/描述:
bufferid integer:ID,在范围 1..shared_buffers中relfilenode oid (references pg_class.relfilenode):关系的文件结点编号reltablespace oid (references pg_tablespace.oid):关系的表空间 OIDreldatabase oid (references pg_database.oid):关系的数据库 OIDrelforknumber smallint:关系内的分叉数relblocknumber bigint:关系内的页面数isdirty boolean:页面是否为正确usagecount smallint:Clock-sweep 访问计数pinning_backends integer:对这个缓冲区加 pin 的后端数量
共享缓存中的每一个缓冲区都有一行。没有使用的缓冲区的行中只有bufferid为非空。共享的系统目录被显示数据库为零。
因为缓冲是所有数据库共享的,通常会有不属于当前数据库的关系的页面。这意味着对于一些行在pg_class中可能不会有匹配的连接行,或者甚至有错误的连接。如果你试图与pg_class连接,将连接限制于reldatabase等于当前数据库 OID 或零的行是一个好主意。
由于缓冲区管理器锁不会用于复制视图将显示的缓冲区状态数据,因此访问pg_buffercache视图对正常缓冲区活动的影响较小,但它不会在所有缓冲区中提供一致的结果集。但是,我们确保每个缓冲区的信息是准确的。
样例输出
test=# SELECT n.nspname, c.relname, count(*) AS buffers
test-# FROM pg_buffercache b JOIN pg_class c
test-# ON b.relfilenode = pg_relation_filenode(c.oid) AND
test-# b.reldatabase IN (0, (SELECT oid FROM pg_database
test(# WHERE datname = current_database()))
test-# JOIN pg_namespace n ON n.oid = c.relnamespace
test-# GROUP BY n.nspname, c.relname
test-# ORDER BY 3 DESC
test-# LIMIT 10;
nspname | relname | buffers
------------+--------------------------------+---------
test | bloomidx | 11778
test | tbloom | 530
pg_catalog | pg_proc | 198
pg_catalog | pg_depend | 136
pg_catalog | pg_depend_reference_index | 88
pg_catalog | pg_attribute | 75
pg_toast | pg_toast_2618 | 73
pg_catalog | pg_depend_depender_index | 71
pg_catalog | pg_proc_proname_args_nsp_index | 51
pg_catalog | pg_collation | 49
(10 rows)
pgcrypto
pgcrypto模块为Halo提供了密码函数。
该模块被认为是"trusted",也就是说,它可以由对当前数据库具有CREATE权限的非超级用户安装。
普通hash函数
D.25.1.1. digest()
digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea
计算一个给定data的一个二进制hash值。type是要使用的算法。标准算法是md5、sha1、sha224、sha256、sha384和sha512。如果使用 OpenSSL 编译了pgcrypto,如表 D.19中所述,有更多算法可用。
如果你想摘要成为一个十六进制字符串,可以在结果上使用encode()。例如:
test=# CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS \$\$
test\$# SELECT encode(digest(\$1, 'sha1'), 'hex')
test\$# \$\$ LANGUAGE SQL STRICT IMMUTABLE;
CREATE FUNCTION
**D.25.1.2. hmac()**
hmac(data text, key text, type text) returns bytea
hmac(data bytea, key bytea, type text) returns bytea
为带有密钥key的data计算hash过的 MAC。type与digest()中相同。
这与digest()相似,但是该哈希只能在知晓密钥的情况下被重新计算出来。这阻止了某人修改数据且还想更改哈希以匹配之的企图。
如果该密钥大于哈希块的尺寸,它将先被哈希然后把结果用作密钥。
口令哈希函数
函数crypt()和gen_salt()是特别设计用来做口令。crypt()完成,
而gen_salt()负责为前者准备算法参数。
crypt()中的算法在以下方面不同于通常的 MD5 或 SHA1 哈希算法:
1. 它们很慢。由于数据量很小,这是增加蛮力口令破解难度的唯一方法。
2. 它们使用一个随机值(称为salt),这样具有相同口令的用户将得到不同的密文口令。
这也是针对逆转算法的一种额外保护。
3. 它们会在结果中包括算法类型,这样用不同算法哈希的口令能共存。
4. 其中一些是自适应的 --- 这意味着当计算机变得更快时,你可以调整该算法变得更慢,而不会产生与现有口令的不兼容。
表 D.16列出了crypt()函数所支持的算法。
表 D.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 加密
---------- -------------- ---------- ----------- ---------- -----------------------
D.25.2.1. 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。
D.25.2.2. gen_salt()
gen_salt(type text [, iter_count integer ]) 返回 text
产生一个在crypt()中使用的新随机 salt 字符串。该 salt 字符串也告诉了crypt()要使用哪种算法。
type参数指定哈希算法。可接受的类型是:des、xdes、md5以及bf。
iter_count参数让用户可以为使用迭代计数的算法指定迭代计数。计数越高,哈希口令花的时间更长并且因而需要更多时间去攻破它。不过使用太高的计数会导致计算一个哈希的时间高达数年 --- 这并不使用。如果iter_count参数被忽略,将使用默认的迭代计数。允许的iter_count值与算法相关,如表 D.17所示。
表 D.17. crypt()的迭代计数
---------------- ---------------- ---------------- -----------------
算法 默认值 最小值 最大值
xdes 725 1 16777215
bf 6 4 31
---------------- ---------------- ---------------- -----------------
对xdes算法还有额外的限制:迭代计数必须是一个奇数。
要选取一个合适的迭代计数,考虑最初的 DES 加密被设计成在当时的硬件上每秒钟完成 4次哈希。低于每秒 4 次哈希的速度很可能会损害可用性。而超过每秒 100 次哈希又可能太快了。
表 D.18给出了不同哈希算法的相对慢度的综述。该表展示了在假设口令只含有小写字母或者大小写字母及数字的情况下,在一个 8 字符口令中尝试所有字符组合所需要的时间。
在crypt-bf项中,在一个斜线之后的数字是gen_salt的iter_count参数
表 D.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-des和crypt-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 字符的不像词的口令可能会逃过破解,也可能不能逃脱。
PGP 加密函数
这里的函数实现了OpenPGP (RFC 4880) 标准的加密部分。对称密钥和公钥加密都被支持。
一个加密的 PGP 消息由两个部分或者包组成:
• 包含一个会话密钥的包 --- 加密过的对称密钥或者公钥。
• 包含用会话密钥加密过的数据的包。
当用一个对称密钥(即一个口令)加密时:
1. 给定的口令被使用一个 String2Key (S2K) 算法哈希。这更像crypt()算法 --- 有目的地慢并且使用随机salt --- 但是它会产生一个全长度的二进制密钥。
2. 如果要求一个独立的会话密钥,将会生成一个新的随机密钥。否则该 S2K 密钥将被直接用作会话密钥。
3. 如果直接使用 S2K 密钥,那么只有 S2K 设置将被放入会话密钥包中。否则会话密钥会用S2K 密钥加密并且放入会话密钥包中。
当使用一个公共密钥加密时:
1. 一个新的随机会话密钥会被生成。
2. 它被用公共密钥加密并且放入到会话密钥包中。
在两种情况下,要被加密的数据按下列步骤被处理:
1. 可选的数据操纵:压缩、转换成 UTF-8 或者行末转换。
2. 数据会被加上一个随机字节的块作为前缀。这等效于使用一个随机 IV。
3. 追加一个随机前缀和数据的 SHA1 哈希。
4. 所有这些都用会话密钥加密并且放在数据包中。
D.25.3.1. pgp_sym_encrypt()
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
使用一个对称PGP密钥 psw加密data。options参数可以包含下文所述的选项设置。
D.25.3.2. pgp_sym_decrypt()
pgp_sym_decrypt(msg bytea, psw text [, options text ]) 返回 text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea解密一个用对称密钥加密过的 PGP 消息。
不允许使用pgp_sym_decrypt解密bytea数据。这是为了避免输出非法的字符数据。使用pgp_sym_decrypt_bytea解密原始文本数据是好的。
options参数可以包含下文所述的选项设置。
D.25.3.3. pgp_pub_encrypt()
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
用一个公共 PGP 密钥 key加密data。给这个函数一个私钥会产生一个错误。options参数可以包含下文所述的选项设置。
D.25.3.4. pgp_pub_decrypt()
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) 返回 text
pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]])
returns bytea
解密一个公共密钥加密的消息。key必须是对应于用来加密的公钥的私钥。如果私钥是用口令保护的,你必须在psw中给出该口令。如果没有口令,但你想要指定选项,你需要给出一个空口令。
不允许使用pgp_pub_decrypt解密bytea数据。这是为了避免输出非法的字符数据。使用pgp_pub_decrypt_bytea解密原始文本数据是好的。
options参数可以包含下文所述的选项设置。
D.25.3.5. pgp_key_id()
pgp_key_id(bytea) 返回 text
pgp_key_id抽取一个 PGP 公钥或私钥的密钥 ID。或者如果给定了一个加密过的消息,它给出一个用来加密数据的密钥 ID。
它能够返回 2 个特殊密钥 ID:
• SYMKEY
该消息是用一个对称密钥加密的。
• ANYKEY
该消息是用公钥加密的,但是密钥 ID 已经被移除。这意味着你将需要尝试你所有的密钥来看看哪个能解密该消息。pgcrypto本身不产生这样的消息。
注意:不同的密钥可能具有相同的 ID。这很少见但是是一种正常事件。客户端应用则应该尝试用每一个去解密,看看哪个合适 --- 像处理ANYKEY一样。
D.25.3.6. armor(), dearmor()
armor(data bytea [ , keys text[], values text[] ]) 返回 text
dearmor(data text) returns bytea
这些函数把二进制数据包装/解包成 PGP ASCII-armored 格式,其基本上是带有 CRC 和额外格式化的 Base64。
如果指定了keys和values数组,每一个键/值对的 armored 格式上会增加一个armor header。两个数组都必须是单一维度的,它们的长度必须相同。键和值不能包含任何非 ASCII 字符。
D.25.3.7. pgp_armor_headers
pgp_armor_headers(data text, key out text, value out text) returns setof record
pgp_armor_headers()从data中抽取 armor header。返回值是一个有两列的行集合,包括键和值。如果键或值包含任何非-ASCII 字符,它们会被视作 UTF-8。
D.25.3.8. PGP 函数的选项
选项被命名为与 GnuPG 类似的形式。一个选项的值应该在一个等号后给出,各个选项之间用逗号分隔。例如:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
除了convert-crlf之外所有这些选项只适用于加密函数。解密函数会从 PGP 数据中得到这些参数。
最有趣的选项可能是compress-algo和unicode-mode。其余的应该可以使用合理的默认值。
D.25.3.8.1. cipher-algo
要用哪个密码算法。
值:bf, aes128, aes192, aes256 (只用于 OpenSSL:3des, cast5)
默认:aes128
适用于:pgp_sym_encrypt, pgp_pub_encrypt
D.25.3.8.2. compress-algo
要使用哪种压缩算法。只有Halo编译时使用了 zlib 时才可用。
值:
0 - 不压缩
1 - ZIP 压缩
2 - ZLIB 压缩 (= ZIP 外加元数据和块CRC)
默认:0
适用于:pgp_sym_encrypt, pgp_pub_encrypt
D.25.3.8.3. compress-level
压缩多少。级别越高压缩得越小但是速度也越慢。
0 表示禁用压缩。
值:0, 1-9
默认:6
适用于:pgp_sym_encrypt, pgp_pub_encrypt
D.25.3.8.4. convert-crlf
加密时是否把\n转换成\r\n以及解密时是否把\r\n转换成\n。
RFC 4880 指定文本数据存储时应该使用\r\n换行。
使用这个选项能够得到完全 RFC 兼容的行为。
值:0, 1
默认:0
适用于:pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt
D.25.3.8.5. disable-mdc
不用 SHA-1 保护数据。使用这个选项的唯一好的理由是实现与古董级别 PGP 产品的兼容,这些产品在受 SHA-1 保护的包被加入到 RFC 4880 之前就已经存在了。最近的 gnupg.org和 pgp.com 软件能很好地支持它。
值:0,1
默认:0
适用于:pgp_sym_encrypt, pgp_pub_encrypt
D.25.3.8.6. sess-key
使用单独的会话密钥。公钥加密总是使用一个单独的会话密钥。这个选项是用于对称密钥加密的,对称密钥加密默认直接使用 S2K 密钥。
值:0,1
默认:0
适用于:pgp_sym_encrypt
D.25.3.8.7. s2k-mode
要使用哪一种 S2K 算法。
值:
0 - 不用 salt。危险!
1 - 用 salt 但是使用固定的迭代计数。
3 - 可变的迭代计数。
默认:3
适用于:pgp_sym_encrypt
D.25.3.8.8. s2k-count
S2K 算法要使用的迭代次数。它必须是一个位于 1024 和 65011712 之间的值,首尾两个值包括在内。
默认:65536 和 253952 之间的一个随机值
适用于:pgp_sym_encrypt,只能用于 s2k-mode=3
D.25.3.8.9. s2k-digest-algo
要在 S2K 计算中使用哪种摘要算法。
值:md5, sha1
默认:sha1
适用于:pgp_sym_encrypt
D.25.3.8.10. s2k-cipher-algo
要用哪种密码来加密独立的会话密钥。
值:bf, aes, aes128, aes192, aes256
默认:use cipher-algo
适用于:pgp_sym_encrypt
D.25.3.8.11. unicode-mode
是否把文本数据在数据库内部编码和 UTF-8 之间来回转换。如果你的数据库已经是 UTF-8,将不会转换,但是消息将被标记为 UTF-8。没有这个选项它将不会被标记。
值:0,1
默认:0
适用于:pgp_sym_encrypt, pgp_pub_encrypt
D.25.3.9. 用 GnuPG 生成 PGP 密钥
要生成一个新密钥:
gpg --gen-key
更好的密钥类型是"DSA 和 Elgamal"。
对于 RSA 密钥,你必须创建仅用于签名的 DSA 或 RSA 密钥作为主控密钥,然后用gpg --edit-key增加一个 RSA 加密子密钥。
要列举密钥:
gpg --list-secret-keys
要以 ASCII-保护格式导出一个公钥:
gpg -a --export KEYID > public.key
要以 ASCII-保护格式导出一个私钥:
gpg -a --export-secret-keys KEYID > secret.key
在把这些密钥交给 PGP 函数之前,你需要对它们使用dearmor()。或者如果你能处理二进制数据,你可以从命令中去掉-a。
D.25.3.10. PGP 代码的限制
• 不支持签名。这也意味着它不检查加密子密钥是否属于主控密钥。
• 不支持加密密钥作为主控密钥。由于通常并不鼓励那种用法,这应该不是问题。
• 不支持多个子密钥。这可能看起来像一个问题,因为在实践中普遍需要多个子密钥。在另一方面,你不能把你的常规 GPG/PGP 密钥用于pgcrypto,而是创建一些新的密钥,因为使用场景相当不同。
原始的加密函数
这些函数只在数据上运行一次加密,它们不具有 PGP 加密的任何先进特性。因此它们有一些主要的问题:
1. 它们直接把用户密钥用作加密密钥。
2. 它们不提供任何完整性检查来查看被加密数据是否被修改。
3. 它们希望用户自己管理所有加密参数,甚至是 IV。
4. 它们无法处理文本。
因此,在介绍了 PGP 加密后,我们不鼓励使用原始的加密函数。
encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea
encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
使用type指定的密码方法加密/解密数据。type字符串的语法是:
algorithm [ - mode ] [ /pad: padding ]
其中algorithm是下列之一:
• bf --- Blowfish
• aes --- AES (Rijndael-128, -192 或 -256)
并且mode是下列之一:
• cbc --- 下一个块依赖前一个(默认)
• ecb --- 每一个块被独立加密(只用于测试)
并且padding是下列之一:
• pkcs --- 数据可以是任意长度(默认)
• none --- 数据必须是密码块尺寸的倍数
因此,例如这些是等效的:
encrypt(data, 'fooz', 'bf')
encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
在encrypt_iv和decrypt_iv中,iv参数是 CBC 模式的初始值,ECB 会忽略它。如果不是准确的块尺寸,它会被修剪或用零填充。在没有这个参数的函数中,它的值都被默认为零。
随机数据函数
gen_random_bytes(count integer) returns bytea
返回count个密码上强壮的随机字节。一次最多可以抽取 1024 个字节。这是为了避免耗尽随机数发生池。
gen_random_uuid() 返回 uuid
返回一个版本 4 的(随机的)UUID。
注解
D.25.6.1. 配置
pgcrypto会根据查找主Halo configure脚本配置它自身。影响它的选项是--withzlib以及--with-openssl。
在编译了 zlib 时,PGP 加密函数能够在加密前压缩数据。
在编译了 OpenSSL 时,会有更多可用算法。公钥加密函数也会更快,因为 OpenSSL 有优化得更好的 BIGNUM 函数。
表 D.19. 使用和不用 OpenSSL 的功能总结
----------------------- --------------------- ----------------------
功能 内建 使用 OpenSSL
MD5 yes yes
SHA1 yes yes
SHA224/256/384/512 yes yes
其他摘要算法 no yes(注意 1)
Blowfish yes yes
AES yes yes
DES/3DES/CAST5 no yes
原始加密 yes yes
PGP yes yes
PGP yes yes
----------------------- --------------------- ----------------------
注意:
- OpenSSL 支持的任何摘要算法都是自动选取的。这对于使用密码来说是不可能的,因为需要被显式地支持。
D.25.6.2. NULL 处理
只要任何参数是 NULL,所有的函数都会返回 NULL。在不当使用时这可能会导致安全风险。
D.25.6.3. 安全性限制
所有pgcrypto函数都在数据库服务器内部运行。这意味着在pgcrypto和客户端应用之间移动的所有数据和口令都是明文。因此,你必须:
1. 本地连接或者使用 SSL 连接。
2. 信任系统管理员和数据库管理员。
如果你不能这样做,那么最好在客户端应用中进行加密。
该实现无法抵抗侧信道攻击4。例如,一个pgcrypto解密函数完成所需的时间是随着密文尺寸变化的。
pg_freespacemap
pg_freespacemap模块提供了一种方法来检查空闲空间映射(FSM)。它提供了一个称为pg_freespace的函数,准确地说是两个重载的函数。这些函数显示空闲空间映射为一个给定页面所记录的值,或者显示关系中所有页面的记录值。
默认情况下,使用仅限于超级用户和pg_stat_scan_tables 角色的成员。可以使用GRANT给其他人授予访问权限。
函数
pg_freespace(rel regclass IN, blkno bigint IN) 返回 int2
根据 FSM,返回由blkno指定的关系页面上的空闲空间总量。
pg_freespace(rel regclass IN, blkno OUT bigint, avail OUT int2)
根据 FSM,显示关系的每个页面上的空闲空间总量。一个(blkno bigint, avail int2)元组的集合会被返回,每一个元组对应关系中的一个页面。
存储在空闲空间映射中的值不准确。它们被圆整到BLCKSZ的 1/256(对于默认的BLCKSZ是 32字节),并且在元组被插入和更新时它们不会被实时更新。
对于索引,被跟踪的是整个没有使用的页面,而不是页面中的空闲空间。因此,这些值可能没有意义,只是表示一个页面是满的还是空的。
样例输出
test=# SELECT * FROM pg_freespace('foo');
blkno | avail
-------+-------
0 | 0
1 | 0
2 | 0
3 | 32
4 | 704
5 | 704
6 | 704
7 | 1216
8 | 704
9 | 704
10 | 704
11 | 704
12 | 704
13 | 704
14 | 704
15 | 704
16 | 704
17 | 704
18 | 704
19 | 3648
(20 rows)
text=# SELECT * FROM pg_freespace('foo', 7);
pg_freespace
--------------
1216
(1 row)
pg_prewarm
pg_prewarm模块提供一种方便的方法把关系数据载入到操作系统缓冲区或者 Halo缓冲区。可以使用pg_prewarm函数手工执行预热,或者通过在shared_preload_libraries中包括pg_prewarm来自动执行预热。在后一种情况中,系统将运行一个后台工作者,它会周期性地把共享内存中的内容记录在一个名为autoprewarm.blocks的文件中,并且在重新启动后用两个后台工作者重新载入那些块。
函数
pg_prewarm(regclass, mode text default 'buffer', fork text default 'main',
first_block int8 default null,
last_block int8 default null) RETURNS int8
第一个参数是要预热的关系。
第二个参数是要使用的预热方法,下文将会进一步讨论。
第三个参数是要被预热的关系分叉,通常是main。
第四个参数是要预热的第一个块号(NULL也被接受,它等同于零)。
第五个参数是要预热的最后一个块号(NULL表示一直预热到关系的最后一个块)。返回值是被预热的块数。
有三种可用的预热方法。prefetch会向操作系统发出异步预取请求(如果支持异步预取),不支持异步预取则抛出一个错误。 read会读取要求范围的块。与prefetch 不同,它是同步的并且在所有平台上都被支持,但是可能较慢。 buffer会把要求范围的块读入道数据库的缓冲区。
注意:使用任意一种方法尝试预热比能缓存的数量更多的块 --- 使用 prefetch或者read(由OS)或者使用 buffer(由Halo ) --- 将很可能导致高编号块被读入时把低编号的块从缓冲区中逐出的情况。被预热的数据也不享受对缓冲区替换的特别保护,因此其他系统活动可能会在刚刚被预热的块被读入后很快就将它们逐出。反过来,预热也可能把其他数据逐出缓存。 由于这些原因,预热通常在启动时最有用,那时缓冲大部分都为空。
autoprewarm_start_worker() RETURNS void
启动主要的autoprewarm工作者。这通常将会自动发生,但是如果没有在服务器启动时配置自动预热,用户希望在稍晚的时候启动该工作者,这个函数就能发挥作用。
autoprewarm_dump_now() RETURNS int8
立即更新autoprewarm.blocks。如果autoprewarm工作者没有运行,但用户希望它在下一次重启后运行,则这个函数会很有用。返回值是写入到autoprewarm.blocks中的记录数。
配置参数
pg_prewarm.autoprewarm (boolean)
控制服务器是否应该运行autoprewarm工作者。
默认这个参数为on。这个参数只能在服务器启动时设置。
pg_prewarm.autoprewarm_interval (int)
这是更新autoprewarm.blocks的间隔。
默认是300秒。如果被设置为0,该文件将不会以常规的间隔方式转储,而是只在服务器关闭时转储。
pgrowlocks
pgrowlocks模块提供了一个函数来显示一个指定表的行锁定信息。
默认情况下,使用仅限于超级用户、pg_stat_scan_tables 角色的成员和在该表上拥有SELECT权限的用户。
F.28.1. 概述
pgrowlocks(text) 返回 setof record
参数是一个表的名称。结果是一个记录集合,其中每一行对应表中一个被锁定的行。输出列如表 D.20所示。
表 D.20. pgrowlocks 输出列
---------------------- ---------------------- ------------------------------------------------------------------------------------------------------------------------------------
名称 类型 描述
locked_row tid 被锁定行的元组 ID(TID)
locker xid 持锁者的事务 ID,如果是多事务则为多事务 ID
multi boolean 如果持锁者是一个多事务,则为真
xids xid\[\] 持锁者的事务 ID(如果是多事务则多于一个)
modes text\[\] 持锁者的锁模式(如果是多事务则多于一个),是一个Key Share、Share、For No Key Update、No Key Update、For Update、Update组成的数组。
pids integer\[\] 锁定后端的进程 ID(如果是多事务则多于一个)
---------------------- ---------------------- ------------------------------------------------------------------------------------------------------------------------------------
pgrowlocks会为目标表加AccessShareLock并且一个一个读取每一行来收集行的锁定信息。这对于一个大表不是很快。
注意:
1. 如果表被其他人整体加上了排他锁,pgrowlocks将被阻塞。
2. pgrowlocks不保证能产生一个自我一致的快照。在它执行期间,有可能加上一个新行锁,也有可能有旧行锁被释放。
pgrowlocks不显示被锁定行的内容。如果你想同时查看行内容,你可以这样做:
SELECT * FROM accounts AS a, pgrowlocks('accounts') AS p
WHERE p.locked_row = a.ctid;
不过要注意,这样一个查询将非常低效。
样例输出
test=# SELECT * FROM pgrowlocks('t1');
locked_row | locker | multi | xids | modes | pids
------------+--------+-------+-------+----------------+--------
(0,1) | 609 | f | {609} | {"For Share"} | {3161}
(0,2) | 609 | f | {609} | {"For Share"} | {3161}
(0,3) | 607 | f | {607} | {"For Update"} | {3107}
(0,4) | 607 | f | {607} | {"For Update"} | {3107}
(4 rows)