PostgreSQL出现死锁该如何解决


Posted in PostgreSQL onMay 30, 2022

什么是数据库死锁

在操作系统领域当中,死锁指的是两个或者两个以上的进程在运行的过程中,因为争夺共同的访问资源而相互等待阻塞,最终导致进程继无法续执行的一种阻塞现象。那么在数据库领域当中死锁又是怎样的表现形式呢?数据库死锁又会带来怎样的问题呢?

在理解数据库死锁之前,我们先来明确下数据库的锁到底是什么?有过Java编程经验的同学都知道,Java中的锁是为了解决共享数据的并发访问安全问题,防止并发访问导致的共享数据出现错乱。那么在数据库领域,数据库中的锁又是来干什么的呢?实际上在数据库中所也是解决并发问题。假如在同一时刻,可能存在多个事务对同一张表的同一个字段进行数字的加减操作,如果没有任何的控制措施也同样会导致各种各样的数据一致性问题。因此数据库的锁实际上也是为了保证数据一致性的一种手段,对可能存在的并发操作进行控制。

下面以一个例子来进行说明,假设有这样两个事务,事务A中包含如下语句:

UPDATE user SET name = '小慕' where id = 1
UPDATE product SET price = price * 10 WHERE id = 2

事务B中包含如下语句:

UPDATE product SET price = price * 100 WHERE id = 2
UPDATE user SET name = '小枫' WHERE id = 1

如果这两个事务并发执行,那么他们可能存在如下的执行情况,当事务A执行的时候,首先运行了查询语句:

UPDATE user SET name = '小慕' where id = 1

相当于事务A给id为1的数据行加上了排他锁,但是事务并没有执行完也就是说此时事务A持有user表的id为1的排他锁,排他锁的特性就是此时其他事务不能对数据进行删除和修改,因此只有等待事务结束释放锁之后才能重新获取。

PostgreSQL出现死锁该如何解决

此时事务B执行更新语句获取了product表id为2的排他锁,接着事务B开始执行user表的update语句,需要获取user表的id为1的排他锁。但是此时事务A并未提交,因此事务A持有表user的id为1的排他锁,事务B只有乖乖阻塞等待事务A释放锁。而此时事务A执行update语句,需要获取product的id为2的排他锁,但是此时事务B持有该排他锁,因此也需要等待事务B锁释放。

UPDATE product SET price = price * 10 WHERE id = 2

PostgreSQL出现死锁该如何解决

事务A在等待事务B结束释放锁,而事务B又在等待事务A释放锁,最终陷入了互相等待的情况也就是所谓的死锁。

PostgreSQL出现死锁该如何解决

那么数据库出现死锁又会导致什么问题呢?数据库死锁会导致严重的性能问题,可能平台因为数据库死锁而导致运行缓慢,严重影响用户正常使用业务,因此如果出现数据库死锁情况需要及时发现以及解决。

定位死锁

//先确定数据库有没有死锁情况发生
select * from pg_stat_activity where datname = 'product_db';

//查询可能锁了的表的oid
select oid from pg_class where relname='product';

//查询对应的pid
select pid from pg_locks where relation='oid'  //上面查询出来的oid

//取消或者终止对应的进程破坏死锁条件
select pg_cancel_backend(pid);
select pg_terminate_backend(pid);

死锁可能原因及解决办法

以上分析了PostgreSQL出现死锁后如何定位分析,那么接下来就需要总结分析分析下PostgreSQL出现死锁情况的原因以及一般的应对解决办法。

1、索引使用不当导致的死锁问题

索引使用存在问题的话会导致死锁问题,假设在一个数据查询的事务当中,进行数据检索的时候没办法按照SQL中的where条件进行查询,因此导致了全表扫描,那么此时数据库表的行级锁会上升为表级锁。如果此时有多个未能按照where条件进行数据查询的事务存在,那么就容易导致数据库死锁问题。也就是说在数据库表数据量比较大的时候,对应进行数据查询的表没有建立索引或者说索引创建的不合理导致无法通过索引进行数据查询,只能通过全表索引,这样的场景下就容易产生死锁。

如何避免:

在进行数据查询的时候,对应的SQL语句不宜太过复杂,也就是说尽量避免多张表的关联查询。

2、不同事务之间的访问顺序问题

当用户A 访问数据库表A时,此时对表A加了共享锁,然后又访问数据库表B。而此时另一个用户B 访问表B,对表B加了共享锁,然后试图访问表A。但是用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,也就是说互相等待对方释放资源,从而导致了死锁的发生。

如何避免:

这种情况在实际项目中遇到的可能比较多,主要还是需要通过控制代码的执行逻辑,避免多表操作时同时锁住多个资源。

避免死锁的建议

(1)如果平台中存在大事务,尽量将其拆分为小事务。因为大事务一般操作的数据库表或者数据都比较多,因此造成死锁或者阻塞的概率就会相对较大。

(2)为数据库表设计合理的索引,尽量避免数据查询时索引未覆盖或者索引失效的情况,因为全表扫描会会导致给表中的数据行上锁,大大增加了数据库产生死锁的概率。

(3)如果业务允许,我们可以尝试将隔离级别调低,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。

(4)在我们自己的代码中,尽量以一致的顺序获取对象上的锁,避免事务中SQL交互执行,从而降低死锁发生的概率。

附:数据库中常见的死锁原因与解决方案

1. 事务之间对资源访问顺序的交替

出现原因: 

一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。

解决方法: 

这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源

2. 并发修改同一记录

出现原因:主要是由于没有一次性申请够权限的锁导致的。

用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁比较隐蔽,但在稍大点的项目中经常发生。 

解决方法:

a. 乐观锁,实现写-写并发

b. 悲观锁:使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

3. 索引不当导致的死锁

出现原因: 

如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。

另外一种情况是由于二级索引的存在,上锁的顺序不同导致的

解决方法:

SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

总结

到此这篇关于PostgreSQL出现死锁该如何解决的文章就介绍到这了!


Tags in this post...

PostgreSQL 相关文章推荐
Centos环境下Postgresql 安装配置及环境变量配置技巧
May 18 PostgreSQL
postgres之jsonb属性的使用操作
Jun 23 PostgreSQL
postgresql使用filter进行多维度聚合的解决方法
Jul 16 PostgreSQL
postgresql 删除重复数据案例详解
Aug 02 PostgreSQL
关于PostgreSQL JSONB的匹配和交集问题
Sep 14 PostgreSQL
基于PostgreSQL/openGauss 的分布式数据库解决方案
Dec 06 PostgreSQL
PostgreSQL13基于流复制搭建后备服务器的方法
Jan 18 PostgreSQL
Rust 连接 PostgreSQL 数据库的详细过程
Jan 22 PostgreSQL
PostgreSQL基于pgrouting的路径规划处理方法
Apr 18 PostgreSQL
PostgreSQL怎么创建分区表详解
Jun 25 PostgreSQL
PostgreSQL基于pgrouting的路径规划处理方法
Apr 18 #PostgreSQL
postgreSQL数据库基础知识介绍
PostgreSQL数据库去除重复数据和运算符的基本查询操作
PostgreSQL聚合函数介绍以及分组和排序
PostgreSQL 插入INSERT、删除DELETE、更新UPDATE、事务transaction
PostgreSQL数据库创建并使用视图以及子查询
PostgreSQL并行计算算法及参数强制并行度设置方法
Apr 07 #PostgreSQL
You might like
亚洲咖啡有什么?亚洲咖啡产地介绍 亚洲咖啡有什么特点?
2021/03/05 新手入门
php dirname(__FILE__) 获取当前文件的绝对路径
2011/06/28 PHP
强制PHP命令行脚本单进程运行的方法
2014/04/15 PHP
destoon设置自定义搜索的方法
2014/06/21 PHP
PHP下SSL加密解密、验证、签名方法(很简单)
2020/06/28 PHP
三种动态加载js的jquery实例代码另附去除js方法
2014/04/30 Javascript
JavaScript中实现sprintf、printf函数
2015/01/27 Javascript
jQuery取得iframe中元素的常用方法详解
2016/01/14 Javascript
JS实现动态表格的添加,修改,删除功能(推荐)
2016/06/15 Javascript
微信小程序获取用户openId的实现方法
2017/05/23 Javascript
JS库particles.js创建超炫背景粒子插件(附源码下载)
2017/09/13 Javascript
layui结合form,table的全选、反选v1.0示例讲解
2018/08/15 Javascript
JS实现简单的点赞与踩功能示例
2018/12/05 Javascript
vue跳转方式(打开新页面)及传参操作示例
2020/01/26 Javascript
原生JavaScript实现贪吃蛇游戏
2020/11/04 Javascript
Django URL传递参数的方法总结
2016/08/28 Python
python并发编程之线程实例解析
2017/12/27 Python
基于OpenCV python3实现证件照换背景的方法
2019/03/22 Python
Python django框架输入汉字,数字,字符生成二维码实现详解
2019/09/24 Python
计算pytorch标准化(Normalize)所需要数据集的均值和方差实例
2020/01/15 Python
Numpy(Pandas)删除全为零的列的方法
2020/09/11 Python
html5的canvas元素使用方法介绍(画矩形、画折线、圆形)
2014/04/14 HTML / CSS
某公司部分笔试题
2013/11/05 面试题
关于毕业的中学校园广播稿
2014/01/26 职场文书
教师自我反思材料
2014/02/14 职场文书
公司委托书范本
2014/04/04 职场文书
教师竞聘演讲稿
2014/05/16 职场文书
个人承诺书怎么写
2014/05/24 职场文书
停车位租赁协议书
2014/09/24 职场文书
教师遵守党的政治纪律情况对照检查材料
2014/09/26 职场文书
学生检讨书如何写
2014/10/30 职场文书
2015年护士医德医风自我评价
2015/03/03 职场文书
企业宣传语大全
2015/07/13 职场文书
医生行业员工的辞职信
2019/06/24 职场文书
分析JVM源码之Thread.interrupt系统级别线程打断
2021/06/29 Java/Android
Java数据结构之堆(优先队列)
2022/05/20 Java/Android