摘要:在现代数据库系统中,锁机制是确保数据一致性和完整性的关键手段。随着分布式数据库技术的发展,锁机制的复杂性和重要性也随之增加。GBase 8c作为一款多模多态的分布式数据库,其锁机制尤为重要,因为它不仅要处理本地事务的并发控制,还要协调跨节点的全局事务。
在现代数据库系统中,锁机制是确保数据一致性和完整性的关键手段。随着分布式数据库技术的发展,锁机制的复杂性和重要性也随之增加。GBase 8c作为一款多模多态的分布式数据库,其锁机制尤为重要,因为它不仅要处理本地事务的并发控制,还要协调跨节点的全局事务。
可能存在哪些锁?
在GBase 8c中,锁分为自旋锁、轻量级锁和常规锁三种类型。自旋锁适用于加锁时间非常短的场景,例如修改标志或读取标志字段,它只有锁定和解锁两种状态,且不会产生死锁。轻量级锁主要用于内部临界区操作比较久的场合,它有两种类型:共享锁和排它锁。共享锁允许多个进程同时获取,而排它锁只能被一个进程拥有。常规锁则用于业务访问的数据库对象加锁,遵守两阶段加锁协议,即访问过程中加锁,事务提交时释放锁。
死锁问题:死锁是指两个或多个事务在相互占有的资源上互相等待,导致所有事务都无法继续执行。在分布式环境中,死锁不仅可能发生在单个节点上,还可能发生在多个节点之间,形成全局死锁。
为了解决这一问题,GBase 8c具备全局死锁解除的能力。当发现死锁情况时,系统会将全局事务号最大的事务退出,从而解决死锁问题。这一机制通过在CN和DN上部署global deadlock process并与GTM建立通讯通道来实现。周期性地对事务等待关系进行检测,并将检测结果发送给GTM用来构建全局等待图。一旦检测到死锁环,立即将执行事务号最大的事务执行退出流程,从而解除全局死锁。本文将指导如何GBase8c分布式环境下通过当前执行SQL或通过表定位到锁是由哪些SQL产生,或者表是否有锁的情况。主要包括如下场景:
发现当前一个SQL执行很久都没返回数据,需要知道当前SQL是否被阻塞导致一直等待锁,如果是被阻塞需要找到锁的来源;
存储过程集成了很多SQL,需要知道具体执行在那一步,以及每个DN当前正在执行的操作,是否有等待产生;
查询具体的表或者SCHMA的锁的来源;
1、锁问题排查
排查过程可能涉及到的视图包括:
pg_stat_activity:显示和当前用户查询相关的信息,字段保存的是上一次执行的信息;
pg_locks:视图存储各打开事务所持有的锁信息;
pg_class:存储数据库对象信息及其之间的关系;
pg_namespace:存储名称空间,即存储schema相关的信息;
2、问题现象
(1)执行truncate表后一直等待,例如:
test=# truncate table stu;(2)delete 和update数据表出现等待一段时间后,出现超时
test=# update stu set age =10 where id当对表执行DML/DDL语句执行超时,或者一直不返回结果的情况下,可以针对该表查看持有锁的会话来源:
select * from pg_stat_activity where pid in(select l.pid from pg_namespace pinner join pg_class c on c.relnamespace=p.oidinner join pg_locks l on l.relation=c.oidwhere p.nspname='public'and c.relname='stu');其中:nspname 表示表所在的schema,relname表示表名称。
通过以上SQL发现pid为140508376790784的会话运行了update stu set age=10,并持有该表的排他锁,并且事务处于idle in trasaction(sql执行完成,但事务未提交)状态,导致其他会话DML/DDL不能执行。
处理步骤:
在生产环境或其他重要环境下,需要和业务部门核对运行该SQL的会话是否可以结束,当可以结束会话,可以直接kill该会话,同时该会话的事务也会自动回滚。
首先使用backend来结束会话,但只能结束active的会话
select pg_cancel_backend(pid);若backend不能结束会话的情况下,使用terminate来强制结束会话
select pg_terminate_backend(pid);结束会话后,再次查询持有该表的锁的会话,发现已经没有会话持有该表的锁。
再次执行DDL/DML语句,则可以正常执行
test=# update stu set age =10 where id注意:
在分布式场景下,因为有多个CN和DN,可能锁由其他节点产生,需要在所有CN节点都按此方法排查。
因为涉及元数据信息查询,需要登录到业务库执行该SQL,否则可能导致结果有误差;
4、批量语句中查看执行步骤
该场景主要是在CN上执行一些批量语句或者存储过程中有很多步骤需要查看当前存储过程执行到那一个步骤。在CN上查看只能看到当前存储过程正在执行中,需要查看具体执行到那个步骤需要在DN上查看。这里主要通过存储过程执行过程中排查为例。排查步骤如下:
(1)在CN上查询到执行存储过程的语句,同样也可以使用库名称,客户端ip,用户进行会话的过滤,并找到PID
select pid,* from pg_stat_activity where query like '%XXXX%';(2)使用上一步找出来的PID查询pg_locks找到全局事务号。注意:查询的全局事务号包含了节点名称,在分布式模式下只取:和#之间的数字串(如:0:818#0 事务号则是818)。
select global_sessionid,* from pg_locks where pid=XXX;(3)登录DN节点,通过CN查询出来的全局事务号,查询pg_locks找到PID
select pid,* from pg_locks where global_sessionid like '%XXX%';(4)通过DN的pg_locks查询出来的pid去pg_stat_activity过滤查询就可以定位到当前DN正在执行的SQL也就是当前存储过程执行的步骤
select * from pg_stat_activity where pid = XXX;GBase 8c通过多种锁机制和全局死锁解除功能,有效地解决了分布式数据库在海量数据和复杂应用场景中不可避免的并发控制问题,减少锁问题的发生,能够满足更高的性能和可靠性要求。
来源:小杨科技论