PostgreSQL中的死锁

版权声明:本文为博主原创之文章,未经博主允许谢绝转载。 https://blog.csdn.net/pg_hgdb/article/details/82909119

PostgreSQL通过使用称为MVCC(多版本并发控制)的方法实现并发控制。在此方法中,当正在更新项目时,更改将不会覆盖原始数据,而是将创建项目的新版本(带有更改)。因此,我们将存储多个版本的项目。这种模型的主要优点之一是为查询(读取)数据而获取的锁与写入数据时获取的锁不冲突,因此读取永远不会阻止写入,写入也从不阻止读取。但是,如果存储了同一项目的多个版本,那么事务看到的是哪个版本呢?这里需要回顾一下事务隔离的概念。事务指定隔离级别,该级别定义一个事务必须与其他事务所做的资源或数据修改隔离的程度。该程度与事务生成的锁直接相关,因此,它可以在事务级别进行指定,而且可以确定正在运行的事务对其他正在运行的事务的影响。

为什么在处理死锁问题时会涉及以上内容呢?因为sql命令会自动获取锁以确保MVCC行为,并且获取的锁类型取决于定义的事务隔离级别。理解各种类型的锁之间的冲突是非常重要的,这是因为两个事务不能同时在同一个对象上持有冲突的锁。还有一个值得注意的细节是,一旦获得锁通常会一直持有到事务结束。当有两个想要同时在同一个对象上持有冲突锁的运行中的事务时会发生什么?其中一个将获得锁定,另一个将不得不等待。

什么是死锁?

数据库死锁有很多种定义,但简而言之就是:数据库死锁是两个或多个事务正在等待彼此释放锁的情况。

例如,以下情况将导致死锁的发生:

应用程序A获取表1第1行的锁来进行更新操作。与此同时,应用程序B获得表2第2行的锁。现在,应用程序A需要锁定表2第2行,来继续执行并完成事务,但它无法获取锁,这是因为该锁现在由应用程序B保持。应用程序A需要等待应用程序B释放它。而且,如果应用程序B需要锁定表1第1行,来继续执行并完成事务,它也无法获取锁,因为该锁由应用程序A持有。

这样我们就遇到了死锁。应用程序A正在等待应用程序B持有的资源来完成运行,而应用程序B正在等待应用程序A持有的资源。那么这种情况下将如何继续呢?数据库引擎将检测死锁并终止其中一个事务,解除阻塞另一个事务并在被杀死的事件上引发死锁错误。

如何预防死锁?

在数据库方面能做出避免死锁的事情并不多。不过还是可以给出一些建议:

1)搜索长时间运行的事务。由于锁通常保持到事务结束,因此事务处理的时间越长,对资源的锁定就越长。如果可能,尝试将长时间运行的事务拆分为更小/更快的事务。

2)有时不可能实际拆分事务,因此工作应该集中于每次都尽量以一致的顺序执行操作,这样事务就能形成格式良好的队列并且不会发生死锁。

3)建议将重试逻辑添加到应用程序中,如果发生死锁,应用程序将再次运行相同的命令。

4)检查使用的隔离级别,可以尝试着更改它们。查找像SELECT FOR UPDATE和SELECT FOR SHARE这样的等命令,因为它们会生成显式锁,然后评估是否真的需要这些语句或者是否可以使用旧的数据快照。如果无法删除这些命令,可以尝试改用较低的隔离级别,例如READ COMMITTED。

5)向表中添加索引时要仔细考虑,选择最合适的进行添加。这样,查询时就需要扫描更少的索引记录,从而设置更少的锁。

6)还可以采取一些预防措施。例如命名一个实例时,避免在添加列的同一条命令中同时添加默认值。如果将此操作拆分为多个命令,例如添加列,添加默认值,更新空值,将最小化锁的影响。

By Kalath

猜你喜欢

转载自blog.csdn.net/pg_hgdb/article/details/82909119