資料庫併發事務控制四:postgresql資料庫的鎖機制
在博文《資料庫併發事務控制四:postgresql資料庫的鎖機制 》
http://blog.csdn.net/beiigang/article/details/43302947
中後面提到:
常規鎖機制可以參考pg的官方手冊,章節和內容見下面
13.3. Explicit Locking
http://www.postgresql.org/docs/9.4/static/explicit-locking.html
這節分為:表鎖、行鎖、頁鎖、死鎖、Advisory鎖(這個名字怎麼翻譯好??? 忠告鎖,公告鎖,諮詢鎖???)。
後面的內容提綱就是:
表鎖、
行鎖、
頁鎖、
死鎖、
Advisory鎖(這個名字怎麼翻譯好??? 忠告鎖,公告鎖,諮詢鎖???)、
感覺有些累了,懶了,不想寫了,後面看情況到網上找些同仁同志同學朋友們的文章,放到這兒歸類,如有需要待下次按我的思路再整理組織,今次先把題目做完整吧。
這兒繼續,下面的內容轉自下面,只是小改了幾處手誤的地方,把第三節的圖換成了官網上的
http://francs3.blog.163.com/blog/static/40576727201082134343604/
原文標題:Postgresql 鎖淺析
一、概述
此文件主要對Postgresql 鎖機制進行分析,在講解的過程中結合實驗,理解Postgresql的鎖機制。
二、表級鎖型別
表級鎖型別分為八種,以下對各種表級鎖型別進行簡單介紹下, 鎖的衝突模式可以參考3.1的圖一:表級鎖衝突模式。
2.1 ACCESS SHARE
“ACCESS SHARE”鎖模式只與“ACCESS EXCLUSIVE” 鎖模式衝突;
查詢命令(Select command)將會在它查詢的表上獲取”Access Shared” 鎖,一般地,任何一個對錶上的只讀查詢操作都將獲取這種型別的鎖。
2.2 ROW SHARE
“Row Share” 鎖模式與”Exclusive’和”Access Exclusive”鎖模式衝突;
”Select for update”和”Select for share”命令將獲得這種型別鎖,並且所有被引用但沒有 FOR UPDATE 的表上會加上”Access shared locks”鎖。
2.3 ROW EXCLUSIVE
“Row exclusive” 與 “Share,Shared roexclusive,Exclusive,Access exclusive”模式衝突;
“Update,Delete,Insert”命令會在目標表上獲得這種型別的鎖,並且在其它被引用的表上加上”Access shared”鎖,一般地,更改表資料的命令都將在這張表上獲得”Row exclusive”鎖。
2.4 SHARE UPDATE EXCLUSIVE
”Share update exclusive,Share,Share row ,exclusive,exclusive,Access exclusive”模式衝突,這種模式保護一張表不被併發的模式更改和VACUUM;
“Vacuum(without full), Analyze ”和 “Create index concurrently”命令會獲得這種型別鎖。
2.5 SHARE
與“Row exclusive,Shared update exclusive,Share row exclusive ,Exclusive,Access exclusive”鎖模式衝突,這種模式保護一張表資料不被併發的更改;
“Create index”命令會獲得這種鎖模式。
2.6 SHARE ROW EXCLUSIVE
與“Row exclusive,Share update exclusive,Shared,Shared row exclusive,Exclusive,Access Exclusive”鎖模式衝突;
任何Postgresql 命令不會自動獲得這種鎖。
2.7 EXCLUSIVE
與” ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE”模式衝突,這種索模式僅能與Access Share 模式併發,換句話說,只有讀操作可以和持有”EXCLUSIVE”鎖的事務並行;
任何Postgresql 命令不會自動獲得這種型別的鎖;
2.8 ACCESS EXCLUSIVE
與所有模式鎖衝突(ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE),這種模式保證了當前只有一個事務訪問這張表;
“ALTER TABLE, DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL” 命令會獲得這種型別鎖,在Lock table 命令中,如果沒有申明其它模式,它也是預設模式。
三、表級鎖衝突模式
3.1 Conflicting lock modes
Table 13-2. Conflicting Lock Modes
Requested Lock Mode | Current Lock Mode | |||||||
---|---|---|---|---|---|---|---|---|
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 |
備註: 上圖是pg 表級鎖的各種衝突模式對照表,‘X’表示衝突項, 在章節四中會對其中典型的鎖模式進行模似演示。
四、實驗
在這一章節中將會對圖一中比較典型的鎖衝突進行模似演練,瞭解這些在Postgresql DBA的日常維護工作中很有幫助,同時也能減少人為故障的發生。
4.1 Access exclusive 鎖與Access share鎖衝突
在日常維護中,大家應該執行過’ALTER TABLE’更改表結構的DDL,例如加欄位,更改欄位資料型別等,根據章節二的理論,在執行’ALTER TABLE’命令時將申請一個Access exclusive鎖, 根據圖一,大家知道Access exclusive 鎖和所有的鎖模式都衝突,那麼,它將會’Select’命令衝突,因為Select 加的是Access share鎖,那麼真的會與‘SELECT‘命令衝突嗎,接下來給大家演示下:
--建立一張測試表 test_2 並插入測試資料
mydb=> create table test_2 (id integer,name varchar(32));
CREATE TABLE
mydb=> insert into test_2 values (1,'franc');
INSERT 0 1
mydb=> insert into test_2 values (2,'tan');
INSERT 0 1
mydb=> select * from test_2;
id | name
----+-------
1 | franc
2 | tan
(2 rows)
--會話一 查詢表資料 ( 這裡獲得Access Shared 鎖)
mydb=> begin;
BEGIN
mydb=> select * from test_2 where id=1;
id | name
----+-------
1 | franc
(1 row)
注意:這裡begin開始事務,沒有提交;
--會話二 更改表結構 (這裡申請 Access Exclusive鎖 )
mydb=> alter table test_2 add column sex char(1);
發現,命令一直等侍,執行不下去;
--會話三 查詢狀態
mydb=# select oid,relname from pg_class where relname='test_2';
oid | relname
-------+---------
33802 | test_2
mydb=# select locktype,database,relation,pid,mode from pg_locks where relation='33802';
locktype | database | relation | pid | mode
----------+----------+----------+-------+---------------------
relation | 16466 | 33802 | 18577 | AccessShareLock
relation | 16466 | 33802 | 18654 | AccessExclusiveLock
mydb=# select datname,procpid,usename,current_query from pg_stat_activity where procpid in (18577,18654);
datname | procpid | usename | current_query
---------+---------+---------+--------------------------------------------
mydb | 18577 | skytf | <IDLE> in transaction
mydb | 18654 | skytf | alter table test_2 add column sex char(1);
(2 rows)
這裡可以看出會話一(pid=18577) 獲取的是 “AccessShareLock”鎖,
會話二(pid=18654 ) 獲取的是 “AccessExclusiveLock”鎖。
--再次回到會話一,執行'end'結束事務後會發生什麼結果
注意,此時會話二還處於等侍狀態
mydb=> end;
COMMIT
--回到會話二發現 原來處於等侍狀態的'ALTER TABLE'命令執行成功
mydb=> alter table test_2 add column sex char(1);
ALTER TABLE
mydb=> \d test_2
Table "skytf.test_2"
Column | Type | Modifiers
--------+-----------------------+-----------
id | integer |
name | character varying(32) |
sex | character(1) |
--回到會話三,鎖已經釋放
mydb=# select locktype,database,relation,pid,mode from pg_locks where relation='33802';
locktype | database | relation | pid | mode
----------+----------+----------+-----+------
(0 rows)
mydb=# select datname,procpid,usename,client_addr,current_query from pg_stat_activity where procpid in (18577,18654);
datname | procpid | usename | client_addr | current_query
---------+---------+---------+-------------+---------------
mydb | 18577 | skytf | | <IDLE>
mydb | 18654 | skytf | | <IDLE>
(2 rows)
實驗說明: 這個實驗說明了 'ALTER TABLE'命令與'SELECT'命令會產生衝突,證實了開始的結論,即"Access exclusive"鎖模式與申請"Access shared"鎖模式的'SELECT'命令相沖突。
4.2 Share 鎖與 Row Exclusive 鎖衝突
在資料庫的維護過程中,建立索引也是經常做的工作,別小看建立索引,如果是一個很繁忙的系統,索引不一定能建立得上,可能會發生等侍, 嚴重時造成系統故障;根據章節二的理論,’Create Index’ 命令需要獲取Share 鎖模式。
根據圖一,”Share” 鎖和”Row Exclusive”鎖衝突,下面來驗證一下:
根據圖一可以看出,share鎖模式和多種鎖模式衝突,有可能會問我,為什麼單獨講share鎖和Row Exclusive衝突呢?因為” Update,Delete,Insert”命令獲取的是Row Exclusive 操作,而這種操作在生產過程中非常頻繁;這個實驗正是模似生產維護過程。
--會話一, 向 test_2 上插入一條資料
mydb=> select * from test_2;
id | name | sex
----+-------+-----
1 | franc |
2 | tan |
(2 rows)
mydb=> begin;
BEGIN
mydb=> insert into test_2 values (3,'fpzhou');
INSERT 0 1
mydb=>
說明: 這個Insert 操作放在一個事務裡,注意此時事務尚未提交。
--會話二,在表test_2上建立索引
mydb=> \d test_2;
Table "skytf.test_2"
Column | Type | Modifiers
--------+-----------------------+-----------
id | integer |
name | character varying(32) |
sex | character(1) |
mydb=> create unique index idx_test_2_id on test_2 (id);
說明: 建立索引命令發生等侍
--會話三,查詢狀態
mydb=# select locktype,database,relation,pid,mode from pg_locks where relation='33802';
locktype | database | relation | pid | mode
----------+----------+----------+-------+------------------
relation | 16466 | 33802 | 18577 | RowExclusiveLock
relation | 16466 | 33802 | 18654 | ShareLock
(2 rows)
mydb=# select datname,procpid,usename,client_addr,current_query from pg_stat_activity where procpid in (18577,18654);
datname | procpid | usename | client_addr | current_query
---------+---------+---------+-------------+----------------------------------------------------
mydb | 18577 | skytf | | <IDLE> in transaction
mydb | 18654 | skytf | | create unique index idx_test_2_id on test_2 (id);
說明: 這裡可以看出"Insert into"(procpid=18577) 命令獲取"RowExclusiveLock”,而"Create Index"(procpid=18654)操作獲取的是"Sharelock", 並且建立索引操作發了等侍,因為這兩種鎖模式是衝突的。
--回到會話一,提交事務,看看會發生什麼
注意,此時建立索引的會話二仍處於等侍狀態
mydb=> end;
COMMIT
--回到會話二,發現建立索引命令成功,等侍消失
mydb=> create unique index idx_test_2_id on test_2 (id);
CREATE INDEX
實驗結論:1 上述實驗說明 "Create index "操作和"Insert"操作衝突;也就是 "Share"鎖和"RowExclusive"鎖衝突。
2 在生產庫上應該避免在業務高峰期執行新建索引操作,因為如果在張大表上新建索引,消耗時間較長,在這個過程中會阻塞業務的DML操作。
4.3 SHARE UPDATE EXCLUSIVE 與自身衝突
根據章節二,大家知道 VACUUM(Without full), Analyze 和 Create index (Concurently)操作會申請獲得”Shared update Exclusive 鎖”。根據圖一,”Shared update Exclusive 鎖”與本身也是會衝突的,下面實驗驗證一下:
--會話一,分析表test_2
mydb=> select * from test_2;
id | name | sex
----+--------+-----
1 | franc |
2 | tan |
3 | fpzhou |
(3 rows)
mydb=>
mydb=>
mydb=> begin;
BEGIN
mydb=> analyze test_2;
ANALYZE
注意: 表分析放在一個事務裡,此時並沒有提交;
--會話二 對錶 test_2 做 vacuum
mydb=> \d test_2;
Table "skytf.test_2"
Column | Type | Modifiers
--------+-----------------------+-----------
id | integer |
name | character varying(32) |
sex | character(1) |
Indexes:
"idx_test_2_id" UNIQUE, btree (id)
mydb=> vacuum test_2;
注意: 當對錶 test_2 執行 vacuum操作時,操作等侍,
--會話三,觀察系統哪裡鎖住了
[postgres@pg1 ~]$ psql -d mydb
psql (9.0beta3)
Type "help" for help.
mydb=# select datname,procpid,waiting,current_query from pg_stat_activity where waiting='t';
datname | procpid | waiting | current_query
---------+---------+---------+----------------
mydb | 20625 | t | vacuum test_2;
(1 row)
這裡說明會話 vacuum test_2 在等侍
mydb=# select oid,relname from pg_class where relname='test_2';
oid | relname
-------+---------
33802 | test_2
(1 row)
mydb=# select locktype,database,relation,pid,mode from pg_locks where relation='33802';
locktype | database | relation | pid | mode
----------+----------+----------+-------+--------------------------
relation | 16466 | 33802 | 20625 | ShareUpdateExclusiveLock
relation | 16466 | 33802 | 20553 | ShareUpdateExclusiveLock
(2 rows)
說明: 這裡可以看出 'Analyze'操作 (pid=20553) 和'Vacuum'操作 (pid=20625)都是加的"ShareUpdateExclusiveLock"。
mydb=# select datname,procpid,waiting,current_query from pg_stat_activity where procpid in (20625,20553);
datname | procpid | waiting | current_query
---------+---------+---------+-----------------------
mydb | 20553 | f | <IDLE> in transaction
mydb | 20625 | t | vacuum test_2;
(2 rows)
說明: 結束上面查詢可以看出會話20625在等侍會話20553,也就是說"vacuum test_2" 被事務堵住了,
--再次回到會話一,提交會話,注意此時會話二處於等侍姿態;
mydb=> end;
COMMIT
--再次回到會話二,發現 vacuum命令執行下去了,等侍消失。
mydb=> vacuum test_2;
VACUUM
mydb=> select datname,procpid,waiting,current_query from pg_stat_activity where waiting='t';
datname | procpid | waiting | current_query
---------+---------+---------+---------------
(0 rows)
實驗結論 1 Analyze 和 Vacuum 操作都會申請獲得 "ShareUpdateExclusiveLock"。
2 ShareUpdateExclusiveLoc與ShareUpdateExclusiveLock是衝突的。
轉自 http://blog.csdn.net/beiigang/article/details/43339327