锁
锁简介
数据库对公共资源的并发控制是通过锁实现的,使用锁的一般流程操作可以简述为3步:加锁、临界区操作、放锁。
当对表进行DDL/DML操作时,数据库会对表进行加锁操作,在事务结束时释放。GaussDB提供了8个级别的锁分别用于不同语句的并发,各操作对应的锁以如下表所示:
| 
        锁级别  | 
      
        名称  | 
      
        用途  | 
      
        冲突关系  | 
     
|---|---|---|---|
| 
        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)