postgresql - 锁

6 阅读7分钟

在连接锁之前需要了解两个概念,session 和 transaction:

session: 客户端连接到PG服务器,就开始一个session,在一个session中可以执行任意多个transaction transaction: 一段sql(一个或多个), begin/begin transaction/start transaction开始一个transaction, commit/rollback结束。 在一个transaction中,所有的sql要么全部成功,要么全部失败(原子性)

锁的分类

  1. 表级锁
  2. 行级锁
  3. 页级锁
  4. 劝告锁

表级锁

执行语句时自动获得,或者在事务中显示地使用lock语句

  1. access share(访问共享锁)

    SELECT 命令在被引用的表上会获得一个这种模式的锁。通常,任何只读取表而不修改它的查询都将获取这种表模式。

  2. row share(行共享)

    防止其他事务修改正在被当前事务读取的数据,从而确保当前事务读取数据的一致性。但是,它并不阻止其他事务读取这些数据,所以称为“行共享”锁。(select for share/select for update)

  3. row exclusive(行独占)

    ROW EXCLUSIVE锁是一种防止其他事务同时修改同一行数据的锁,但其他事务仍然可以查询(读取)这一行。当一个事务通过UPDATEDELETEINSERT命令在一行上获取ROW EXCLUSIVE锁时,其他事务不能对其进行更新,直到ROW EXCLUSIVE锁被释放。

    总的来说,ROW SHAREROW EXCLUSIVE锁都允许其他事务读取被锁定的行,但ROW EXCLUSIVE锁会阻止其他事务修改被锁定的行,而ROW SHARE锁则不会。

  4. share update exclusive(共享更新独占)

    SHARE UPDATE EXCLUSIVE 锁是一种中等级别的锁模式。当一个事务在表上获取 SHARE UPDATE EXCLUSIVE 锁时,其他事务不能获得 SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVE 或 ACCESS EXCLUSIVE 锁。

    这意味着,一旦一个事务在表上获得了 SHARE UPDATE EXCLUSIVE 锁,其他事务就不能更改该表的定义(例如,添加或删除列)或删除该表。然而,即使一个表上有 SHARE UPDATE EXCLUSIVE 锁,其他事务仍然可以在该表上插入、更新和删除行,也可以获取 ROW SHARE 或 ROW EXCLUSIVE 锁。

    SHARE UPDATE EXCLUSIVE 锁通常用于那些需要在不阻止并发读写的情况下,保护表免受结构改变的影响。例如,SHARE UPDATE EXCLUSIVE 锁可以用于在创建一个新的索引或添加一个外键约束时,防止其他事务同时删除表或更改表的定义。

  5. share(共享)

    SHARE锁是一种更严格的锁模式。如果一个事务在表上获得了SHARE锁,其他事务就不能再获得该表的SHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE锁。

    这意味着,一旦一个事务在表上获得了SHARE锁,其他事务就不能更改该表的定义(例如,添加或删除列)或删除该表。然而,即使一个表上有SHARE锁,其他事务仍然可以在该表上插入、更新和删除行,也可以获取ROW SHAREROW EXCLUSIVE锁。

    SHARE锁通常用于在不阻止并发数据改变的情况下,保护表免受结构改变的影响。例如,SHARE锁可以用于在创建一个新的索引或添加一个外键约束时,防止其他事务同时删除表或更改表的定义。

  6. share row exclusive(共享行独占)

    SHARE ROW EXCLUSIVE是PostgreSQL中的一种锁模式。当一个事务在表上获取SHARE ROW EXCLUSIVE锁时,其他事务不能获取该表的SHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE锁,也不能在该表上获取ROW SHAREROW EXCLUSIVESELECT FOR UPDATE/SHARE锁。

    这意味着,当一个事务在表上获取SHARE ROW EXCLUSIVE锁时,其他事务不能改变该表的结构,也不能在该表上插入、更新或删除行,直到SHARE ROW EXCLUSIVE锁被释放。

    SHARE ROW EXCLUSIVE锁主要用于那些需要保护表免受并发读写和结构改变的操作,如VACUUM FULL。这种锁模式能确保在当前事务执行的同时,其他事务不能对表进行任何读写操作或结构改变。

  7. exclusive(独占)

    EXCLUSIVE 锁是 PostgreSQL 中的一种锁模式。当一个事务在表上获取 EXCLUSIVE 锁时,其他事务不能获取该表的 SHARESHARE ROW EXCLUSIVEEXCLUSIVE 或 ACCESS EXCLUSIVE 锁,也不能在该表上获取 ROW SHAREROW EXCLUSIVE 或 SELECT FOR UPDATE/SHARE 锁。

    这意味着,当一个事务在表上获取 EXCLUSIVE 锁时,其他事务不能改变该表的结构,也不能在该表上插入、更新或删除行,直到 EXCLUSIVE 锁被释放。

    EXCLUSIVE 锁主要用于那些需要保护表免受并发读写和结构改变的操作。这种锁模式能确保在当前事务执行的同时,其他事务不能对表进行任何读写操作或结构改变。

  8. access exclusive(访问独占)

    ACCESS EXCLUSIVE是最严格的锁模式。当一个事务在表上获取ACCESS EXCLUSIVE锁时,其他事务不能获取该表的任何其他类型的锁。

    这意味着,当一个事务在表上获取ACCESS EXCLUSIVE锁时,其他事务不能读取或更改该表的数据,也不能更改该表的结构,直到ACCESS EXCLUSIVE锁被释放。

    ACCESS EXCLUSIVE锁通常用于那些需要对表进行大规模改变的操作,如ALTER TABLEDROP TABLETRUNCATEREINDEXCLUSTERVACUUM FULL等。这种锁模式能确保在当前事务执行的同时,其他事务不能对表进行任何操作。

行级锁

同一个事务可能会在相同的行上保持冲突的锁,甚至是在不同的子事务中。但是除此之外,两个事务永远不可能在相同的行上持有冲突的锁。

行级锁不影响数据查询,它们只阻塞对同一行的写入者和加锁者。行级锁在事务结束时或保存点回滚的时候释放,就像表级锁一样

  1. for update
  2. for no key update

    使用 SELECT FOR UPDATE 语句时,被选中的行将被锁定,直到当前事务结束。这意味着,如果有另一个事务试图 UPDATEDELETESELECT FOR UPDATESELECT FOR NO KEY UPDATESELECT FOR SHARE 或 SELECT FOR KEY SHARE 被锁定的行,那么这个事务将被阻塞,直到当前事务结束。

    例如,假设有两个并发事务,事务 A 和事务 B。事务 A 先执行 SELECT FOR UPDATE 语句,锁定了一些行。然后,事务 B 试图 UPDATE 这些被锁定的行。在这种情况下,事务 B 将被阻塞,直到事务 A 结束并释放了这些行的锁。

    FOR NO KEY UPDATE 在 PostgreSQL 中的作用类似于 FOR UPDATE,都是用来锁定被选定的行直到当前事务结束。不过它们之间有一个主要的区别。

    当你使用 FOR UPDATE 锁定一行时,其他事务不能更新该行,也不能更新任何与该行有外键关系的行。这是因为 FOR UPDATE 会锁定该行的所有键(主键和外键)。

    而当你使用 FOR NO KEY UPDATE 锁定一行时,其他事务不能更新该行,但是可以更新与该行有外键关系的行。这是因为 FOR NO KEY UPDATE 只锁定该行的主键,不锁定外键。

    所以,如果你需要锁定一行,但是不希望阻止其他事务更新与该行有外键关系的行,你可以使用 FOR NO KEY UPDATE

    BEGIN; SELECT * FROM orders WHERE id = 1 FOR UPDATE; -- 这里可以进行一些更新操作,例如: UPDATE orders SET status = 'processed' WHERE id = 1; COMMIT;
    
    BEGIN; SELECT * FROM orders WHERE id = 1 FOR NO KEY UPDATE; -- 这里可以进行一些更新操作,例如: UPDATE orders SET status = 'processed' WHERE id = 1; COMMIT;
    
  3. for share
  4. for key share

页级锁

劝告锁

可重入