GaussDB(DWS)表级锁功能实操与思考
本人原来主要接触TD,表级锁的分类、场景等跟GaussDB(DWS)的锁类型有较大区别。TD的表级锁类型较少,GaussDB(DWS)的锁类型更细分一些。
此处对GaussDB(DWS)各种类型的锁都实操一下,加深学习。
锁类型
下图是各类型锁与互斥的关系
从锁的名称我们能大致将锁分为两类:SHARE锁、EXCLUSIVE锁。
这两类型锁的核心思想是,SHARE是多事务共享的,EXCLUSIVE是单事务独享的。根据不同场景分了一下8种锁类型。同时根据锁的相关性做了分类
ACCESS锁访问相关
ACCESS SHARE
SELECT命令在被引用的表上获得一个这种模式的锁,也就是说每当读取表的数据时,都会上一个ACCESS SHARE锁。只与ACCESS EXCLUSIVE锁冲突,也就是说除了DDL语句等,没有别的事务能阻止ACCESS SHARE锁下的读数据动作
ACCESS EXCLUSIVE
ACCESS EXCLUSIVE与所有锁类型冲突,这个模式保证其所有者(事务)是可以访问该表的唯一事务。
ALTER TABLE,DROP TABLE,TRUNCATE,REINDEX,CLUSTER,VACUUM FULL命令会自动请求这种锁。
在LOCK TABLE命令没有明确声明需要的锁模式时,它是缺省锁模式。
ACCESS SHARE与ACCESS EXCLUSIVE锁冲突例子:
--SESSION 1 begin; --对表locka加上ACCESS EXCLUSIVE锁 lock table locka in access exclusive mode; --查询locka的数据 select * from locka;
--SESSION 2 begin; --对表locka加上ACCESS SHARE锁,此处会一直等待SESSION 1的ACCESS EXCLUSIVE锁,直到达到死锁超时。此处换成其他类型的锁结果都一样 lock table locka in access share mode;
ROW行锁
ROW SHARE
SELECT FOR UPDATE和SELECT FOR SHARE命令会自动在目标表上请求ROW SHARE锁(且所有被引用但不是FOR SHARE/FOR UPDATE的其他表上,还会自动加上ACCESS SHARE锁)。
SELECT FOR UPDATE是对行加锁的语句,执行后指定行在其他事务中只能读;而其他行能够正常的增删改查。
表级加ROW SHARE锁后,行级也有锁的例子:
--SESSION 1 begin; --select for update语句自动会加上ROW SHARE select * from locka where col1 = 1 for update;
--SESSION 2 begin; --加上 row share lock lock table locka in row share mode; --查询session 1中指定行 select * from locka where col1 = 1; --查询其他行 select * from locka where col1 = 3; --执行对其他行的DML语句 delete locka where col1 = 3; --执行session 1中一样的行加锁语句,会等待session 1的事务结束,若执行对该行的增删改,也会发生冲突等待,因为该行已被sesion1加上了排他锁。 select * from locka where col1 = 1 for update;
增删改操作的锁
ROW EXCLUSIVE
UPDATE,DELETE,INSERT命令会自动在目标表上请求这个锁(且所有被引用的其他表上还会自动加上的ACCESS SHARE锁)。通常情况下,所有会修改表数据的命令都会请求表的ROW EXCLUSIVE锁。
SHARE UPDATE EXCLUSIVE
VACUUM(不带FULL选项),ANALYZE,CREATE INDEX CONCURRENTLY命令会自动请求这样的锁。
从锁冲突来看,不能够两个事务同时对同一个表进行VACUUM(不带FULL选项),ANALYZE等操作;一个表在进行DML操作时能够进行VACUUM和ANALYZE操作。下面两个例子展示ROW EXCLUSIVE与SHARE UPDATE EXCLUSIVE两种锁的使用
例子1,增删改时能操作VACUUM与ANALIZE:
--SESSION 1 begin; --插入操作 insert into locka values (8);
--SESSION 2 在session 1插入数据后 analyze locka; --vacuum vacuum locka; --查询当前locka的锁是否ROW EXCLUSIVE锁 select a.relname,b.mode from pg_class a inner join pg_locks b on a.oid = b.relation where relname = 'locka';
例子2,确认ANALYZE与VACUUM是SHARE UPDATE EXCLUSIVE锁;
--SESSION 1 begin; --给表上exclusive锁 lock table locka in exclusive mode;
--SESSION 2 --因为ANALYZE与VACUUM不能在显示事务中执行,下面就直接执行看,然后在另外的终端看后台的锁类型 analyze locka; vacuum locka;
--SESSION 3 --在另外的终端看后台的锁类型 select a.relname,b.mode from pg_class a inner join pg_locks b on a.oid = b.relation where relname = 'locka';
在查看vacuum的锁类型时,发现一个问题,就是vacuum显示的是只有AccessShareLock,并没有ShareUpdateExclusiveLock。同时如果只是ACCESSSHARE锁类型的话,那Exclusive锁是不会与之冲突,说明这里面有问题,不知道是pg_locks上显示的问题还是内部的问题了。
SHARE锁与EXCLUSIVE锁
SHARE
SHARE锁之间不冲突、SHARE锁对增删改的ROW EXCLUSIVE冲突。也就是说,两个事务持有SHARE锁,都不能再该事务内增删改,但是如果只有一个事务持有,那该事务是可以增删改的,因为锁冲突起码要有两个事务才行。
CREATE INDEX(不带CONCURRENTLY选项)语句会自动请求这种锁。
SHARE ROW EXCLUSIVE
此锁跟SHARE锁唯一区别是,与同样的锁会冲突,也就是说只能有一个事务能持有该锁,SHARE锁也不能加上。
任何SQL语句都不会自动请求这个锁模式。
EXCLUSIVE
EXCLUSIVE锁顾名思义就是独占了某个表,除了持有ACCESS SHARE锁的事务能访问该表。也就是说,只有对表的读动作可以和持有这个锁模式的事务并发执行。
任何SQL语句都不会在用户表上自动请求这个锁模式。然而在某些操作的时候,会在某些系统表上请求它。
例子:两事务加上SHARE锁后,不能对表增删改:
--SESSION 1 begin; --对表locka加上share锁 lock table lcoka in share mode; --等session 2上锁后,执行以下查询,预估执行成功 select * from locka;
--SESSION 2 begin; --对表locka加上share锁 lock table locka in share mode; --等session 1上锁后,执行以下查询,预估执行成功 select * from locka; --执行完session 1后,执行以下插入操作 insert into locka values (2);
--SESSION 3 --在另外的终端看后台的锁类型 select a.relname,b.mode from pg_class a inner join pg_locks b on a.oid = b.relation where relname = 'locka';
总结
各种SQL操作会自动加持的锁类型,如下表
锁类型 | SQL操作 |
AccessShareLock | SELECT等访问表数据相关语句,如insert select等语句也会对源头表加持该锁 |
RowShareLock | SELECT FOR UPDATE、SELECT FOR SHARE等加持行级锁时,对该表加的锁 |
RowExclusiveLock | 增删改(INSERT、DELETE、UPDATE)时自动对目标表加持的锁类型 |
ShareUpdateExclusiveLock | VACUUM(不带FULL选项),ANALYZE命令会自动请求这样的锁。其中Vacuum在实验时从pg_locks的记录上不持有该类型锁,但表象是持有的。 |
ShareLock | CREATE INDEX语句会自动请求这种锁。 |
ShareRowExclusiveLock | 任何SQL语句都不会自动请求这个锁模式 |
ExclusiveLock | 任何SQL语句都不会自动请求这个锁模式 |
AccessExclusiveLock | ALTER TABLE,DROP TABLE,TRUNCATE,REINDEX,CLUSTER,VACUUM FULL命令会自动请求这种锁 |
我们重温一下上面锁冲突图片,并总结一般场景如下:
1、事务A在操作表结构,如ALTER TABLE,DROP TABLE,TRUNCATE,VACUUM FULL时,其他事务要操作该表只能等待该事务结束
2、事务A在建索引时(CREATE INDEX),其他事务没办法进行增删改操作;能两个事务同时给一个表进行建索引操作。
3、事务A在对表进行增删改操作时,能同时在另外的事务中进行VACUUM,ANALYZE操作,互不影响;但具体VACUUM与ANALYZE会因为增删改的改动导致执行结果不理想,因此我认为这两者还是不要并发执行。
4、虽然增删改的锁在表级锁层面不冲突,但操作某些行时,行上会有行级锁。例如事务A删除行1,事务B删除行1,两个事务结束前,必然有一方等待另一方结束。
5、事务A在查询表A,除非表A在进行DDL与VACUUM FULL等加持ACCESS EXCLUSIVE的锁,不然都能查询表A的数据。
与TD数据库锁的比较思考:
TD的表级锁只有4种,ACCESS、READ、WRITE、EXCLUSIVE。是不考虑并发对一个表进行增删改的。
这个我觉得是因为TD绝大部分都是AP的SQL,没有这样的TP场景,这样的4种锁能满足绝大部分的AP场景,所以TD的开发者在考虑锁的问题的时候就将表级锁简化为4种。
GaussDB(DWS)锁类型考虑了TP场景的,像增删改的RowExclusiveLock,是能够并发加持到同一个表的,而TD这4个锁是不能实现这个场景的,TD的增删改默认加的WRITE锁只能一个事务持有。当然了TD里面也有行级锁,但是使用的场景比较少。
总体来说TD的表级锁只适合AP场景,GaussDB(DWS)适合AP与TP场景。
以上是这两天对GaussDB(DWS)的锁的学习,希望能对读者有所帮助。
- 点赞
- 收藏
- 关注作者
评论(0)