==== LOCK ==== .. container:: refentry :name: SQL-LOCK .. container:: titlepage .. container:: refnamediv .. rubric:: LOCK :name: lock LOCK — 锁定一个表 .. container:: refsynopsisdiv .. rubric:: 大纲 :name: 大纲 .. code:: 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 .. container:: refsect1 :name: id-1.9.3.155.5 .. rubric:: 描述 :name: 描述 ``LOCK TABLE``\ 获得一个表级锁,必要时会等待任何冲突锁被释放。如果指定了\ ``NOWAIT``\ ,``LOCK TABLE``\ 不会等待以获得想要的锁:如果它不能立刻得到,该命令会被中止并且发出一个错误。一旦获取到,该锁会被在当前事务中一直持有(没有\ ``UNLOCK TABLE``\ 命令,锁总是在事务结束时被释放)。 当一个视图被锁定时,出现在该视图定义查询中的所有关系也将被使用同样的锁模式递归地锁住。 在为引用表的命令自动获取锁时, PostgreSQL总是尽可能使用最不严格的锁模式。提供\ ``LOCK TABLE``\ 是用于想要更严格的锁定的情况。例如,假设一个应用运行一个\ ``READ COMMITTED`` 隔离级别的事务,并且需要确保一个表中的数据在该事务的期间保持稳定。要实现这个目的,必须在查询之前在表上获得\ ``SHARE``\ 锁模式。这将阻止并发的数据更改并且确保该表的后续读操作会看到已提交数据的一个稳定视图,因为\ ``SHARE``\ 锁模式与写入者所要求的``ROW EXCLUSIVE``\ 锁有冲突,并且你的 ``LOCK TABLE name`` IN SHARE MODE 语句将等待,直到任何并发持有\ ``ROW EXCLUSIVE``\ 模式锁的持有者提交或者回滚。因此,一旦得到锁,就不会有未提交的写入还没有解决。更进一步,在释放该锁之前,任何人都不能开始。 要在运行在\ ``REPEATABLE READ``\ 或\ ``SERIALIZABLE`` 隔离级别的事务中得到类似的效果,你必须在执行任何 ``SELECT``\ 或者数据修改语句之前执行 ``LOCK TABLE``\ 语句。一个 ``REPEATABLE READ``\ 或者\ ``SERIALIZABLE``\ 事务的数据视图将在它的第一个\ ``SELECT``\ 或者数据修改语句开始时被冻结。在该事务中稍后的一个\ ``LOCK TABLE``\ 仍将阻止并发写-但它不会确保该事务读到的东西对应于最新的已提交值。 如果一个此类事务正要修改表中的数据,那么它应该使用 ``SHARE ROW EXCLUSIVE``\ 锁模式来取代 ``SHARE``\ 模式。这会保证一次只有一个此类事务运行。如果不用这种模式,死锁就可能出现:两个事务可能都要求 ``SHARE``\ 模式,并且都不能获得 ``ROW EXCLUSIVE``\ 模式来真正地执行它们的更新(注意一个事务所拥有的锁不会冲突,因此一个事务可以在它持有\ ``SHARE`` 模式时获得\ ``ROW EXCLUSIVE``\ 模式 — 但是如果有其他人持有\ ``SHARE``\ 模式时则不能)。为了避免死锁,确保所有的事务在同样的对象上以相同的顺序获得锁,并且如果在一个对象上涉及多种锁模式,事务应该总是首先获得最严格的那种模式。 .. container:: refsect1 :name: id-1.9.3.155.6 .. rubric:: 参数 :name: 参数 .. container:: variablelist *``name``* 要锁定的一个现有表的名称(可以是模式限定的)。如果在表名前指定了 ``ONLY``\ ,只有该表会被锁定。如果没有指定了 ``ONLY``\ ,该表和它所有的后代表(如果有)都会被锁定。可选地,在表名后指定\ ``*``\ 来显式地表示把后代表包括在内。 命令\ ``LOCK TABLE a, b;``\ 等效于``LOCK TABLE a; LOCK TABLE b;``\ 。这些表会被按照在 ``LOCK TABLE``\ 中指定的顺序一个一个 被锁定。 *``lockmode``* 锁模式指定这个锁和哪些锁冲突。 如果没有指定锁模式,那儿将使用最严格的模式\ ``ACCESS EXCLUSIVE``\ 。 ``NOWAIT`` 指定\ ``LOCK TABLE``\ 不等待任何冲突锁被释放:如果所指定的锁不能立即获得,那么事务就会中止。 .. container:: refsect1 :name: id-1.9.3.155.7 .. rubric:: 注解 :name: 注解 ``LOCK TABLE ... IN ACCESS SHARE MODE``\ 要求目标表上的\ ``SELECT``\ 特权。\ ``LOCK TABLE ... IN ROW EXCLUSIVE MODE``\ 要求目标表上的\ ``INSERT``\ 、\ ``UPDATE``\ 、\ ``DELETE``\ 或\ ``TRUNCATE``\ 特权。所有其他形式的\ ``LOCK``\ 要求表级\ ``UPDATE``\ 、\ ``DELETE``\ 或\ ``TRUNCATE``\ 特权。 在该视图上执行锁定的用户必须具有该视图上相应的特权。此外视图的拥有者必须拥有底层基关系上的相关特权,但是执行锁定的用户不需要底层基关系上的任何权限。 ``LOCK TABLE``\ 在一个事务块外部没有用处:锁将只保持到语句完成。因此如果在一个事务块外部使用了\ ``LOCK``\ ,PostgreSQL会报告一个错误。使用\ `BEGIN `__\ 和\ `COMMIT `__\ (或者\ `ROLLBACK `__\ )定义一个事务块。 ``LOCK TABLE``\ 只处理表级锁,因此涉及到 ``ROW``\ 的模式名称在这里都是不当的。这些模式名称应该通常被解读为用户在被锁定表中获取行级锁的意向。还有,``ROW EXCLUSIVE``\ 模式是一个可共享的表锁。记住就 ``LOCK TABLE``\ 而言,所有的锁模式都具有相同的语义,只有模式的冲突规则有所不同。关于如何获取一个真正的行级锁的信息, .. container:: refsect1 :name: id-1.9.3.155.8 .. rubric:: 示例 :name: 示例 在将要向一个外键表中执行插入时在主键表上获得一个 ``SHARE``\ 锁: .. code:: programlisting BEGIN WORK; LOCK TABLE films IN SHARE MODE; SELECT id FROM films WHERE name = 'Star Wars: Episode I - The Phantom Menace'; -- 如果记录没有被返回就做 ROLLBACK INSERT INTO films_user_comments VALUES (_id_, 'GREAT! I was waiting for it for so long!'); COMMIT WORK; 在将要执行一次删除操作前在主键表上取一个 ``SHARE ROW EXCLUSIVE``\ 锁: .. code:: programlisting 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; .. container:: refsect1 :name: id-1.9.3.155.9 .. rubric:: 兼容性 :name: 兼容性 在 SQL 标准中没有\ ``LOCK TABLE``\ ,SQL 标准中使用 ``SET TRANSACTION``\ 指定事务上的并发层次。 PostgreSQL也支持这样做,详见 `SET TRANSACTION `__\ 。 除\ ``ACCESS SHARE``\ 、\ ``ACCESS EXCLUSIVE``\ 和 ``SHARE UPDATE EXCLUSIVE``\ 锁模式之外, PostgreSQL 锁模式和 ``LOCK TABLE``\ 语法与 Oracle 中的兼容。