你可能会在服务器的日志文件中看到类似如下的日志信息:
"LOG: 00000: process 60792 acquired AccessExclusiveLock on tuple (0,11) of relation 17344 of database 17074 after 2299.932 ms"
上面的日志翻译成中文大概是:
进程60792获取了oid为17074的数据库中oid为17344表的(0,11)元组上的AccessExclusiveLock,耗时2299.932 ms
有点绕,大概就是某个进程获取某个库中某张表某几行的AccessExclusiveLock。
问:是不是数据库中所有发生的获取锁的信息,都会被记录下来呢?
答:不是。只有在log_lock_waits = on, 并且获取锁的时间超过了deadlock_timeout,服务器才会记录这条信息。
log_lock_waits:控制当会话等待时间超过deadlock_timeout以获取锁时是否产生日志消息。默认为关闭。
deadlock_time的含义可以看我上一篇文章。
需要提醒的是,这条日志信息并不代表已经出现了死锁,但是这样的日志事件对于我们确定锁争用非常有用,因为它告诉我们服务器花了"大量"时间获取锁行/对象/表的锁。
当我们在日志中看到很多这样的消息,我们就有必要去检查下库中是否长时间存在很多的exclusive锁,同时需要确认为什么这样的exclusive锁,会一直存在。
查看当前的锁信息。
SELECT locker.pid,pc.relname,locker.mode,locker_act.application_name,least(query_start,xact_start) start_time,locker_act.state,CASEWHEN granted='f' THEN'wait_lock'WHEN granted='t' THEN'get_lock'END lock_satus,current_timestamp - least(query_start,xact_start) AS runtime,locker_act.queryFROM pg_locks locker,pg_stat_activity locker_act, pg_class pcWHERE locker.pid=locker_act.pidAND NOT locker.pid=pg_backend_pid()AND application_name<>'pg_statsinfod'AND locker.relation = pc.oidAND pc.reltype<>0 --and pc.relname='t'ORDER BY runtime desc;
最后,pg中有一个参数叫lock_timeout,表示在试图获取表、索引、行或其他数据库对象上的锁时,如果等待锁的时间超过lock_timeout指定的值,则终止语句。默认是0,表示关闭此功能。
如果不设置lock_timeout,一个session的锁等待将无限拉长(如果statement_timeout也没有限制的话),一旦大量的连接涌入进来全部等待一个短时间内无法释放的不兼容锁,那么数据库的连接数可能在短时间内被打爆。所以我们一般需要去设置这个值。
强制一些等待超时的session自动终止,不要无限期等待而占用数据库连接,引发系统级的异常。
参考:https://postgresqlco.nf/doc/zh/param/lock_timeout/
https://pganalyze.com/docs/log-insights/locks/L70




