锁等待检测
操作场景
在日常作业开发中,数据库事务管理中的锁一般指的是表级锁,DWS中支持的锁模式有8种,按排他级别分别为1~8。每种锁模式都有与之相冲突的锁模式,由锁冲突表定义相关的信息,锁冲突表如表1所示。
举例:用户u1对某张表test执行INSERT事务时,此时持有RowExclusiveLock锁;此时用户u2也对test表进行VACUUM FULL事务,则该事务与INSERT事务产生锁冲突,处于锁等待状态。
常用的锁等待检测主要通过查询视图pgxc_lock_conflicts、pgxc_stat_activity、pgxc_thread_wait_status、pg_locks进行。其中pgxc_lock_conflicts视图在8.1.x版本后支持,根据集群版本号不同,检测方式不同。
| 锁等级 | 名称 | 用途 | 冲突关系 |
|---|---|---|---|
| 1 | 访问共享锁(AccessShareLock) | SELECT | 8 |
| 2 | 行共享锁(RowShareLock) | SELECT FOR UPDATE/FOR SHARE | 7 | 8 |
| 3 | 行排他锁(RowExclusiveLock) | INSERT/UPDATE/DELETE | 5 | 6 | 7 | 8 |
| 4 | 共享更新排他锁(ShareUpdateExclusiveLock) | VACUUM、ANALYZE、CREATE INDEX CONCURRENTLY、COMMENT ON | 4 | 5 | 6 | 7 | 8 |
| 5 | 共享锁(ShareLock) | CREATE INDEX | 3 | 4 | 6 | 7 | 8 |
| 6 | 共享行排他锁(ShareRowExclusiveLock) | ROW SELECT...FOR UPDATE | 3 | 4 | 5 | 6 | 7 | 8 |
| 7 | 排他锁(ExclusiveLock) | BLOCK ROW SHARE/SELECT...FOR UPDATE | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 8 | 访问排他锁(AccessExclusiveLock) | DROP CLASS/VACUUM FULL | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
操作步骤
构造锁等待场景:
- 打开一个新的连接会话,使用普通用户u1连接DWS数据库,在自己的同名SCHEMA u1下创建测试表u1.test。
1CREATE TABLE test (id int, name varchar(50));
- 开启事务1,进行INSERT操作。
1 2
START TRANSACTION; INSERT INTO test VALUES (1, 'lily');
- 打开一个新的连接会话,使用系统管理员dbadmin连接DWS数据库,执行VACUUM FULL操作,发现语句阻塞。
1VACUUM FULL u1.test;
锁等待检测(8.1.x及以上版本)
- 打开一个新的连接会话,使用系统管理员dbadmin连接DWS数据库,通过pgxc_lock_conflicts视图查看锁冲突情况。
如下图,回显中查看granted字段为“f”,表示VACUUM FULL语句正在等待其他锁。granted字段为“t”,表示INSERT语句是持有锁。nodename,表示锁产生在的位置,即CN或DN位置,例如cn_5001。
1SELECT * FROM pgxc_lock_conflicts;

- 据语句内容确认是否中止持锁语句。如果终止,则执行以下语句。pid从1获取,cn_5001为上面查询到的nodename。
1execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)';

锁等待检测(8.0.x及以前版本)
- 在数据库中执行以下语句,获取VACUUM FULL操作对应的query_id。
1SELECT * FROM pgxc_stat_activity WHERE query LIKE '%vacuum%'AND waiting = 't';

- 根据获取的query_id,执行以下语句查看是否存在锁等待,并获取对应的tid。其中,{query_id}从1获取。
1SELECT * FROM pgxc_thread_wait_status WHERE query_id = {query_id};

回显中“wait_status”存在“acquire lock”表示存在锁等待。同时查看“node_name”显示在对应的CN或DN上存在锁等待,记录相应的CN或DN名称,例如cn_5001或dn_600x_600y。
- 执行以下语句,到等锁的对应CN或DN上通过查询pg_locks系统表查看VACUUM FULL操作在等待哪个锁。以下以cn_5001为例,如果在DN上等锁,则改为相应的DN名称。pid为2获取的tid。
回显中记录relation的值。
1execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE pid = {tid} AND granted = ''f''';

- 根据获取的relation,通过查询pg_locks系统表查看当前持有锁的pid。{relation}从3获取。
1execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE relation = {relation} AND granted = ''t''';

- 根据pid,执行以下语句,查到对应的SQL语句。{pid}从4获取。
1execute direct on (cn_5001) 'SELECT query FROM pg_stat_activity WHERE pid={pid}';

- 根据语句内容确认是中止持锁语句还是待持锁语句结束再重新执行VACUUM FULL。如果终止,则执行以下语句。pid从4获取。
中止结束后,再尝试重新执行VACUUM FULL。
1execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)';
