
# 锁等待检测
#### 操作场景
在日常作业开发中，数据库事务管理中的锁一般指的是**表级锁** ，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锁级别及冲突关系 
| 锁等级 | 名称                                | 用途                                                  | 冲突关系                                 |
|:---|:---|:---|:---|
| 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 |
   
#### 操作步骤
**构造锁等待场景：**
1. 打开一个新的连接会话，使用普通用户u1连接DWS数据库，在自己的同名SCHEMA u1下创建测试表u1.test。 
   ```
   CREATE TABLE test (id int, name varchar(50));
   ```
   
   
2. 开启事务1，进行INSERT操作。 
   ```
   START TRANSACTION;
   INSERT INTO test VALUES (1, 'lily');
   ```
   
   
3. 打开一个新的连接会话，使用系统管理员dbadmin连接DWS数据库，执行VACUUM FULL操作，发现语句阻塞。 
   ```
   VACUUM FULL u1.test;
   ```
   
   
**锁等待检测（8.1.x及以上版本）**
1. 打开一个新的连接会话，使用系统管理员dbadmin连接DWS数据库，通过pgxc_lock_conflicts视图查看锁冲突情况。
   
   如下图，回显中查看granted字段为"f"，表示VACUUM FULL语句正在等待其他锁。granted字段为"t"，表示INSERT语句是持有锁的。nodename，表示锁产生在的位置，即CN或DN位置，例如cn_5001。
   ```
   SELECT * FROM pgxc_lock_conflicts;
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001369354124.png "点击放大")
   
   
2. 根据语句内容确认是否中止持锁语句。如果终止，则执行以下语句。*pid* 从[1]获取，cn_5001为上面查询到的nodename。
   
   ```
   execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001476317005.png "点击放大")
   
   
**锁等待检测（8.0.x及以前版本）**
1. 在数据库中执行以下语句，获取VACUUM FULL操作对应的query_id。
   
   ```
   SELECT * FROM pgxc_stat_activity WHERE query LIKE '%vacuum%'AND waiting = 't';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001420513901.png "点击放大")
   
   
2. 根据获取的query_id，执行以下语句查看是否存在锁等待，并获取对应的tid。其中，*{query_id}* 从[1]获取。
   
   ```
   SELECT * FROM pgxc_thread_wait_status WHERE query_id = {query_id};
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001420475085.png "点击放大")
   回显中"wait_status"存在"acquire lock"表示存在锁等待。同时查看"node_name"显示在对应的CN或DN上存在锁等待，记录相应的CN或DN名称，例如cn_5001或dn_600x_600y。
   
   
3. 执行以下语句，到等锁的对应CN或DN上通过查询pg_locks系统表查看VACUUM FULL操作在等待哪个锁。以下以cn_5001为例，如果在DN上等锁，则改为相应的DN名称。pid为[2]获取的tid。
   
   回显中记录relation的值。
   ```
   execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE pid = {tid} AND granted = ''f''';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001420673809.png "点击放大")
   
   
4. 根据获取的relation，通过查询pg_locks系统表查看当前持有锁的pid。*{relation}* 从[3]获取。
   
   ```
   execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE relation = {relation} AND granted = ''t''';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001369354136.png "点击放大")
   
   
5. 根据pid，执行以下语句，查到对应的SQL语句。*{pid}* 从[4]获取。
   
   ```
   execute direct on (cn_5001) 'SELECT query FROM pg_stat_activity WHERE pid={pid}';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001369833772.png "点击放大")
   
   
6. 根据语句内容确认是中止持锁语句还是待持锁语句结束再重新执行VACUUM FULL。如果终止，则执行以下语句。*pid* 从[4]获取。
   
   中止结束后，再尝试重新执行VACUUM FULL。
   ```
   execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)';
   ```
   ![](https://support.huaweicloud.com/trouble-dws/figure/zh-cn_image_0000001369513860.png "点击放大")
   
   
 
