我们在使用数据库时,经常遇到锁等待的事情。例如一个用户在更新一条记录时,另一个会话要更新同一条记录就需要等待。这种锁等待是比较显而易见的,有一些锁等待你可能会觉得匪夷所思。下面就由DBA+社群联合发起人周正中,跟大家聊聊这些“隐式”锁请求。
专家简介
周正中
网名:德哥@Digoal
DBA+社群联合发起人
PostgreSQL中国社区发起人之一,负责杭州分会,兼任社区CTO一职。曾就职于斯凯网络,负责数据库部门。现就职于阿里巴巴,负责RDS PG内核组事务。
例如:
会话A:
会话B:
select * from pg_get_indexdef('test_pkey'::regclass); -- 查询索引的定义
如果test_pkey是test的一个索引,它会进入等待状态。
我们来跟踪一下
首先请开启LOCK DEBUG。
会话A:
会话B:
等待中。
此时提交或回归会话A,然后会话B可以继续:
正常情况下,查询索引定义如果没有被堵塞,观察到的锁是这样的:
还有很多获得对象定义的函数,使用时需要注意。
例如:
获取规则定义也要请求表的AccessShareLock。
会话A:
会话B:
pg_get_viewdef也有这个问题,需要获得视图引用的表的AccessShareLock。
另外再提供一个需要注意的点,PG对未获得,但是在等待中的锁也在冲突列表中。
例如用户1对A表在做一个比较大的查询,另一个用户2需要对A表执行DDL,那么显然用户2的DDL无法获得排它锁在等待用户1的状态。此后,用户3或其他用户发起对A的查询请求也会被用户2堵塞,这是非常危险的,如果用户1不释放锁,那么用户2就会一直等待,同时会堵塞所有对A表的任何请求。
所以建议用户在执行DDL操作时,加一个锁超时的参数,防止出现以上情况。
set lock_timeout='1s';
还有一种autocommit的场景下,建议对需要持有大锁的SQL在执行前加上语句超时,防止长时间持锁或等待锁,在某些场景甚至可能造成拥塞,例如短连接的场景,用户可能不断发起请求,把数据库连接用完。
set statement_timeout ='1s';
以上锁DEBUG信息都可以参考src/include/storage/lock.h获得详细的描述。