前言
说实话数据库卡死,还是比较少见的,我工作六七年也只遇到过两三次。上周就遇到一次,而且上周发生的十分的频繁。可能适合数据库有很大关系,因为我们搞了国产化,用了人大金仓的数据库。今天就讨论一下面临数据库卡死的时候,如何查到问题所在。(ps:本篇文章只是提一个一种排查的思路)
一、查看数据库线程活跃状态
数据库卡死是,先不要重启,我们可以通过查看数据库线程状态,来确定那个线程出现了问题,从而手动关闭线程,把风险降到最低(下面sql用postgre sql)。
通过pg_stat_activity视图查询活动线程
SELECT pid,client_addr,state,query,wait_event,wait_event_type,query_start,state_change FROM pg_stat_activity
当然pg_stat_activity
还有很多其他字段,你可以select *,上面列举的是一些关键字段,以下是一些常见的状态值的解释:
pid
:会话ID、进程ID。usename
:连接数据库的用户名。query
:正在执行的查询语句。state
:会话状态,active,表示正在执行或正在等待完成;idle,表示处于空闲的状态wait_event_type
:正在等待的事件类型(如果有),如IO、锁等。wait_event
:正在等待的具体事件(如果有)。query_start
:查询开始时间state_change
:状态修改时间
通过这些关键的字段可以查看当前数据库目前在执行的sql,通过state字段可以找到当前正在执行的sql,通过query_start、state_change粗略估计sql执行时间。找到对应会话的pid
二、查看数据库持有锁情况
数据库卡死是,除了通过查看数据库线程状态,来确定那个线程出现了问题,还可以观察数据库目前持有锁的情况,来定位是不是锁没有被释放。
查看数据库锁信息
SELECT * FROM pg_locks;
在 PostgreSQL 的 pg_locks
视图中,以下是常见字段的含义:
-
locktype
:表示锁的类型。常见的锁类型包括relation
(表级锁)、page
(页级锁)、tuple
(行级锁)等。 -
database
:表示所属的数据库的 OID(对象标识符)。 -
relation
:表示被锁定对象(如表、索引)的 OID。 -
page
:表示被锁定页的位置。如果锁定的是表级锁或行级锁,则该字段为 null。 -
tuple
:表示被锁定行的位置。如果锁定的是表级锁或页级锁,则该字段为 null。 -
virtualxid
:表示虚拟事务 ID。在锁定的是事务级别锁时,该字段表示锁定的事务。 -
transactionid
:表示锁定的事务的 ID。如果锁定的是事务级别锁,则该字段包含事务的 ID。对于其他类型的锁,该字段为 null。 -
classid
:表示锁定的对象的类 ID。对于关系型锁(relation),该字段表示关联的目录(catalog)的 OID。对于其他类型的锁,该字段可能有不同的含义。 -
objid
:表示锁定的对象的 ID。对于关系型锁(relation),该字段表示关联的对象(如表、索引)的 OID。对于其他类型的锁,该字段可能有不同的含义。 -
objsubid
:表示锁定对象的子标识符。对于某些类型的锁,如行级锁,该字段表示被锁定行的位置。 -
pid
:表示持有锁的进程 ID。 -
mode
:表示锁定的模式(如共享锁、独占锁等)。 -
granted
:表示锁是否已被授予。
这些字段提供了关于锁的类型、锁定对象、持有锁的事务或进程等信息。通过查询 pg_locks
视图,并解释这些字段的值,可以获得有关当前数据库中锁的详细信息。
三、查看是否发生死锁
通过查看数据库线程活跃状态,以及线程持有的锁情况可以去初步判断是否是某一个sql执行过长,或者存在长时间持有锁的情况。通过上面两个步骤可以看到两个视图都存在pid,我们也可以通过pid进行关联查询,分析sql持有锁的情况。
SELECT
p.pid,
query,
locktype,
mode
FROM
pg_stat_activity p
JOIN
pg_locks l ON p.pid = l.pid
WHERE
l.granted = FALSE;
上述查询将返回所有当前存在的会话和锁的信息,其中 pg_locks.granted = FALSE 表示未获得的锁。这样,你可以检查是否有会话在等待锁并导致死锁的情况。
如果查询结果中存在循环等待的锁(即互相等待对方释放锁的情况),则表示发生了死锁。
另外,你还可以使用 PostgreSQL 提供的 pg_deadlock_timeout 参数来设置死锁检测的超时时间。默认情况下,该参数值为 1 秒。如果死锁检测超过该时间,将引发一个错误,并在日志中记录死锁信息。
四、手动关闭线程
如果能确认时某个线程导致数据库卡死,我们就可以手动关闭该线程。
SELECT pg_cancel_backend(<pid>);
-- 或
SELECT pg_terminate_backend(<pid>);
总结
本篇文章主要分析了pg_stat_activity
,pg_locks
的字段含义,以及在排查数据库问题时的应用场景。在面临数据卡死的场景时候,可以查询这两个视图分析问题,而不是直接重启。