更新时间:2025-09-19 GMT+08:00
分享

锁简介

数据库通过锁对公共资源进行并发控制,使用锁的一般流程操作可以简述为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));
NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'c1' as the distribution column by default.
HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
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));
NOTICE:  The 'DISTRIBUTE BY' clause is not specified. Using 'id' as the distribution column by default.
HINT:  Please use 'DISTRIBUTE BY' clause to specify suitable data distribution column.
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)
    
    --删除表。
    gaussdb=# DROP TABLE testl2;
    DROP TABLE

相关文档