PostgreSQL 锁浅析
转载自:http://francs3.blog.163.com/blog/static/40576727201082134343604/ 此文档主要对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,Access exclusive”模式冲突,这种模式保护一张表不被并发的模式更改和VACUUM; “Vacuum(without full),Analyze ”和 “Create index concurrently”命令会获得这种类型锁。 2.5 SHARE 与“Row exclusive,Shared update exclusive,Share row exclusive,Access exclusive”锁模式冲突,这种模式保护一张表数据不被并发的更改; “Create index”命令会获得这种锁模式。 2.6 SHARE ROW EXCLUSIVE 与“Row exclusive,Share update exclusive,Shared,Shared row 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,and ACCESS EXCLUSIVE),这种模式保证了当前只有一个事务访问这张表; “ALTER TABLE,DROP TABLE,TRUNCATE,REINDEX,CLUSTER,VACUUM FULL” 命令会获得这种类型锁,在Lock table 命令中,如果没有申明其它模式,它也是缺省模式。 三、表级锁冲突模式
四、实验 在这一章节中将会对图一中比较典型的锁冲突进行模似演练,了解这些在Postgresql DBA的日常维护工作中很有帮助,同时也能减少人为故障的发生。 4.1 Access exclusive 锁与Access share锁冲突 在日常维护中, 大家应该执行过’ALTER TABLE’更改表结构的DDL,例如加字段,更改字段数据类型等,根据章节二的理论,在执行’ALTER TABLE’命令时将申请一个Access exclusive锁,根据图一,大家知道Access exclusive 锁和所有的锁模式都冲突,那么,它将会’Select’命令冲突,因为Select 加的是Access share锁,那么真的会与‘SELECT‘命令冲突吗,接下来给大家演示下:
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)
mydb=> begin;
BEGIN
mydb=> select * from test_2 where id=1; id | name ----+-------
1 | franc
(1 row)
注意:这里begin开始事务,没有提交;
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”锁。
注意,此时会话二还处于等侍状态 mydb=> end;
COMMIT
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,mode from pg_locks where relation='33802'; locktype | database | relation | pid | mode ----------+----------+----------+-----+------
(0 rows)
mydb=# select datname,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,Insert”命令获取的是Row Exclusive 操作,而这种操作在生产过程中非常频繁;这个实验正是模似生产维护过程。
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 操作放在一个事务里,注意此时事务尚未提交。
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,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,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
实验结论: 4.3 SHARE UPDATE EXCLUSIVE 与自身冲突 根据章节二,大家知道 VACUUM(Without full),Analyze 和 Create index (Concurently)操作会申请获得”Shared update Exclusive 锁”。根据图一,”Shared update Exclusive 锁”与本身也是会冲突的,下面实验验证一下:
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
注意: 表分析放在一个事务里,此时并没有提交;
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,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,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,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
mydb=> vacuum test_2; VACUUM
mydb=> select datname,current_query from pg_stat_activity where waiting='t'; datname | procpid | waiting | current_query ---------+---------+---------+---------------
(0 rows)
**实验结论:
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |