沉沙
2018-09-25
来源 :
阅读 1827
评论 0
摘要:本篇教程探讨了大数据技术 greenplum在执行vacuum和insert产生死锁问题定位及解决方案,希望阅读本篇文章以后大家有所收获,帮助大家对大数据技术的理解更加深入。
本篇教程探讨了大数据技术 greenplum在执行vacuum和insert产生死锁问题定位及解决方案,希望阅读本篇文章以后大家有所收获,帮助大家对大数据技术的理解更加深入。
<
使用自己编译的开源版本的greenplum数据库用于学习,版本为PostgreSQL 8.3.23 (Greenplum Database 4.3.99.00 build dev) on x86_64-unknown-linux-gnu, compiled by GCC gcc (GCC) 4.8.5 20150623
在使用的过程中遇到不少的问题,今天记录一下高并发的情况下,执行insert和vacuum操作造成的死锁,以及解决方案
一、问题描述:
在对ao分区表进行高并发测试的时候同时执行了vacuum的动作,发现会有死锁的问题产生,可以通过如下手段复现:
1)多个线程循环执行insert into t3 select * from XX(t3为本例中用于测试的分区表),为了提高问题复现的速度,可以仅对表中的一个分区进行操作
2)单个线程循环执行vacuum的动作
一段时间后会发现vacuum和insert都卡住,至此问题被复现出来。
二、结论:
该问题仅在AO分区表中会出现,产生的原因是由于数据库加锁的流程设计不合理导致(详情见分析过程)。
三、问题定位及解决方案
抛砖引玉,仅分享自己知道的一点点东西,如果有错误的地方还请指正,欢迎大家一起来讨论这个问题。
1)首先连接master执行:
select * from pg_stat_activity;
查到如下结果:
(图一)
发现有一个session是lock状态,为了弄清楚这里的lock的具体情况,则连接master和standby查看锁的具体情况,执行如下sql:
select a.locktype,b.relname,substring(c.current_query,1,50),c.xact_start,a.pid,a.mode,a.granted from pg_locks a,pg_class b,pg_stat_activity c
where a.relation = b.oid and a.pid = c.procpid and relname like 't3%';
master:
(图二)
segment1:
(图三)
segment2:
(图四)
需要注意的是,这里图三和图四中在pg_stat_activity表中的current_query一列有
(图五)
官方的文档里,对该值仅有这么一小段描述,这里并不能完全的表达出这个字段的意思,我们来看源码:
(图六)
源码里就可以看出这种状态说明该连接的事务是处于阻塞状态。
那么又是为什么会有锁等待的出现呢,因为同一时间,针对同一个对象,有些锁是不能够同时被不同的事务所持有的,如果一个事务持有了某个锁,另一个事务需要获取相同的锁或者是与这个锁冲突的锁,就会出现等待的情况,我们来看下锁之间的冲突情况:
(图七)
综合以上的所有信息,画出了如下的图:
(图七)
说明:
相同颜色的色块表示同一个连接(在物理机上,由ps -ef | grep $pid查到的con确定),红色的线表示等待关系。
由上图可以清晰的看出,在master上,con52等待con49持有的锁,而在segment2中,con49等待con52持有的锁,因此产生了死锁。
那么又是什么造成了这种锁的状态的形成?官方的描述中AccessShareLock仅仅是在对表数据不产生任何影响的查询语句才会申请:
(图八)
这里看图七的master节点,明明产生的是AccessShareLock,与官方的描述出现了不一致,对于这个疑惑,只有源码能告诉我们答案:
(图九)
这里有个if条件,这说明在处理ao分区表的时候,会在处理完成后会在master上加上AccessShareLock,结合图七和图九,也就解释了为什么在master会产生insert的锁等待vacuum的情况了,同时也回答了为什么结论中说该问题仅会出现在ao分区表中。
至此,产生这个问题的原因基本明晰了,那么遇到了这种情况该如何解决?
要知道不同的锁之间谁等待了谁,提供如下sql,很方便的就能知道等待关系:
?12345678910111213141516171819202122232425262728293031323334353637383940414243create or replace function f_lock_level(i_mode text) returns integer as $$begin RETURN (select case i_mode when 'INVALID' then 0 when 'AccessShareLock' then 1 when 'RowShareLock' then 2 when 'RowExclusiveLock' then 3 when 'ShareUpdateExclusiveLock' then 4 when 'ShareLock' then 5 when 'ShareRowExclusiveLock' then 6 when 'ExclusiveLock' then 7 when 'AccessExclusiveLock' then 8 else 0 end );end;$$ language plpgsql strict; --查询with t_wait as (select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.current_query,b.xact_start,b.query_start,b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.procpid and not a.granted),t_run as(select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a,transactionid,b.current_query,b.xact_start,b.query_start,b.usename,b.datname from pg_locks a,pg_stat_activity b where a.pid=b.procpid and a.granted)select r.locktype,r.mode r_mode,r.usename r_user,r.datname r_db,r.relation::regclass,r.pid r_pid,r.page r_page,r.tuple r_tuple,r.xact_start r_xact_start,r.query_start r_query_start,now()-r.query_start r_locktime,r.current_query r_query,w.mode w_mode,w.pid w_pid,w.page w_page,w.tuple w_tuple,w.xact_start w_xact_start,w.query_start w_query_start,now()-w.query_start w_locktime,w.current_query w_query from t_wait w,t_run r where r.locktype is not distinct from w.locktype and r.database is not distinct from w.database and r.relation is not distinct from w.relation and r.page is not distinct from w.page and r.tuple is not distinct from w.tuple and r.classid is not distinct from w.classid and r.objid is not distinct from w.objid and r.objsubid is not distinct from w.objsubid and r.transactionid is not distinct from w.transactionid and r.pid <> w.pid order by f_lock_level(w.mode)+f_lock_level(r.mode) desc,r.xact_start;
注意:上面的sql适用于文章开头部分greenplum内核版本,不同的版本会有些许差异,这个sql是我根据文章最后参考资料德哥给出的SQL自己修改的,如果您的环境中无法运行,请使用德哥给出的语句。
该sql会返回类似如下的结果(部分):
(图十)
每条结果以w_开头的结果表示正在等待的会话信息,以r_开头的代表正在运行的会话信息。
为了不影响正常的插入流程,可以找到vacuum语句的pid,使用select pg_terminate_backend($pid);语句终止vacuum的会话,业务即可继续进行。
那么问题是不是就此可以结束?其实并没有,我们来看官方文档:
官方指出,在发生死锁的情况下,会自动的回滚一个事务,保证另一个事务的正常运行,那么在上面所述的情况下,为什么没有发生事务的自动回滚?这是我一直没有想明白的问题,如果有相关的专业人士看到这篇文章请指点一二,解答我心中的疑问,感激不尽!
本文由职坐标整理发布,学习更多的大数据技术相关知识,请关注职坐标大技术云计算大技术技术频道!
喜欢 | 0
不喜欢 | 0
您输入的评论内容中包含违禁敏感词
我知道了

请输入正确的手机号码
请输入正确的验证码
您今天的短信下发次数太多了,明天再试试吧!
我们会在第一时间安排职业规划师联系您!
您也可以联系我们的职业规划师咨询:
版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
沪公网安备 31011502005948号