postgresql 索引状态_PostgreSQL中的锁:3.其他锁

我们已经讨论了一些对象级锁(特别是关系级锁),以及行级锁及其与对象级锁的连接,还探讨了等待队列。这次我们要来个大杂烩。我们将从死锁开始(实际上,我计划上次讨论死锁,但是这篇文章本身篇幅太长了),然后简要回顾一下对象级锁,最后讨论谓词锁

死锁

使用锁时,我们可能会遇到死锁。当事务一试图获取事务二已经使用的资源,而事务二试图获取事务一使用的资源时,就会发生这种情况。左下图说明了这一点:实线箭头表示获取的资源,而虚线箭头表示尝试获取已使用的资源。

为了可视化死锁,可以方便地构建等待图。为此,我们删除特定的资源,只保留事务,并指示哪个事务等待哪个事务。如果一个图包含一个循环(从一个顶点开始,我们可以沿着箭头走到它自己),这就是死锁。

3336f2c5ade1a4b0c8c3f01c99943e7b.png

不仅对于两个事务,对于任何数量的事务,都可能发生死锁。

如果发生死锁,则所涉及的事务只能无限期地等待。因此,包括PostgreSQL在内的所有DBMS都会自动跟踪锁定。

然而,检查需要一定的过程,而且不希望每次请求一个新锁时都进行检查(毕竟死锁很少发生)。因此,当一个进程试图获取锁,但无法获取时,它将排队并«进入休眠状态»,但会将计时器设置为deadlock_timeout参数中指定的值(默认为1秒)。如果资源提前释放,这当然是好的,数据库可以节省开支。但是,如果deadlock_timeout到期,等待继续,等待进程将唤醒并启动检查。

如果检查(包括建立等待图表并搜索周期)没有检测到死锁,则它将继续休眠。

早些时候,我在评论中因为没有提到lock_timeout参数而受到了相当大的指责,这个参数会影响任何运并避免无限长的等待:如果在指定的时间内无法获取锁,则运算会以lock_not_available错误终止。不要将此参数与statement_timeout混淆,后者限制执行运算的总时间,无论后者是等待锁定还是执行常规工作。

但是如果检测到死锁,其中一个事务(在大多数情况下,是启动检查的事务)将被迫中止。这将释放它获得的锁,并允许其他事务继续进行。

死锁通常意味着应用程序设计不正确。有两种方法可以检测到这种情况:第一,消息将出现在服务器日志中,第二,其值pg_stat_database.deadlocks将增加。

死锁示例

通常,死锁是由锁定表行的顺序不一致引起的。让我们考虑一个简单的例子。第一个事务将从第一个帐户中转移100卢布到第二个帐户中。为此,交易减少了第一个帐户:

=> BEGIN;=> UPDATE accounts SET amount = amount - 100.00 WHERE acc_no = 1;UPDATE 1

同时,第二个事务将从第二个帐户向第一个帐户转帐10卢布。从减少第二个帐户开始

|  => BEGIN;|  => UPDATE accounts SET amount = amount - 10.00 WHERE acc_no = 2;|  UPDATE 1

现在,第一个事务尝试增加第二个帐户,但检测到行上的锁。

=> UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 2;

然后,第二个交易尝试增加第一个帐户,也被阻止。

|  => UPDATE accounts SET amount = amount + 10.00 WHERE acc_no = 1;

因此出现了循环等待,它不会自行结束。在第二个步骤中,尚无法访问资源的第一个事务将启动检查死锁并被服务器强制中止。 

ERROR:  deadlock detectedDETAIL:  Process 16477 waits for ShareLock on transaction 530695; blocked by process 16513.Process 16513 waits for ShareLock on transaction 530694; blocked by process 16477.HINT:  See server log for query details.CONTEXT:  while updating tuple (0,2) in relation "accounts"

现在,第二个事务可以继续。

|  UPDATE 1|  => ROLLBACK;=> ROLLBACK;

执行此类操作的正确方法是以相同顺序锁定资源。例如:在这种情况下,帐户可以按其编号的升序锁定。

两个UPDATE命令的死锁  

有时,在似乎永远不会发生的情况下,我们可能会陷入僵局。例如:将SQL命令视为原子命令是方便且通常的,但是UPDATE命令在更新行时将其锁定。这不会立即发生。因此,如果命令更新行的顺序与另一命令执行行的顺序不一致,则会发生死锁。

尽管这种情况不太可能发生,但仍然可能发生。为了重现它,我们将在amount列上以降序创建索引amount

=> CREATE INDEX ON accounts(amount DESC);

为了能够观察到发生的情况,让我们创建一个函数,该函数可以增加传递的值,但是非常非常缓慢,直到一整秒:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值