
GaussDB常规锁简介
GaussDB 从轻量到重量定义了三种锁:
l spinLock(自旋锁),系统级共享资源的封锁操作
l LWLock(轻量锁):系统级共享资源的封锁操作,在系统运行期间,系统级的资源需要加锁,操作后,被释放。
l RegularLock(常规锁/重量锁):用于并发保护用户表的数据。
常规锁按照被锁对象按照类型可以分为10种:relation,extend,page,tuple,transactionid,virtualxid,object,userlock,advisory,pg_locks视图中第一列locktype的取值就是这些。下面我们主要介绍常见的表锁(relation)和行锁(tuple)。
1 表锁
当对表进行DDL/DML 操作时,数据库会对表进行加锁操作,在事务结束时释放。常规锁按照粒度可以分为8个等级,各操作对应的锁以及相容性如下表所示:
当两个事务的锁产生冲突时,未拿到锁的线程会等锁,等锁超过系统设置参数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
2 行锁
2.1 行锁模式
GaussDB不支持for key share和for no key update模式的行级锁,支持以下两种模式:
l For share: 使用select for share语句时持有该模式锁,后台会对tuple加5级锁(ShareLock);
l For update: 使用select for update, delete, update等操作时持有该模式的锁,后台会对tuple加7级锁(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个事务,A、B、C依次对同一行修改,均未提交:
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锁。如果B和C分别锁超时,那么超时报错信息如下所示:
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
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就是