一、问题创造
二、 Lock 类型
三、各种锁block关系

四、实验
4.1 实验:Access exclusive 锁与Access share锁相互block 。
4.2 Share 锁与 Row Exclusive 锁冲突
4.3 update和delete在同一张表上是否可以并发?
4.4 基于postgresql 9.4 仿照dead lock
4.5 什么时候会获取exclusive lock
五、总结
一、问题创造
晚上接到报警信息,GP有慢查询,
慢查询的监控设定的是300s,truncate实行300s这个太不应该了,但是首先想到的是数据库是不是出问题了。查看了GP集群并未创造非常。于是连续追查,从pg_log中看到有一个sql实行了9000+s。韶光点也很吻合。
回顾了一下不才班前有剖析数据库的数据倾斜,利用的是官方手册中的这两个view
gp_skew_coefficientsgp_skew_idle_fractions。此时创造还有一个报警没看到。
从韶光顺序上来看,查询系统表的报警先来。
可是只是select view这会block truncate操作吗?
答案是yes,select会申请一个Access share锁,truncate 会申请一个Access excludsive锁
这两个是会相互block。
二、 Lock 类型1
ACCESS SHARE
ACCESS SHARE”锁模式只与“ACCESS EXCLUSIVE” 锁模式冲突;
查询命令(Select command)将会在它查询的表上获取”Access Shared” 锁,一样平常地,任何一个对表上的只读查询操作都将获取这种类型的锁. (Oracle 等沒有)
2
ROW SHARE
”Select for update”和”Select for share”命令将得到这种类型锁,并且所有被引用但没有 FOR UPDATE 的表上会加上”Access shared locks”锁
3
ROW EXCLUSIVE
Update, Delete, Insert”命令会在目标表上得到这种类型的锁,并且在其它被引用的表上加上”Access shared”锁,一样平常地,变动表数据的命令都将在这张表上得到”Row exclusive”锁
4
SHARE UPDATE EXCLUSIVE
Vacuum(without full), Analyze ”和 “Create index concurrently”命令会得到这种类型锁
5
SHARE
Create index”命令会得到这种锁模式
6
SHARE ROW EXCLUSIVE
任何Postgresql 命令不会自动得到这种锁
7
EXCLUSIVE
任何Postgresql 命令不会自动得到这种类型的锁
8
ACCESS EXCLUSIVE
ALTER TABLE, DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL” 命令会得到这种类型锁,在Lock table 命令中,如果没有申明其它模式,它也是默认模式
三、各种锁block关系一旦得到,常日会持有一个锁,直到transaction结束。但是,如果在建立savepoint后得到了锁,则如果将savepoint回滚到该锁,则会立即开释该锁。这符合以下原则:ROLLBACK取消自savepoint以来的所有命令。PL / pgSQL非常块中得到的锁也是如此:从该块进行的缺点转义将开释在个中获取的锁。
Once acquired, a lock is normally held till end of transaction. But if a lock is acquired after establishing a savepoint, the lock is released immediately if the savepoint is rolled back to. This is consistent with the principle that ROLLBACK cancels all effects of the commands since the savepoint. The same holds for locks acquired within a PL/pgSQL exception block: an error escape from the block releases locks acquired within it.
Requested Lock ModeCurrent Lock ModeACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVEACCESS SHARE XROW SHARE XXROW EXCLUSIVE XXXXSHARE UPDATE EXCLUSIVE XXXXXSHARE XX XXXSHARE ROW EXCLUSIVE XXXXXXEXCLUSIVE XXXXXXXACCESS EXCLUSIVEXXXXXXXX
四、实验测试常见的锁类型相互block的情形。
制造数据,可以利用gp自带的generate_series存储过程天生。,语法为 insert into select generate_series...
create table locktest(id int,cname varchar(50),remark text);insert into locktest select generate_series(1,10000),'wx good boy',md5('wx good boy');
4.1 实验:Access exclusive 锁与Access share锁相互block 。
场景一
begin代表开启一个事务,须要手动提交。
先在locktest上加Access share锁。
qmstst=# begin;BEGINqmstst=# select from locktest; id | cname | remark -------+-------------+---------------------------------- 3 | wx good boy | ac40ed9ead3cc7af7148f17c2c625b0e 5 | wx good boy | ac40ed9ead3cc7af7148f17c2c625b0e 7 | wx good boy | ac40ed9ead3cc7af7148f17c2c625b0eqmstst=# end;COMMITqmstst=#
其余一个窗口对locktest做truncate 操作,上一个操作完成后truncate立时完成
qmstst=# truncate table locktest;TRUNCATE TABLETime: 2776107.982 ms
结论: Access share 会block Access Exculsive锁
场景二
在事务中前辈行truncate操作,相称于给table上了一把独占锁,Access Exclusice锁,这个锁会block其他所有操作。
qmstst=# begin;BEGINTime: 0.918 msqmstst=# truncate table locktest;TRUNCATE TABLETime: 14.306 msqmstst=# end;COMMITTime: 9.241 msqmstst=#
可以看到select 确实被block了。
qmstst=# select from locktest; id | cname | remark ----+-------+--------(0 rows) Time: 99918.776 ms
在GP4中哪个sql block了哪个sql?在GPCC中是可以看到的。
通过GPCC可以看到是谁block了谁,什么类型的锁?
4.2 Share 锁与 Row Exclusive 锁冲突在数据库的掩护过程中,创建索引也是常常做的事情,别鄙视创建索引,如果是一个很繁忙的系统,索引不一定能创建得上,可能会发生等侍, 严重时造成系统故障;
Update, Delete, Insert 会获取RowExclusiveLock
qmstst=# select from locktest; id | cname | remark ----+-----------+---------------------------------- 4 | lock test | 7cf8555c01940f5373c5998954e68bd1 6 | lock test | 7cf8555c01940f5373c5998954e68bd1 2 | lock test | 7cf8555c01940f5373c5998954e68bd1 1 | lock test | 7cf8555c01940f5373c5998954e68bd1 3 | lock test | 7cf8555c01940f5373c5998954e68bd1 5 | lock test | 7cf8555c01940f5373c5998954e68bd1 7 | lock test | 7cf8555c01940f5373c5998954e68bd1 9 | lock test | 7cf8555c01940f5373c5998954e68bd1 8 | lock test | 7cf8555c01940f5373c5998954e68bd1 10 | lock test | 7cf8555c01940f5373c5998954e68bd1(10 rows) Time: 6.707 msqmstst=# qmstst=# begin;BEGINTime: 0.789 msqmstst=# insert into locktest(id,cname,remark) values('11','loookte test',md5('loookte test'));INSERT 0 1Time: 3.585 msqmstst=# end; (先不焦急提交)
通过pg_locks查看lock信息
qmstst=# select locktype,database,relation,pid,mode from pg_locks; locktype | database | relation | pid | mode ---------------+----------+----------+-------+------------------ relation | 17149 | 53443799 | 32661 | ShareLock transactionid | | | 28957 | ExclusiveLock relation | 17149 | 10333 | 28957 | AccessShareLock relation | 17149 | 53443799 | 32570 | RowExclusiveLock qmstst=# select relname,oid from pg_class where oid = 53443799; relname | oid ----------+---------- locktest | 53443799(1 row)
你可以自己两个表join查一次就能得到结果。也可以利用pg的内置数据类型查看
qmstst=# select 53443799::regclass; regclass ---------- locktest(1 row)
’Create Index’ 命令须要获取Share 锁模式。当不提交insert 事务的时候,alter table一贯被block。
qmstst=# create unique index locktest_id_inx on locktest (id);CREATE INDEXTime: 436904.083 ms
事实上这两个也是相互block,须要把稳把稳的是,在alter table时候如果某些insert 哀求低延迟,一定要把稳此问题。
4.3 update和delete在同一张表上是否可以并发?psql1 上进行delete操作。
qmstst=# begin;BEGINTime: 67.224 msqmstst=# qmstst=# delete from locktest where id=15;DELETE 0Time: 2.786 ms
psql2 上进行update操作,并非同一行数据。
Timing is on.qmstst=# begin;BEGINTime: 61.623 msqmstst=# update locktest set cname = '99999' where id = '13';UPDATE 0Time: 222093.301 ms 被壅塞了良久,直到psql1 的事务结束。
结论:GP4 环境下对付同一堆表不支持并发的update 和delete。
在GP4 并不存在全局去世锁检讨gp_enable_global_deadlock_detector 这个参数,并且每个delete/update 操作都会对表加上一个Exclusive锁。这在GP6有了很大改进。
~]$ gpconfig --show gp_enable_global_deadlock_detector 20200609:10:21:33:018265 gpconfig:P1QMSTST01:gpadmin-[ERROR]:-Failed to retrieve GUC information, guc does not exist: gp_enable_global_deadlock_detector'NoneType' object is not iterable
qmstst=# select from pg_locks where relation=53443799; locktype | database | relation | page | tuple | transactionid | classid | objid | objsubid | transaction | pid | mode | granted | mppsessionid | mppiswriter | gp_segment_id ----------+----------+----------+------+-------+---------------+---------+-------+----------+-------------+-------+------------------+---------+--------------+-------------+--------------- relation | 17149 | 53443799 | | | | | | | 41007522 | 8272 | ExclusiveLock | t | 1613721 | t | -1 relation | 17149 | 53443799 | | | | | | | 53981107 | 13198 | RowExclusiveLock | t | 1613721 | t | 0(2 rows)
4.4 基于postgresql 9.4 仿照dead lock
在仿照的时候一定要把稳去世锁产生的四个条件,刚开始仿照的时候还不能领会其事理导致不能重现去世锁。
导致去世锁产生的条件便是如下图:
1、process1 持有了A lock
2、process2 持有了B lock
3、process1同时要求B lock
4、process2同时要求A lock
此时去世锁就产生了
psql1
qmstst=# begin;BEGINTime: 0.210 msqmstst=# update locktest set cname='99999' where id=3;UPDATE 1Time: 1.702 ms
psq2
qmstst=# begin;BEGINTime: 0.203 msqmstst=# update locktest set cname = '888888' where id=4;UPDATE 1Time: 2.285 msqmstst=# update locktest set cname = '888888' where id=3;UPDATE 1Time: 6986.422 ms
然后再回到psql1
qmstst=# update locktest set cname='99999' where id=4;ERROR: deadlock detected (seg0 10.50.10.170:6000 pid=24516)DETAIL: Process 24516 waits for ShareLock on transaction 3127071; blocked by process 25819.Process 25819 waits for ShareLock on transaction 3127066; blocked by process 24516.HINT: See server log for query details.CONTEXT: while updating tuple (0,2) in relation "locktest"Time: 1002.614 ms
这时去世锁发生了。两个update事务相互等待对方持有的lock,末了一个update就会报dead lock,须要把稳的是仅有末了触发dead lock的事务会失落败,在psql2中更新id=3 的那个事务并不会影响。对应上图的第四步。
在GP4中只管update是表锁,一样平常不会涌现dead lock。但是在vacuum/analyze操作的时候发生dead lock的机会还是很大的。
下面是在正式环境创造的一段dead lock报错
ERROR: deadlock detectedDETAIL: Process 46199 waits for ShareUpdateExclusiveLock on relation 465909 of database 17149; blocked by process 7768.Process 7768 waits for ExclusiveLock on relation 1296434658 of database 17149; blocked by process 46199.
查看到两个relation 分别是
qmsprd=# select 465909 ::regclass; regclass --------------------- wpp_adefect_panel_f(1 row) Time: 1.165 msqmsprd=# select 1296434658::regclass; regclass ----------------------------------- wpp_adefect_panel_f_1_prt_p202005(1 row) Time: 1.152 ms
ShareUpdateExclusiveLock 的获取显而易见,但是分区表上的Exclusive无法确定其来源。(在GP6的版本上官方文档给出的解释是: GP6获取该lock的几率变得很小.官方文档描述见4.5节)
后来查询log创造每周六做vacuum、analyze的时候总会发生deadlock的情形。看起来对全体表做vacuum、analyze还是有风险的,上上策为对单独分区做analyze、vacuum full。这样不好吗?
通过以下sql查询deadLock的结果。
select logtime,logsessiontime, loguser,loghost,logmessage,logdebug,logfile,logstack from log_alert_history where1=1 and logmessage like '%deadlock%' order by logtime desc
再把稳看 04/21 02:20:11 那两个insert into。 两个RowExclusiveLock 也会发生dead lock,通过这个再次加深一下对dead lock的理解。哈哈dead lock真是防不胜防啊。
Process 774 waits for RowExclusiveLock on relation 691206 of database 17149; blocked by process 25233.Process 25233 waits for RowExclusiveLock on relation 691235 of database 17149; blocked by process 774.Process 774 waits for RowExclusiveLock on relation 691206 of database 17149; blocked by process 17913.Process 17913 waits for RowExclusiveLock on relation 691235 of database 17149; blocked by process 774.
4.5 什么时候会获取exclusive lock
postgresql 8.4官方是这么说的:
Conflicts with the ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE lock modes. This mode allows only concurrent ACCESS SHARE locks, i.e., only reads from the table can proceed in parallel with a transaction holding this lock mode.
This lock mode is not automatically acquired on user tables by any PostgreSQL command. However it is acquired on certain system catalogs in some operations.
postgresql 9.4官方是这么说的:
Conflicts with the ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE lock modes. This mode allows only concurrent ACCESS SHARE locks, i.e., only reads from the table can proceed in parallel with a transaction holding this lock mode.
Acquired by REFRESH MATERIALIZED VIEW CONCURRENTLY.
可以看到9.4得到该锁的情形变得明确了。 事实上,在8.4 我创造DML乃至会莫名其妙获取这个Exclusive锁,这是我们不肯望看到的,这一点在9.4 有了很大提升。
五、总结事出必有因,那回到最初的问题,为什么查这两个view会block truncate呢?
gp_skew_coefficientsgp_skew_idle_fractions。由于gp_skew_coefficients是嵌套了好几层还有一些存储过程,详细调用关系没有细看,我猜是由于该view的某个过程对须要truncate的表加了Access share锁,因此到了truncate一贯被壅塞,终极大量报警,cancel 那个block的sql问题就办理了。
排查数据倾斜任重道远。
drop、truncate 事理:
对付PostgreSQL数据库来说每个表都是由一个或几个文件组成的,文件由一个数据组成,如名称为:123468,文件大小不能超过一个设定值,目前是1G,如果表的内容超过了1G,1g内容写到下一个加了”.数字"的文件中,如123468.1,当123468.1写满了,再放到123468.2文件中,依此类推。当greenplum在truncate table或drop table时,会把表对应的文件删除,删除的办法是,先删除123468这个文件,然后遍历这个目录下的所有文件,看这个目录下的每一个文件名前面是否是123468.n的格式(n为1,2,3...),如果是,再把这个文件删除。随着greenplum数据库的变大,数据目录下有几十万个文件乃至 到达百万个文件,于是遍历目录就会很慢。
参考:https://www.postgresql.org/docs/8.4/explicit-locking.html