GaussDB常规锁简介

举报
大鱼炖海棠 发表于 2020/12/25 10:16:29 2020/12/25
【摘要】  GaussDB 从轻量到重量定义了三种锁:l  spinLock(自旋锁),系统级共享资源的封锁操作l  LWLock(轻量锁):系统级共享资源的封锁操作,在系统运行期间,系统级的资源需要加锁,操作后,被释放。l  RegularLock(常规锁/重量锁):用于并发保护用户表的数据。常规锁按照被锁对象按照类型可以分为10种:relation,extend,page,tuple,transa...


 GaussDB 从轻量到重量定义了三种锁:

l  spinLock(自旋锁),系统级共享资源的封锁操作

l  LWLock(轻量锁):系统级共享资源的封锁操作,在系统运行期间,系统级的资源需要加锁,操作后,被释放。

l  RegularLock(常规锁/重量锁):用于并发保护用户表的数据。

常规锁按照被锁对象按照类型可以分为10种:relationextendpagetupletransactionidvirtualxidobjectuserlockadvisorypg_locks视图中第一列locktype的取值就是这些。下面我们主要介绍常见的表锁(relation)和行锁(tuple)。

1 表锁

当对表进行DDL/DML 操作时,数据库会对表进行加锁操作,在事务结束时释放。常规锁按照粒度可以分为8个等级,各操作对应的锁以及相容性如下表所示:

image.png

         当两个事务的锁产生冲突时,未拿到锁的线程会等锁,等锁超过系统设置参数lockwait_timeout(默认值20min)就会报错。报错信息会将持锁语句等信息打印出来,例如:

ERROR:  Lock wait timeout: thread 140354461361920 on node   coordinator1 waiting for AccessShareLock on relation 16655 of database 14764   after 1200.057 s

DETAIL:  blocked by hold lock thread 140354804238080,   statement <drop table aa;>, hold lockmode AccessExclusiveLock.

2 行锁

2.1 行锁模式

GaussDB不支持for key sharefor no key update模式的行级锁,支持以下两种模式:

l  For share: 使用select for share语句时持有该模式锁,后台会对tuple5级锁(ShareLock);

l  For update: 使用select for update, delete, update等操作时持有该模式的锁,后台会对tuple7级锁(ExclusiveLock),根据上图中各级锁的相容性可知,并发更新同一条语句时会产生行锁冲突;

2.2 并发更新参数

allow_concurrent_tuple_update=false时,并发更新同一条记录不会等锁,直接报错:

abort transaction due to concurrent   update

allow_concurrent_tuple_update=true时,并发更新同一条记录会产生锁冲突。等锁超过系统设置参数update_lockwait_timeout(默认值2min)就会报错。报错信息可以分为几种,最重要的标志就是超时时间。

2.3 行锁加锁流程

行锁等到事务提交才会释放,其他事物如果等待这个行锁,必须等待这个事务锁释放。tuple锁可以保证多个修改事务加锁的顺序问题,原则是先来先拿锁,修改完tuple后,tuple锁会立即释放,而事务锁不会释放。假设有3个事务,ABC依次对同一行修改,均未提交:

Session A

Session  B

Session  C

begin;

update t set b=1;




set max_query_retry_times=0;

begin;

update t set b=2;




set update_lockwait_timeout='1s';

set max_query_retry_times=0;

begin;

update t set b=3;

此时A处于idle in transaction状态,B持有tuple锁但是等待A的事务锁,C等待B持有的tupe锁。如果BC分别锁超时,那么超时报错信息如下所示:

C 先报错:

ERROR:    Lock wait timeout: thread 139728477222656 on node datanode3 waiting   for ExclusiveLock on tuple (0,1) of relation 33287 of database 14194 after   2000.096 ms

DETAIL:    blocked by hold lock thread 139728418502400, statement <UPDATE   public.t SET b = 2>, hold lockmode ExclusiveLock.

 

B报错:

ERROR:    Lock wait timeout: thread 139728477222656 on node datanode3 waiting   for ShareLock on transaction 24858 after 120018.206 ms

DETAIL:    blocked by hold lock thread 139728691128064, statement < update t   set b=1; >, hold lockmode ExclusiveLock.

注释:

1. 如果此时A事务中又执行了一条select * from t;那么B的报错信息中statement就是<select * from t;>

2. 对于列存表,行锁可以理解为对一个cu的锁,因此,即时表面上更新的数据不是同一行,也可能出现等锁的情况。

2.4 其他报错案例

如果持锁事务残留或者正处于提交状态,detail信息可能是

ERROR:  dn_6005_6006: Lock wait timeout: thread 140354804238080 on node dn_6001_6002   waiting for ShareLock on transaction 867484 fter 12000.453ms

DETAIL:    blocked by hold lock thread 0, statement <pending twophase   transaction>, hold lockmode (null).

此时可以根据事务号查询是否有残留事务:

Select * from pg_prepared_xacts;

如果确认残留,调用gs_clean清理残留事务即可。

3 如何查询锁等待

在没有锁等待超时报错的情况下,我们通常会结合pgxc_stat_activity, pgxc_thread_wait_statusm pg_locks等视图来定位锁等待问题,这种方法过于繁琐。我们可以采用以下两种简单方式之一来快速定位锁等待问题:

方法一:

通过设置参数来达到提前报错的目的,具体可以参考案例:

https://bbs.huaweicloud.com/forum/thread-82664-1-1.html

方法二:

         使用附件提供的工具查询锁等待情况.使用方法:

1. 创建存储过程

2. 在cn上执行

SELECT * FROM pgxc_locks_wait;


4 锁超时处理办法

现场规避:通过以上方法,找到持锁线程,在对应的实例上执行以下sql杀掉持锁线程

Select pg_terminate_backend(xxxxx);   --xxxxx为找到的持锁线程

长期手段:从业务侧避免锁冲突行为。

 

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。