PostgreSQL 教程: SKIP LOCKED 跳过锁定

七月 29, 2024

摘要:在本教程中,您将学习如何在 PostgreSQL 中使用SKIP LOCKED,以避免和解决死锁的问题。

目录

事务

事务是构建数据库驱动的应用程序的基本组件。当一个事务开始后,它可以读取和修改数据库中的数据。当它修改数据时,它会获得对它正在更改的资源(如行或表)的锁定。该锁定可防止其他事务同时修改相同的资源,从而确保以一致且可预测的方式对数据进行更改。

但是,如果两个事务同时尝试访问或修改同一资源,则它们最终可能会陷入死锁情况,即在另一个事务释放其锁定之前,任何一个事务都无法继续进行。

最佳实践

为了避免死锁,在设计和实现事务时,遵循一些最佳实践非常重要:

  • 避免长时间锁定资源:长事务和长时间运行的锁定可能会增加死锁的可能性。尽量保持事务简短,避免长时间持有锁。
  • 优化查询:一个事务的查询应该是最优的,并尝试仅对必需的行执行操作。这样可以减少事务锁定的行数,并允许其他事务访问这些行。
  • 确保更新以一致的顺序进行:确保相关资源的更新以一致的顺序进行。
  • 避免显式锁定和表级锁:尽可能避免使用显式锁定或表级锁,因为表级锁会限制对整个表的任何操作,并阻止任何其他事务对其执行任何查询。

解决死锁

如果遇到死锁,解决死锁的第一步是,分析情况并确定所涉及的事务、它们争用的资源以及导致死锁的事件顺序。您可以使用 PostgreSQL 日志文件或查询 pg_stat_activity 系统视图,来收集此信息。您可以使用此信息来识别死锁中涉及的查询和资源。

确定所涉及的查询和资源后,可以检查它们,以确定是否可以根据最佳实践进行任何优化,以减少未来出现死锁的可能性。

PostgreSQL SKIP LOCKED 示例

如果需要从一个表进行 SELECT,并在事务完成之前保护这些行不被更新,则可以指定 FOR UPDATE,但如果某些行被锁定了,则可以指定 SKIP LOCKED 来告诉查询忽略这些行,只对它可以访问的任何行执行操作。

下面是如何使用 “SELECT … FOR UPDATE SKIP LOCKED“ 的一个示例:

在会话 1 中:

BEGIN;

SELECT * FROM colours;
 id | name
----+-------
  1 | red
  2 | green
  3 | blue
(3 rows)

UPDATE colours SET name = 'scarlet' WHERE name = 'red';

在会话 2 中:

BEGIN;

SELECT * FROM colours FOR UPDATE NOWAIT;
ERROR:  could not obtain lock on row in relation "colours"

SELECT * FROM colours FOR UPDATE SKIP LOCKED;
 id | name
----+-------
  2 | green
  3 | blue
(2 rows)

在指定 SKIP LOCKED 选项,使用 SELECT … FOR UPDATE 时,有几件事要记住。

  • 应仅在多个事务可能同时尝试锁定相同行的情况下,才使用 SKIP LOCKED,并且需要可以接受某些事务能够跳过锁定的行。
  • 如果使用不小心,跳过锁定的行可能会导致数据不一致。请确保应用程序逻辑可以处理跳过某些行的情况。

或者,我们也可以在使用 SELECT … FOR UPDATE 时,指定 NOWAIT 选项。指定 NOWAIT 选项时,如果无法立即锁定选定的行,则语句将立即报告错误,而不是等待。它要求应用程序中有正确的错误处理逻辑。

总结

在设计实现事务和监控应用程序性能时,通过应用最佳实践,您可以避免和解决 PostgreSQL 数据库中的死锁。在使用诸如 “SELECT … FOR UPDATE SKIP LOCKED“ 这类技术时,请务必谨慎使用它,并严谨地测试您的应用程序。