什么是表锁
数据库中保证了数据的并发控制,用户和业务只需要执行SQL即可,不需要担心不同SQL之间的并发问题。而内核实现并发控制时,会用到不同的锁,最为常见的锁就是表锁。在PolarDB PostgreSQL中,表锁分为8级,从弱到强以及对应的场景场景如下所示 | |
| |
| SELECT FOR UPDATE/FOR SHARE |
| |
ShareUpdateExclusiveLock(4) | VACUUM (non-FULL),ANALYZE, CREATE INDEX CONCURRENTLY |
| CREATE INDEX (WITHOUT CONCURRENTLY) |
| blocks SELECT...FOR UPDATE |
| blocks ROW SHARE/SELECT...FOR UPDATE |
| ALTER TABLE, DROP TABLE, VACUUM FULL, TRUNCATE |
不同级别的锁之间可能会相互阻塞,锁之间的相容性矩阵如下,X代表两个锁互相冲突。锁等级 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1 |
|
|
|
|
|
|
| X |
2 |
|
|
|
|
|
| X | X |
3 |
|
|
|
| X | X | X | X |
4 |
|
|
| X | X | X | X | X |
5 |
|
| X |
| X | X | X | X |
6 |
|
| X | X | X | X | X | X |
7 |
| X | X | X | X | X | X | X |
8 | X | X | X | X | X | X | X | X |
什么是表锁等待
当多个后端进程持有的或尝试持有的表锁出现互斥关系时,他们就需要等待持有互斥表锁的进程释放表锁,此时就会出现表锁等待现象。处于表锁等待中的进程对外表现为该进程好像卡住了,迟迟没有返回结果。需要注意的是,表锁等待并不意味着此时表上被别的进程持有的表锁一定和表锁等待的进程请求的表锁互斥。举个例子:A进程执行SELECT,持有了1级锁,迟迟没有结束B进程执行TRUNCATE,请求8级锁,此时和A进程互斥,出现表锁等待C进程执行SELECT,请求1级锁,虽然和当前A进程持有的表锁不冲突,但由于B进程请求在前,因此也需要表锁等待,B进程执行完后释放8级锁后C进程才能持有到锁。PolarDB PostgreSQL这样设计表锁的原因在于防止高级别锁请求的饿死,如果C进程可以跨过B进程获取锁,那么B进程很可能一直被跨过导致一直无法请求到锁,出现饿死。从上述分析中也可以得出,TRUNCATE操作是一个危险的操作,TRUNCATE时如果表上有查询的长事务则会锁住整个表,使得整个表都无法访问直到查询的长事务结束。pg_stat_activity是PolarDB PostgreSQL中关于后端信息的视图,可以借助这个视图使用如下SQL查询出现表锁等待的后端信息select state,xact_start,datname,usename,query,pid from pg_stat_activity where wait_event_type = 'Lock' and wait_event = 'relation';复制
state —— 这个后端的当前总体状态。可能的值是:- idle in transaction:后端在一个事务中,但是当前没有正在执行一个查询。
- idle in transaction (aborted):这个状态与idle in transaction相似,不过在该事务中的一个语句导致了一个错误。
使用如下SQL查看阻塞住后端进程的信息,返回一个pid的数组,数组中的进程中的会话阻塞了指定进程 ID 所代表的服务器进程。select pg_blocking_pids({$pid});
复制
确认持有锁的后端进程可以取消后,使用如下SQL取消持有表锁的后端进程,及时释放锁资源,让处于表锁等待状态的后端能够获取到表锁。 输入的pid为持有表锁的后端处理进程的进程号。select pg_terminate_backend(${pid});
复制
关于 PolarDB PostgreSQL 版
PolarDB PostgreSQL 版是一款阿里云自主研发的云原生关系型数据库产品,100% 兼容 PostgreSQL,高度兼容Oracle语法;采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 、Ganos全空间数据处理能力和高可靠、高可用、弹性扩展等企业级数据库特性。同时,PolarDB PostgreSQL 版具有大规模并行计算能力,可以应对 OLTP 与 OLAP 混合负载。