KingabseES的鎖機制--表級鎖
前言
在併發控制的過程中,事務隔離起著重要作用。事務隔離是控制如何和何時進行更改以及何時必須對彼此、使用者和系統可見的屬性。
KingabseES 通過多版本併發控制架構實現隔離。多版本併發控制是一種允許多個會話同時訪問同一記錄的技術,即,當會話 A 正在更新一條記錄時,會話 B 仍然可以訪問該記錄。
但是以下情況,應該怎麼辦:
- 如果會話 A 和會話 B 都想同時更新相同的記錄。
- 如果在會話 A 訪問表時,在會話 B 中試圖 truncate 表。
- 如果會話 A 正在更新表,而會話 B 試圖 vacuum 表。
這裡出現了鎖定的概念。
鎖機制
鎖機制在 KingbaseES 裡非常重要 (對於其他的 RDBMS 也是如此),特別是對於資料庫應用開發人員,高併發應用的開發人員必須熟悉。大部分資料異常情況,跟死鎖或者資料不一致有關係,基本上都是由於對鎖機制不太瞭解導致的。
鎖定型別取決於執行的命令型別。 KingabseES 支援三種鎖定機制:
- 表級鎖 ( Table-Level Locks )
- 行級鎖 ( Row-Level Locks )
- 建議性鎖 ( 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 中選擇獲取訪問共享鎖定的內容
(SESSION 1)# begin ;
BEGIN
(SESSION 1)# select *, pg_sleep(300) from acl ;
... -
嘗試從會話 2 中截斷表
(SESSION 2)# begin;
BEGIN
(SESSION 2)# truncate table acl;
..
會話 1 獲取 AccessShareLock ,以獲取記錄。
會話 2 想要 truncate 表,必須使用 AccessExclusiveLock 模式鎖,但由於鎖衝突,正在等待獲取。
ACCESS SHARE LOCK與ACCESS 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 UPDATE 和 SELECT FOR SHARE 命令在目標表上獲取此模式的鎖。
EXCLUSIVE 與 *ACCESS EXCLUSIVE *鎖定模式衝突 。
-
從會話 1 中選擇獲取訪問共享鎖定的內容
(SESSION 1)# begin ;
BEGIN
(SESSION 1)# select *, pg_sleep(300) from acl for update ;
... -
從會話 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)、ANALYZE、CREATE INDEX CONCURRENTLY、CREATE STATISTICS 和 ALTER TABLE VALIDATE 以及其他少數幾個 ALTER TABLE 命令獲取共享更新獨佔鎖。
與 SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS 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 SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE | |
ACCESS SHARE | X | |||||||
ROW SHARE | X | X | ||||||
ROW EXCLUSIVE | X | X | X | X | ||||
SHARE UPDATE EXCLUSIVE | X | X | X | X | X | |||
SHARE | X | X | X | X | X | |||
SHARE ROW EXCLUSIVE | X | X | X | X | X | X | ||
EXCLUSIVE | X | X | X | X | X | X | X | |
ACCESS EXCLUSIVE | X | X | X | X | X | X | X | X |
從上圖可以得出的結論是
- 兩個事務不能同時在同一張表上持有衝突模式的鎖。
例如,如果存在正在進行的訪問共享鎖,則另一個會話無法獲得獨佔訪問。
- 許多事務可以同時持有非衝突鎖定模式。
例如,根據上圖,行共享鎖與行獨佔鎖不衝突,因此它們可以由多個事務/會話一次持有。
- 一些鎖定模式是自衝突的
例如,一個ACCESS EXCLUSIVE 鎖一次不能被多個事務持有
- 雖然有些鎖定模式,不會自衝突
例如,一個ACCESS SHARE鎖可以由多個事務持有。
下一篇,繼續介介紹 KingbaseES 行級鎖。
KINGBASE研究院