How to detect query which holds the lock in Postgres?

前端 未结 5 2047
醉话见心
醉话见心 2020-12-07 08:51

I want to track mutual locks in postgres constantly.

I came across Locks Monitoring article and tried to run the following query:

SELECT bl.pid     A         


        
相关标签:
5条回答
  • 2020-12-07 09:17

    Postgres has a very rich system catalog exposed via SQL tables. PG's statistics collector is a subsystem that supports collection and reporting of information about server activity.

    Now to figure out the blocking PIDs you can simply query pg_stat_activity.

    select pg_blocking_pids(pid) as blocked_by
    from pg_stat_activity
    where cardinality(pg_blocking_pids(pid)) > 0;
    

    To, get the query corresponding to the blocking PID, you can self-join or use it as a where clause in a subquery.

    SELECT query
    FROM pg_stat_activity
    WHERE pid IN (select unnest(pg_blocking_pids(pid)) as blocked_by from pg_stat_activity where cardinality(pg_blocking_pids(pid)) > 0);
    

    Note: Since pg_blocking_pids(pid) returns an Integer[], so you need to unnest it before you use it in a WHERE pid IN clause.

    Hunting for slow queries can be tedious sometimes, so have patience. Happy hunting.

    0 讨论(0)
  • 2020-12-07 09:17

    One thing I find that is often missing from these is an ability to look up row locks. At least on the larger databases I have worked on, row locks are not shown in pg_locks (if they were, pg_locks would be much, much larger and there isn't a real data type to show the locked row in that view properly).

    I don't know that there is a simple solution to this but usually what I do is look at the table where the lock is waiting and search for rows where the xmax is less than the transaction id present there. That usually gives me a place to start, but it is a bit hands-on and not automation friendly.

    Note that shows you uncommitted writes on rows on those tables. Once committed, the rows are not visible in the current snapshot. But for large tables, that is a pain.

    0 讨论(0)
  • 2020-12-07 09:20

    Since 9.6 this is a lot easier as it introduced the function pg_blocking_pids() to find the sessions that are blocking another session.

    So you can use something like this:

    select pid, 
           usename, 
           pg_blocking_pids(pid) as blocked_by, 
           query as blocked_query
    from pg_stat_activity
    where cardinality(pg_blocking_pids(pid)) > 0;
    
    0 讨论(0)
  • 2020-12-07 09:41

    From this excellent article on query locks in Postgres, one can get blocked query and blocker query and their information from the following query.

    CREATE VIEW lock_monitor AS(
    SELECT
      COALESCE(blockingl.relation::regclass::text,blockingl.locktype) as locked_item,
      now() - blockeda.query_start AS waiting_duration, blockeda.pid AS blocked_pid,
      blockeda.query as blocked_query, blockedl.mode as blocked_mode,
      blockinga.pid AS blocking_pid, blockinga.query as blocking_query,
      blockingl.mode as blocking_mode
    FROM pg_catalog.pg_locks blockedl
    JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
    JOIN pg_catalog.pg_locks blockingl ON(
      ( (blockingl.transactionid=blockedl.transactionid) OR
      (blockingl.relation=blockedl.relation AND blockingl.locktype=blockedl.locktype)
      ) AND blockedl.pid != blockingl.pid)
    JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
      AND blockinga.datid = blockeda.datid
    WHERE NOT blockedl.granted
    AND blockinga.datname = current_database()
    );
    
    SELECT * from lock_monitor;
    

    As the query is long but useful, the article author has created a view for it to simplify it's usage.

    0 讨论(0)
  • 2020-12-07 09:41

    This modification of a_horse_with_no_name's answer will give you the blocking queries in addition to just the blocked sessions:

    SELECT
        activity.pid,
        activity.usename,
        activity.query,
        blocking.pid AS blocking_id,
        blocking.query AS blocking_query
    FROM pg_stat_activity AS activity
    JOIN pg_stat_activity AS blocking ON blocking.pid = ANY(pg_blocking_pids(activity.pid));
    
    0 讨论(0)
提交回复
热议问题