您正在查看: 标签 进一步 下的文章

Server Programming InterfaceLOCK

LOCK

Name

LOCK -- 锁定一个表

Synopsis

LOCK [ TABLE ] [ ONLY ] name [, ...] [ IN lockmode MODE ] [ NOWAIT ]

这里的lockmode可以是下列之一: ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE| SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE

描述

LOCK TABLE获取一个表级锁,必要时等待任何冲突的锁释放。
如果声明了NOWAIT,那么LOCK
TABLE并不等待它所需要的锁:如果无法立即获取该锁,那么该命令退出并且发出一个错误信息。如果成功获取了这个锁,那么它就会在当前事务的余下部分一直保持。
没有UNLOCK TABLE命令;锁总是在事务结尾释放。

在为那些引用了表的命令自动请求锁的时候,PostgreSQL总是尽可能使用最小限制的锁模式。 LOCK TABLE是为你在需要更严格的锁的场合提供的。 例如,假设一个应用在"读已提交"隔离级别上运行事务, 并且它需要保证在表中的数据在事务的运行过程中都存在。要实现这个目的, 你可以在查询之前对表使用SHARE锁模式进行锁定。 这样将保护数据不被并发修改并且为任何更进一步的对表的读操作提供实际的当前状态的数据, 因为SHARE锁模式与任何写操作需要的ROW EXCLUSIVE模式冲突, 并且你的LOCK TABLE name IN SHARE MODE语句将等到所有当前 持有ROW EXCLUSIVE模式的锁提交或回卷后才执行。 因此,一旦你获得该锁,那么就不会存在未提交的写操作,并且其他人只能在你释放锁之后才能再次获取锁。

如果运行在"可串行化"隔离级别实现类似的效果的时候,你必须在执行任何LOCK TABLE或数据修改语句之前运行一个SELECT语句。 一个可串行化事务的数据图像将在其第一个SELECT或者数据修改语句开始的时候冻结住。 稍后的LOCK TABLE将仍然阻止并发的写,但它不能保证事务读取的东西对应最近提交的数值。

如果一个此类的事务准备修改一个表中的数据, 那么应该使用SHARE ROW EXCLUSIVE锁模式,而不是SHARE模式。 这样就保证任意时刻只有一个此类的事务运行。不这样做就可能会死锁: 当两个并发的事务可能都请求SHARE模式,然后试图更改表中的数据时, 两个事务在实际执行更新的时候都需要ROW EXCLUSIVE锁模式,但是它们无法再次获取这个锁。 请注意,一个事务自己的锁是从不冲突的,因此一个事务可以在持有SHARE模式的锁的时候请求ROW EXCLUSIVE模式(但是不能在任何其它事务持有SHARE模式的时候请求)。为了避免死锁, 所有事务应该保证以相同的顺序对相同的对象请求锁,并且,如果涉及多种锁模式, 那么事务应该总是最先请求最严格的锁模式。

有关锁模式和锁定策略的更多信息,请参考Section 13.3。

参数

name

要锁定的现存表的名字(可以有模式修饰)。 若已声明ONLY,则仅仅锁定那个表。若ONLY未声明,表 机器字表(若存在)将会被锁。

命令LOCK TABLE a, b;等效于LOCK TABLE a; LOCK TABLE b;。 表是按照LOCK TABLE命令中声明的顺序一个接一个顺序上锁的。

lockmode

锁模式声明这个锁和那些锁冲突。锁模式在Section 13.3里描述。

如果没有声明锁模式,那么使用最严格的模式ACCESS EXCLUSIVE模式。

NOWAIT

声明LOCK TABLE不去等待任何冲突的锁释放: 如果无法不等待理解获取所要求的锁,那么事务退出。

注意

LOCK TABLE ... IN ACCESS SHARE MODE需要在目标表上有SELECT权限。 所有其它形式的LOCK至少需要UPDATE,DELETE或TRUNCATE权限之一。

LOCK TABLE在事物块之外是无效的:锁将对语句完成时仍持有。 所以PostgreSQL报告一个错误,若LOCK 在一个事物块外被使用。使用BEGIN和COMMIT(或者ROLLBACK)来定义一个事物块。

LOCK TABLE只处理表级的锁,因此那些有ROW字样的锁都是用词不当。 这些模式名字通常应该理解为用户视图在一个被锁定的表中获取行级的锁。 同样,ROW EXCLUSIVE模式也是一个可共享的表级锁。 一定要记住,只要是涉及到LOCK TABLE,那么所有锁模式都有相同的语意, 区别只是它们与哪种锁冲突的规则。有关如何获取一个行级锁的信息, 请参阅Section 13.3.2和SELECT命令参考页的FOR UPDATE/FOR SHARE子句子句信息。

例子

演示在往一个外键表上插入时在有主键的表上使用SHARE的锁:

BEGIN WORK;
LOCK TABLE films IN SHARE MODE;
SELECT id FROM films WHERE name = 'Star Wars: Episode I - The Phantom Menace';
-- Do ROLLBACK if record was not returned
INSERT INTO films_user_comments VALUES (_id_, 'GREAT! I was waiting for it for so long!');
COMMIT WORK;

在执行删除操作时对一个有主键的表进行SHARE ROW EXCLUSIVE锁:

BEGIN WORK;
LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
DELETE FROM films_user_comments WHERE id IN(SELECT id FROM films WHERE rating < 5);
DELETE FROM films WHERE rating < 5;
COMMIT WORK;

兼容性

在SQL标准里面没有LOCK TABLE,可以使用SET TRANSACTION 来声明当前事务的级别。PostgreSQL也支持这个,参阅 SET TRANSACTION获取详细信息。

除了ACCESS SHARE,ACCESS EXCLUSIVE, 和SHARE UPDATE EXCLUSIVE锁模式外,PostgreSQL 锁模式和LOCK TABLE语句都与那些在Oracle 里面的兼容。

libpq-C库在多线程程序里的行为

31.18. 在多线程程序里的行为

libpq是可重入的并且是线程安全的。另外,在你编译自己的应用代码时, 可能需要使用额外的编译器命令行选项。请参考你的系统的文档获取有关如何 编译多线程应用的信息。或者查阅src/Makefile.global, 找PTHREAD_CFLAGS和PTHREAD_LIBS。此功能允许查询 的libpq的线程安全状态:

PQisthreadsafe

返回线程的libpq库的安全状况

int PQisthreadsafe();

libpq是线程安全的时,返回1 和如果不是返回0。

一个现实是,两个线程不能试图同时操作同一个PGconn对象。特别是, 你不能从不同的线程里通过同一个连接对象发出并发的命令。 (如果你需要运行并行命令,请使用多个连接。)

PGresult对象在创建后是只读的,因此可以自由地在线程之间传递。

过时了的函数PQrequestCancel和PQoidStatus都是线程不安全的, 因此不应该在一个多线程的程序里面使用。PQrequestCancel可以由PQcancel代替。 PQoidStatus可以由PQoidValue代替。

If you are using Kerberos inside your application (in addition to inside libpq),you will need to do locking around Kerberos calls because Kerberos functions are not thread-safe. See functionPQregisterThreadLockin the libpqsource code for a way to do cooperative locking betweenlibpqand your application. 如果在你的应用内部使用了 Kerberos (而不仅仅是libpq里面), 你就需要在 Kerberos 调用周围锁住,因为 Kerberos 函数不是线程安全的。 参阅libpq源代码里面的PQregisterThreadLock获取一个 在libpq和你的应用之间进行恰当锁定的方法。

If you experience problems with threaded applications,run the program insrc/tools/threadto see if your platform has thread-unsafe functions. This program is run by configure,but for binary distributions your library might not match the library used to build the binaries. 如果你的线程应用有问题,那么运行一个在src/tools/thread里的程序, 看看你的平台是否有线程安全的函数。这个程序由configure运行,但如果是二进制版本, 你的库可能就不能和制作二进制的那个库匹配了。

libpq-C库SSL支持

31.17. SSL支持

PostgreSQL本机支持使用SSL连接对客户端/服务器通讯进行加密, 以增强安全性。参阅Section 17.8获取有关服务器端SSL功能的细节。

libpq读取全系统 OpenSSL配置文件,默认情况下,文件命名 为openssl.cnf并且存放在openssl version -d 目录报告中,此默认可以通过设置环境变量OPENSSL_CONF覆盖配置名称文件名称。

31.17.1. 证书验证

缺省,PostgreSQL不会执行任何服务器证书验证。 这就意味着可以在客户端没有察觉的情况下骗过服务认证(如,通过修改一个DNS记录或接管服务IP)。 为了避免这种情况,必须使用SSL证书认证。

如果sslmode参数设置为verify-ca,libpq将通过检查受信任的证书颁发机构的证书链(CA)来通过 服务是可信任的。如果sslmode设置为verify-full,libpq会通过验证服务主机名匹配认证来认为服务是可信任的。 如果服务验证不能被通过,那么SSL连接会失败。在大多数对安全要求较高的环境中,建议使用verify-full。

在verify-full模式下,认证的cn(Common Name)属性与主机名进行匹配。 如果cn以*开始,会被看做是一个通配符,并且会匹配除了.之外的所有字符。 这就意味着认证不会匹配子域名。如果是使用IP而不是主机名进行连接,会进行IP匹配检查(不会做DNS检查)。

为了允许服务器认证通过,一个或多个信任的CA认证必须放在用户的home目录下的~/.postgresql/root.crt文件中。 Windows下的文件名是%APPDATA%\postgresql\root.crt。

如果存在~/.postgresql/root.crl文件(Windows下是%APPDATA%\postgresql\root.crl文件),同样也会检查CRL。

root认证文件和CRL的位置可以通过设置sslrootcert和sslcrl连接参数,或PGSSLROOTCERT和PGSSLCRL环境变量进行修改。

31.17.2. 客户端证书

如果服务器要求一个信任的客户端认证,libpq将发送存储在~/.postgresql/postgresql.crt文件中的 证书。该认证必须通过一个或多个服务器信任的CA认证。 同时也必须出示一个匹配的私钥文件~/.postgresql/postgresql.key。 私钥文件不允许任何对世界或组的访问;通过chmod 0600 ~/.postgresql/postgresql.key命令可以实现。 在Windows上,这个文件是%APPDATA%\postgresql\postgresql.crt和%APPDATA%\postgresql\postgresql.key, 同时没有特定的权限检查,因为目录被认为是安全的。证书和key文件的位置可以通过sslcert和sslkey连接参数, 或PGSSLCERT和PGSSLKEY环境变量进行覆盖重写。

在一些情况下,客户端认证可能会被一个"intermediate"的证书认证来标记, 而不是一个能直接被服务信任的。为了使用一个这种认证,向postgresql.crt文件 追加标记认证权,并且直到"root"都可以被服务器信任。root认证应该被包含在任何一种情况(postgresql.crt包含一个或多个认证)下。

需要注意的是,root.crt列出了最高级别的CA,被认为对标记服务认证中是可信任的。 原则上,不需要列出标记客户端认证的CA,尽管在大多数情况下,CA仍会被认为对服务器认证是可信任的。

31.17.3. 在不同的模式提供保护

sslmode参数的不同值提供了不同的保护级别。SSL可以提供为三种攻击提供保护:

Table 31-2. SSL的攻击

类型描述
Eavesdropping(窃听) 如果一个第三方可以在客户端与服务器端之间检查网络通信,那么它就能读取两边的连接信息(包括用户名和密码)以及传递的数据。对此, SSL通过加密进行防护。
MITM 如果客户端和服务器端进行传递数据的时候,第三方可以对其进行修改,那么他就能伪装成服务器,然后查看或修改数据(即使是加密的)。第三方接着可以向原始服务器发出连接信息和数据,最终造成无法防护这种攻击。这种攻击常用的载体有DNS中毒或IP绑架,即客户端被定向到预期之外的不同的服务器。同样还有几种其他的方法也能做到这种攻击。SSL通过服务器到客户端的证书验证来阻止这种攻击。
模拟 如果第三方可以伪装成一个认证了的客户端,那么它就能轻松访问到它本来不能访问的数据。典型的,如不安全的密钥管理,就会造成这种情况。SSL通过客户端认证来阻止这种情况,即确保只有知道有效认证的人员才能访问连接服务器。

对于一个被称为安全的连接来说,进行连接之前,必须在客户端和服务器端都进行SSL配置。 如果只在服务器端进行配置,在它知道服务器端需要高级认证之前不会发送敏感信息(如密码等)。 在libpq中,可以通过将sslmode参数设置为verify-full或verify-ca来确保安全连接, 并且为系统提供一个root认证以进行安全认证。类似于使用https和URL进行加密网页浏览。

一旦服务器已经认证,客户端就可以发送敏感信息。 这就意味着知道这一刻,客户端都不需要知道,是否认证需要证书,只在服务器配置,对其安全地指定。

所有以加密和密钥交换方式得SSL选项都会产生开销, 因此在性能和安全之间需要进行一个权衡。下表说明不同sslmode为值的安全风险, 以及关于安全和开销所做出的声明:

Table 31-3. SSL模式说明

sslmode窃听保护MITM保护声明
disabled 我不关心安全,我不想来支付加密的开销
允许可能 我不关心安全性,但我会支付的加密开销 ,如果服务器的治持的话
喜欢或许 我不关心加密,但我想支付加密开销 ,如果服务器支持它。
要求 我想我的数据是加密的,我接受的开销。我相信 该网络将确保我始终连接到服务器,按我想象的。
验证CA取决于对CA规定 我希望我的数据加密,我接受的开销。我想成为 确保连接到一个服务器,我坚信。
全验证 我希望我的数据加密,我接受开销。我想确定连接到一个服务器我相信,它是我 指定。

verify-ca和verify-full之间的不同是根据rootCA的规定。 如果使用的是一个公用CA,verify-ca允许那些带有CA注册的客户端对服务器进行连接访问。 在这种情况下,应该使用verify-full。如果使用的是一个本地CA, 甚至是一个自签名证书,使用verify-ca通常会提供充分的保护。

sslmode缺省值是prefer。如在表中说明的那样,从安全角度来看这样做是没有意义的, 并且如果可能的话,它只承诺性能的开销。它仅提供了缺省向后兼容性,在安全部署中不建议使用。

31.17.4. SSL文件的使用

Table 31-4. libpq/客户端SSL文件的使用

文件内容影响
~/.postgresql/postgresql.crt客户端证书服务器要求
~/.postgresql/postgresql.key客户端的私钥 证明由所有者发送的客户端证书,并不表示 证书拥有者是值得信赖
~/.postgresql/root.crt受信任的证书颁发机构 检查服务器证书是由受信任的证书机关签署。
~/.postgresql/root.crl证书颁发机构吊销证书服务器证书必须不在这个名单

31.17.5. SSL library initialization SSL库初始化

如果应用程序初始化libssl和/或libcrypto库以及libpq编译为支持SSL, 应该调用PQinitOpenSSL来告诉libpq说libssl和/或libcrypto已经被应用程序初始化了, 因此libpq将不会在初始化这些库。 参阅http://h71000.www7.hp.com/doc/83final/BA554_90007/ch04.html

PQinitOpenSSL

允许应用程序选择安全库初始化。

void PQinitOpenSSL(int do_ssl,int do_crypto);

当do_ssl为非0时,在第一次打开一个数据库连接之前,libpq将初始化OpenSSL库。当do_crypto为非0时,libcrypto库将被初始化。缺省(如果PQinitOpenSSL没有被调用),两个库都会被初始化。如果没有编译SSL支持,会提供该函数,但不会做任何事情。

如果应用程序使用并初始化OpenSSL,或其底层libcrypto库,那么在第一次打开一个数据库连接之前,必须调用这个函数(带有适当0值的参数)。同样要确保在打开一个数据库连接之前做过初始化。

PQinitSSL

允许应用程序选择安全库初始化。

void PQinitSSL(int do_ssl);

此功能相当于PQinitOpenSSL(do_ssl,do_ssl)它的应用是足够的同时初始化或都不初始化OpenSSL和libcrypto。

从8.0之后的版本就提供了PQinitSSL函数,然而PQinitOpenSSL函数是在8.4中才添加的,因此对老版本的libpq的使用,PQinitSSL是对应用程序的不错的选择。

libpq-C库LDAP查找连接参数

31.16. LDAP查找连接参数

如果libpq已经通过LDAP支持(configure的--with-ldap选项)进行的编译, 可以从一个中央服务器,通过LDAP检索连接选项,如host或dbname。 这样做的好处是,如果一个数据路连接参数发生了改变,在所有客户端的连接信息不必进行改变。

LDAP连接参数查找使用连接服务文件pg_service.conf(参阅Section 31.15)。 在pg_service.conf中的以ldap://开始的一行被看做是一个LDAP URL,并且会执行一个LDAP查询。 返回结果会是一个keyword = value,用于设置连接选项。URL必须符合RFC 1959,并且是如下形式:

ldap://[hostname[:port]]/search_base?attribute?search_scope?filter

这里localhost和port缺省的hostname缺省为389。

pg_service.conf的处理在LDAP成功查找之后就会被终止,但如果不能成功连接LDAP服务,那么会继续。 这是为了进一步指向不同的LDAP服务器的LDAP URL线而提供的一个回滚, 标准的keyword = value格式,或缺省的连接参数。 如果想在这种情况下获得一个错误信息,可以再LDAP URL后添加一个语法不正确的行。

LDIF文件创建的一个样本LDAP条目:

version:1
dn:cn=mydatabase,dc=mycompany,dc=com
changetype:add
objectclass:top
objectclass:groupOfUniqueNames
cn:mydatabase
uniqueMember:host=dbserver.mycompany.com
uniqueMember:port=5439
uniqueMember:dbname=mydb
uniqueMember:user=mydb_user
uniqueMember:sslmode=require

might be queried with the following LDAP URL:

ldap://ldap.mycompany.com/dc=mycompany,dc=com?uniqueMember?one?(cn=mydatabase)

也可以通过LDAP查找来混合日常服务文件。一个pg_service.conf中完整的一节的例子如下:

# only host and port are stored in LDAP,specify dbname and user explicitly
[customerdb]
dbname=customer
user=appuser
ldap://ldap.acme.com/cn=dbserver,cn=hosts?pgconnectinfo?base?(objectclass=*)

数据定义缺省值

5.2. 缺省值

一个字段可以赋予缺省值。如果新创建了一个数据行,而有些字段的数值没有声明, 那么这些字段将被填充为它们各自的缺省值。一条数据修改命令也可以明确地要求 把一个字段设置为它的缺省值,而不用事先知道这个缺省值是什么。有关数据操作 的命令在Chapter 6。

如果没有明确声明缺省值,那么缺省值是 NULL。这么做通常是合理的,因为NULL表示"未知"。

在一个表定义里,缺省值是在字段数据类型后面列出的。比如:

CREATE TABLE products (product_no integer,name text,price numeric DEFAULT 9.99
);

缺省值可以是一个表达式,它会在插入缺省值的时候计算(不是 在创建表的时候)。一个常见的例子是一个timestamp字段可能有缺省值 CURRENT_TIMESTAMP ,它表示插入行的时刻。另外一个常见的例子是为 每一行生成一个""serial number""。在PostgreSQL里, 通常是用类似下面这样的方法生成的:

CREATE TABLE products (product_no integer DEFAULT nextval('products_product_no_seq'),...
);

这里的nextval()从一个序列对象(参阅Section 9.15)提供后继的数值。这种做法非常普遍, 以至于我们有一个专门的缩写用于此目的:

CREATE TABLE products (product_no SERIAL,...
);

SERIAL 缩写在Section 8.1.4里有进一步描述。