
# 锁
#### 锁简介
数据库对公共资源的并发控制是通过锁实现的，使用锁的一般流程操作可以简述为3步：加锁、临界区操作、放锁。
当对表进行DDL/DML操作时，数据库会对表进行加锁操作，在事务结束时释放。GaussDB提供了8个级别的锁分别用于不同语句的并发，各操作对应的锁以如下表所示：
表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                                 | 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 |
   
当两个事务的锁产生冲突时，未获取到锁的线程会等待锁。如果等待时间超过了系统设置的参数lockwait_timeout（默认20分钟），则会发生锁等待超时。其他与锁相关的参数见"配置运行参数 \> GUC参数说明 \> 锁管理"。
**锁示例：**
```
--建表并插入数据。
gaussdb=# CREATE TABLE testl1(c1 INT, c2 VARCHAR(5));
CREATE TABLE
gaussdb=# INSERT INTO testl1 VALUES (1,'a'),(2,'b'),(3,'c');
INSERT 0 3
--查看会话参数。
gaussdb=# SHOW lockwait_timeout;
 lockwait_timeout 
------------------
 20min
(1 row)
--在第一个会话中执行。
gaussdb=# BEGIN;
BEGIN
gaussdb=# INSERT INTO testl1 VALUES (4,'d');
INSERT 0 1
--在第二个会话中执行，该SQL会一直等到第一个会话结束才开始执行。
gaussdb=# CREATE INDEX idx_testl1_c1 ON testl1(c1);
--第一个会话中结束事务。
gaussdb=# END;
COMMIT
--删除表。
gaussdb=# DROP TABLE testl1;
DROP TABLE
```
#### 查询锁等待
```
--建表并插入数据。
gaussdb=# CREATE TABLE testl2(id INT,info VARCHAR(10));
CREATE TABLE
gaussdb=# INSERT INTO testl2 VALUES (1,'info1'),(2,'info2');
INSERT 0 2
--第一个会话中执行。
gaussdb=# BEGIN;
BEGIN
gaussdb=# CREATE INDEX idx_testl2_id ON testl2(id);
CREATE INDEX
--第二个会话中执行。 
gaussdb=# INSERT INTO testl2 VALUES (3,'info3');
```
- 打开一个新的会话，通过如下SQL查询出等待执行的SQL。
  ```
  gaussdb=# SELECT datname,pid,query,query_id,waiting FROM pg_stat_activity WHERE waiting = TRUE;
  datname  |       pid       |                 query                  |     query_id     | waiting 
  ----------+-----------------+----------------------------------------+------------------+---------
   postgres | 140389444876032 | INSERT INTO testl2 VALUES (3,'info3'); | 3940649678789642 | t
  (1 row)
  ```
  
- 通过pid查询等待事件
  ```
  gaussdb=# SELECT node_name,db_name,query_id,tid,wait_status,wait_event FROM pg_thread_wait_status WHERE tid = 140389444876032 AND db_name IS NOT NULL;
   node_name | db_name  |     query_id     |       tid       | wait_status  | wait_event 
  -----------+----------+------------------+-----------------+--------------+------------
   dn_6001   | postgres | 3940649678789642 | 140389444876032 | acquire lock | relation
  (1 row)
  ```
  
- 通过如下SQL查询出持锁SQL的pid
  ```
  --通过如下sql查询出持锁语句关联的pid， locktype和上面查询出的wait_event保持一致。
  gaussdb=# SELECT relation,pid,mode,granted FROM pg_locks WHERE pid = 140389444876032 AND locktype='relation';
   relation |       pid       |       mode       | granted 
  ----------+-----------------+------------------+---------
      26466 | 140389444876032 | RowExclusiveLock | f
  (1 row)
  --通过如下语句查询出持锁语句的pid， granted字段为t表示持锁语句。
  gaussdb=# SELECT relation,pid,mode,granted FROM pg_locks WHERE relation = 26466;
   relation |       pid       |       mode       | granted 
  ----------+-----------------+------------------+---------
      26466 | 140389444876032 | RowExclusiveLock | f
      26466 | 140389939934976 | AccessShareLock  | t
      26466 | 140389939934976 | ShareLock        | t
  (3 rows)
  ```
  
- 通过如下SQL可以查询出持锁语句的详细信息。
  如下信息显示该语句状态为"idle in transaction" 表示事务待提交。手动提交事务后，待执行的INSERT语句才会开始执行。
  ```
  gaussdb=# SELECT datname,pid,query,query_id,waiting,state FROM pg_stat_activity WHERE pid = 140389939934976;
   datname  |       pid       |                   query                   | query_id | waiting |        state        
  ----------+-----------------+-------------------------------------------+----------+---------+---------------------
   postgres | 140389939934976 | CREATE INDEX idx_testl2_id ON testl2(id); |        0 | f       | idle in transaction
  (1 row)
  ```
  根据实际情况决定是否通过pg_terminate_backend函数去对应的节点上结束持锁SQL的线程。
  示例：
  ```
  gaussdb=# SELECT pg_terminate_backend(140389939934976);
   pg_terminate_backend 
  ----------------------
   t
  (1 row)
  ```
  
 
