1. 程式人生 > 其它 >KingabseES的鎖機制--表級鎖

KingabseES的鎖機制--表級鎖

目錄

前言

在併發控制的過程中,事務隔離起著重要作用。事務隔離是控制如何和何時進行更改以及何時必須對彼此、使用者和系統可見的屬性。

KingabseES 通過多版本併發控制架構實現隔離。多版本併發控制是一種允許多個會話同時訪問同一記錄的技術,即,當會話 A 正在更新一條記錄時,會話 B 仍然可以訪問該記錄。

但是以下情況,應該怎麼辦:

  1. 如果會話 A 和會話 B 都想同時更新相同的記錄。
  2. 如果在會話 A 訪問表時,在會話 B 中試圖 truncate 表。
  3. 如果會話 A 正在更新表,而會話 B 試圖 vacuum 表。

這裡出現了鎖定的概念。

鎖機制

鎖機制在 KingbaseES 裡非常重要 (對於其他的 RDBMS 也是如此),特別是對於資料庫應用開發人員,高併發應用的開發人員必須熟悉。大部分資料異常情況,跟死鎖或者資料不一致有關係,基本上都是由於對鎖機制不太瞭解導致的。

鎖定型別取決於執行的命令型別。 KingabseES 支援三種鎖定機制:

  1. 表級鎖 ( Table-Level Locks )
  2. 行級鎖 ( Row-Level Locks )
  3. 建議性鎖 ( Advisory Locks )

表級和行級的鎖可以是顯式的也可以是隱式的,建議性鎖一般是顯式的。顯式的鎖由顯式的使用者請求(通過特殊的查詢)獲取,隱式的鎖是通過標準的 SQL 命令來獲取。

  • 隱式鎖意味著當事務結束時鎖會預設關閉。
  • 顯式鎖一旦獲得,可能會一直保持到顯式釋放。我們可以使用該WITH LOCK 語句顯式獲取鎖。

除了表級和行級的鎖,還有頁級共享/排除鎖,用於控制對共享快取池裡表頁的訪問。在一行資料被讀取或者更新後,這些鎖會立即被釋放。應用程式開發者通常不需要關注頁級的鎖。

表級鎖 ( Table-Level Locks )

表級鎖通過內建 SQL 命令獲取(隱式);此外,它們可以通過 LOCK 命令顯式獲取。表級鎖包括:

  • 訪問共享(ACCESS SHARE) - SELECT 命令可在查詢中引用的表上獲得該鎖。一般規則是所有的查詢中只有讀表才獲取此鎖。
  • 行共享(ROW SHARE) - SELECT FOR UPDATE 和 SELECT FOR SHARE 命令可在目標表上獲得該鎖(以及查詢中所有引用的表的訪問共享鎖)。
  • 行獨佔(ROW EXCLUSIVE) - UPDATE、INSERT 和 DELETE 命令在目標表上獲得該鎖(以及查詢中所有引用的表的訪問共享鎖)。 一般規則是所有修改表的查詢獲得該鎖。
  • 共享更新獨佔(SHARE UPDATE EXCLUSIVE) - VACUUM(不含FULL),ANALYZE,CREATE INDEX CONCURRENTLY,和一些 ALTER TABLE 的命令獲得該鎖。
  • 共享(SHARE) - CREATE INDEX 命令在查詢中引用的表上獲得該鎖。
  • 共享行獨佔(SHARE ROW EXCLUSIVE) - 不被任何命令隱式獲取。
  • 排他(EXCLUSIVE) - 這個鎖模式在事務獲得此鎖時只允許讀取操作並行。它不能由任何命令隱式獲取。
  • 訪問獨佔(ACCESS EXCLUSIVE) - ALTER TABLE,DROP TABLE,TRUNCATE,REINDEX,CLUSTER 和 VACUUM FULL 命令在查詢中引用的表上獲得該鎖。此鎖模式是 LOCK 命令的預設模式。

訪問共享(ACCESS SHARE)

訪問共享鎖是由只從表中讀取但不修改它的查詢獲取的。通常,這是一個選擇查詢。

隱式鎖定示例:

  1. 從會話 1 中選擇獲取訪問共享鎖定的內容

    (SESSION 1)# begin ;
    BEGIN
    (SESSION 1)# select *, pg_sleep(300) from acl ;
    ...

  2. 嘗試從會話 2 中截斷表
    (SESSION 2)# begin;
    BEGIN
    (SESSION 2)# truncate table acl;
    ..

會話 1 獲取 AccessShareLock ,以獲取記錄。

會話 2 想要 truncate 表,必須使用 AccessExclusiveLock 模式鎖,但由於鎖衝突,正在等待獲取。

ACCESS SHARE LOCKACCESS EXCLUSIVE鎖定模式衝突。

這時檢視鎖的資訊,可以得到了兩個鎖。看到 AccessShareLock 的 granted 值是 true ,而 AccessExclusiveLock 的 granted 值是 false。

kingbase=#  \! ksql -c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE  relation = 'acl'::regclass; "
pid  | vxid | lock_type |      lock_mode      | granted | relname
------+------+-----------+---------------------+---------+---------
7244 | 6/9  | relation  | AccessShareLock     | t       | acl
7364 | 3/12 | relation  | AccessExclusiveLock | f       | acl
(2 rows)

查詢鎖和鎖的PID

kingbase=# \! ksql -c "SELECT locked.pid AS locked_pid, locker.pid AS locker_pid, locked_act.usename AS locked_user, locker_act.usename AS locker_user, locked.transactionid, relname FROM pg_locks locked LEFT OUTER JOIN pg_class ON (locked.relation = pg_class.oid), pg_locks locker, pg_stat_activity locked_act, pg_stat_activity locker_act WHERE locker.granted = true AND locked.granted = false AND locked.pid = locked_act.pid AND locker.pid = locker_act.pid AND locked.relation = locker.relation;"
 locked_pid | locker_pid | locked_user | locker_user | virtualtransaction |  relname
------------+------------+-------------+-------------+--------------------+---------------
       7364 |       7244 | kingbase    | kingbase    | 3/12               |  acl
(1 row)

顯式鎖定示例

kingbase=# begin;
BEGIN
kingbase=# LOCK TABLE emp IN ACCESS SHARE MODE;
LOCK TABLE
kingbase=#  \! ksql -c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE  relation = 'emp'::regclass; "
 pid  | vxid | lock_type |    lock_mode    | granted | relname
------+------+-----------+-----------------+---------+------------------------------------
6020 | 4/8  | relation  | AccessShareLock | t       | emp
(1 rows)

kingbase=#

行共享(ROW SHARE)

SELECT FOR UPDATESELECT FOR SHARE 命令在目標表上獲取此模式的鎖。

EXCLUSIVE 與 *ACCESS EXCLUSIVE *鎖定模式衝突 。

  1. 從會話 1 中選擇獲取訪問共享鎖定的內容

    (SESSION 1)# begin ;
    BEGIN
    (SESSION 1)# select *, pg_sleep(300) from acl for update ;
    ...

  2. 從會話 2 檢視鎖和鎖的PID資訊

    kingbase=# ! ksql -c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE relation = 'acl'::regclass; "

     pid  | vxid | lock_type |  lock_mode   | granted | relname
     ------+------+-----------+---------------------+---------+---------
     7691 |      | relation  | RowShareLock | t       | acl
     (1 rows)
    

行獨佔( ROW EXCLUSIVE )

命令 UPDATE、 DELETE和 INSERT在目標表上獲取此鎖定模式。

SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE等鎖模式,與 ACCESS EXCLUSIVE 鎖模式衝突 。

隱式鎖定示例

例子

(SESSION 1)#begin;
BEGIN
(SESSION 1)#select * from acl;
 id | sno | name | sal | dept
----+-----+------+-----+-------
 1 | 1 | A | 200 | IT
 2 | 2 | B | 200 | IT
 3 | 3 | C | 300 | SALES
(3 rows)

(SESSION 1)#insert into acl values(4,4,'D',400,'IT');
INSERT 0 1
(SESSION 1)#

現在,這個會話 1 獲得了一個行獨佔鎖。

從會話 2 開始,嘗試更改表。

(SESSION 2)#alter table acl drop dept;

會話 2 將等待會話 1 釋放鎖,因為 alter table drop column 需要 ACCESS EXCLUSIVE 鎖,這與 ROW EXCLUSIVE 鎖衝突。

kingbase=#  \! psql -c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE  relation = 'acl'::regclass; "
 pid  |  vxid  | lock_type |      lock_mode      | granted | relname
------+--------+-----------+---------------------+---------+----------+---------
8038 | 4/1364 | relation  | AccessShareLock     | t       | acl
8038 | 4/1364 | relation  | RowExclusiveLock    | t       | acl
8058 | 3/14   | relation  | AccessExclusiveLock | f       | acl
(3 rows)

執行 pg_stat_activity 以找到 PID,

kingbase=# select pid, wait_event_type, wait_event, query from pg_stat_activity;
-[ RECORD 3 ]---+---------------------------------------------------------------
pid             | 8058
wait_event_type | Lock
wait_event      | relation
query           | alter table acl drop dept;

現在,根據 PID 檢視 pg_locks 資訊

kingbase=# select locktype, relation, virtualxid, transactionid, mode, granted from pg_locks where pid='8058';
   locktype    | relation | virtualxid | transactionid |        mode         | granted
---------------+----------+------------+---------------+---------------------+---------
virtualxid    |          | 3/14       |               | ExclusiveLock       | t
transactionid |          |            |           671 | ExclusiveLock       | t
relation      |    32803 |            |               | AccessExclusiveLock | f
(3 rows)

顯式鎖定示例

(SESSION 1)#begin;
BEGIN
(SESSION 1)#lock table acl IN ROW EXCLUSIVE MODE;
LOCK TABLE
(SESSION 1)#\! psql -c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE  relation = 'acl'::regclass; "
 pid  |  vxid  | lock_type |    lock_mode     | granted | relname
------+--------+-----------+------------------+---------+---------
 8509 | 4/1394 | relation  | RowExclusiveLock | t       | acl
(1 row)

共享更新獨佔(SHARE UPDATE EXCLUSIVE)

VACUUM(非 FULL)ANALYZECREATE INDEX CONCURRENTLYCREATE STATISTICSALTER TABLE VALIDATE 以及其他少數幾個 ALTER TABLE 命令獲取共享更新獨佔鎖。

SHARE UPDATE EXCLUSIVESHARE、SHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE 鎖模式衝突。此模式保護表,避免併發模式更改和 VACUUM 執行。

隱式鎖定示例

例子

(SESSION 1)#vacuum pgbench_accounts;
..
..

獲得的鎖是

kingbase=#  SELECT locktype,transactionid,virtualtransaction,mode FROM pg_locks ;
  locktype  | transactionid | virtualtransaction |           mode
------------+---------------+--------------------+--------------------------
 relation   |               | 3/180              | RowExclusiveLock
 virtualxid |               | 3/180              | ExclusiveLock
 relation   |               | 5/379              | AccessShareLock
 virtualxid |               | 5/379              | ExclusiveLock
 relation   |               | 3/180              | ShareUpdateExclusiveLock
(5 rows)

可以使用 lock_timeout 來避免等待鎖。
s
lock_timeout 將不會在指定時間內獲得訪問權,如果發生超時則等待鎖的會話斷開。

SET lock_timeout TO '2s';

顯式鎖定示例

(SESSION 1)#begin;
BEGIN
(SESSION 1)#LOCK TABLE acl IN SHARE UPDATE EXCLUSIVE MODE ;
LOCK TABLE
(SESSION 1)#

鎖的資訊是

kingbase#  \! psql --c "select pid, virtualxid vxid, locktype lock_type, mode lock_mode, granted, relation::regclass relname from pg_locks WHERE  relation = 'acl'::regclass; "
 pid  | vxid  | lock_type |        lock_mode         | granted | relname
 ------+-------+-----------+--------------------------+---------+---------
8719 | 3/185 | relation  | ShareUpdateExclusiveLock | t       | acl
(1 row)

共享(SHARE)

由 CREATE INDEX (無 CONCURRENTLY)獲得共享鎖。

CREATE INDEX 的非併發版本使用 ShareLock 防止表更新,例如 DROP TABLE 或 INSERT 或 DELETE。

ROW EXCLUSIVE與, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, 和 ACCESS EXCLUSIVE lock 模式衝突 。此模式保護表免受併發資料更改的影響。

隱式鎖定示例

(SESSION 1)#create index abalance_ind on pgbench_accounts(balance);
...
...

鎖的資訊是

kingbase=#  SELECT locktype,transactionid,virtualtransaction,mode FROM pg_locks ;
   locktype    | transactionid | virtualtransaction |        mode
---------------+---------------+--------------------+---------------------
 virtualxid    |               | 3/196              | ExclusiveLock
 relation      |               | 5/382              | AccessShareLock
 virtualxid    |               | 5/382              | ExclusiveLock
 relation      |               | 3/196              | ShareLock
 transactionid |           737 | 3/196              | ExclusiveLock
 relation      |               | 3/196              | AccessExclusiveLock
(6 rows)

顯式鎖定示例

(SESSION 1)#begin;
BEGIN
(SESSION 1)#SELECT * FROM acl FOR SHARE;
 id | sno | name | sal | dept
----+-----+------+-----+-------
  1 |   1 | A    | 200 | IT
  2 |   2 | B    | 200 | IT
  3 |   3 | C    | 300 | SALES
  4 |   4 | D    | 400 | IT
(4 rows)

(SESSION 1)#

kingbase=#  \! psql -f implicit.sql
 pid  | vxid  | lock_type |  lock_mode   | granted | xid_lock | relname
------+-------+-----------+--------------+---------+----------+---------
 8719 | 3/197 | relation  | RowShareLock | t       |          | acl
(1 row)

共享行獨佔(SHARE ROW EXCLUSIVE)

由 CREATE COLLATION, CREATE TRIGGER和多種ALTER TABLE 獲得共享行獨佔鎖。

ROW EXCLUSIVE與, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, 和 ACCESS EXCLUSIVE lock 模式衝突 。此模式可保護表免受併發資料更改的影響,並且是自排斥的,因此一次只能有一個會話保持它。

顯式鎖定示例

(SESSION 1)#BEGIN ;
BEGIN
(SESSION 1)#LOCK TABLE acl IN SHARE ROW EXCLUSIVE MODE;
LOCK TABLE
(SESSION 1)#

kingbase=#  \! psql -f implicit.sql
 pid  | vxid  | lock_type |       lock_mode       | granted | xid_lock | relname
------+-------+-----------+-----------------------+---------+----------+---------
 8719 | 3/198 | relation  | ShareRowExclusiveLock | t       |          | acl
(1 row)

排他(EXCLUSIVE)

由 REFRESH MATERIALIZED VIEW CONCURRENTLY 獲得排他鎖。

ROW SHARE與, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, 和 ACCESS EXCLUSIVE lock 模式衝突 。這種模式只允許併發 ACCESS SHARE 鎖,即只從表中讀取,可以與持有這種鎖模式的事務並行進行。

隱式鎖定示例

kingbase=# REFRESH MATERIALIZED VIEW CONCURRENTLY pgbench_accounts_mv WITH DATA;
..
..
kingbase=#  SELECT locktype,transactionid,virtualtransaction,mode FROM pg_locks ;
   locktype    | transactionid | virtualtransaction |        mode
---------------+---------------+--------------------+---------------------
 relation      |               | 4/18               | AccessShareLock
 relation      |               | 4/18               | AccessShareLock
 virtualxid    |               | 4/18               | ExclusiveLock
 relation      |               | 3/10               | AccessShareLock
 virtualxid    |               | 3/10               | ExclusiveLock
 relation      |               | 4/18               | AccessExclusiveLock
 transactionid |           747 | 4/18               | ExclusiveLock
 relation      |               | 4/18               | AccessShareLock
 relation      |               | 4/18               | ExclusiveLock
(9 rows)

顯式鎖定示例

kingbase=# begin;
BEGIN
kingbase=# LOCK TABLE acl IN  EXCLUSIVE MODE;
LOCK TABLE
kingbase=#
kingbase=# \! psql -f implicit.sql
 pid  | vxid | lock_type |   lock_mode   | granted | xid_lock | relname
------+------+-----------+---------------+---------+----------+---------
 2611 | 4/20 | relation  | ExclusiveLock | t       |          | acl
(1 row)

訪問獨佔(ACCESS EXCLUSIVE)

由 DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL, 和 REFRESH MATERIALIZED VIEW (不帶 CONCURRENTLY)命令獲取訪問獨佔鎖。也有很多形式, ALTER TABLE 在這個級別獲取鎖。這也是 LOCK TABLE 未明確指定模式的語句的預設鎖定模式

與所有模式的鎖衝突(ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, 和 ACCESS EXCLUSIVE)。這種模式保證持有者是唯一以任何方式訪問表的事務。

隱式鎖定示例

kingbase=# vacuum full pgbench_accounts;
..
..
kingbase=#  SELECT locktype,transactionid,virtualtransaction,mode FROM pg_locks ;
   locktype    | transactionid | virtualtransaction |        mode
---------------+---------------+--------------------+---------------------
 virtualxid    |               | 4/22               | ExclusiveLock
 relation      |               | 3/12               | AccessShareLock
 virtualxid    |               | 3/12               | ExclusiveLock
 relation      |               | 4/22               | AccessExclusiveLock
 transactionid |           749 | 4/22               | ExclusiveLock
 relation      |               | 4/22               | AccessExclusiveLock
(6 rows)

顯式鎖定示例

kingbase=# begin;
BEGIN
kingbase=# LOCK TABLE acl IN ACCESS EXCLUSIVE MODE;
LOCK TABLE

kingbase=# \! psql -f implicit.sql
pid | vxid | lock_type | lock_mode | granted | xid_lock | relname
------+------+-----------+---------------------+---------+----------+---------
2611 | 4/19 | relation | AccessExclusiveLock | t | | acl
(1 row)

下圖描述了鎖定模式的衝突。

請求的鎖模式當前的鎖模式
ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE
ACCESS SHAREX
ROW SHAREXX
ROW EXCLUSIVEXXXX
SHARE UPDATE EXCLUSIVEXXXXX
SHAREXXXXX
SHARE ROW EXCLUSIVEXXXXXX
EXCLUSIVEXXXXXXX
ACCESS EXCLUSIVEXXXXXXXX

從上圖可以得出的結論是

  • 兩個事務不能同時在同一張表上持有衝突模式的鎖

例如,如果存在正在進行的訪問共享鎖,則另一個會話無法獲得獨佔訪問。

  • 許多事務可以同時持有非衝突鎖定模式

例如,根據上圖,行共享鎖與行獨佔鎖不衝突,因此它們可以由多個事務/會話一次持有。

  • 一些鎖定模式是自衝突的

例如,一個ACCESS EXCLUSIVE 鎖一次不能被多個事務持有

  • 雖然有些鎖定模式,不會自衝突

例如,一個ACCESS SHARE鎖可以由多個事務持有。


下一篇,繼續介介紹 KingbaseES 行級鎖。

KINGBASE研究院