如何解决PostgreSQL 中用户无法删除的问题

问题描述

在日常管理 PostgreSQL,有时候我们需要对不需要使用的用户进行删除,有可能会出现删除不了的情况,报错如下:

rudonx=# drop user rudonx1;
ERROR:  role "rudonx1" cannot be dropped because some objects depend on it
DETAIL:  1 object in database rudonx1

我们应该如何排查此类问题?

问题分析

删除用户之前,我们需要预先删除依赖该用户的对象,如 我们要删除 rudonx1 这个用户,需要删除所有 owner 为 rudonx1 的对象。

分析解决

有了前面的问题分析,那么后续的处理办法无非是两种:

  1. 删除这些对象
  2. 将这些对象的 owner 修改为其他用户,这样适用于这些对象还继续使用的情况,生产上这种情况较多。

这里面有个问题是,通常一个用户下面的表很多,我们如何可以快速的找到这些对象,并生成对应的语句?

操作步骤

1.确保当前的 search path 是正确的

rudonx1=# show search_path;
  search_path   
----------------
 "$user",public
(1 row)

2.运行如下语句找到依赖的对象

rudonx1=# SELECT                    
'alter table "' || nsp.nspname || '"."' || cls.relname || '" owner to username;' || chr ( 13 )
FROM
pg_catalog.pg_class cls,
pg_catalog.pg_namespace nsp,
pg_user pu
WHERE
nsp.nspname IN ( 'public' )
AND cls.relnamespace = nsp.oid
and pu.usesysid=cls.relowner
AND cls.relkind IN ('r','v') 
and pu.usename='rudonx1'
ORDER BY
nsp.nspname,
cls.relname;
                       ?column?                       
------------------------------------------------------
 alter table "public"."ttt" owner to username;\r
 alter table "public"."ttt1" owner to username;\r
 alter table "public"."ttt2" owner to username;\r
 alter table "public"."ttt_view" owner to username;\r
(4 rows)
  • public:指定搜索 public schema 下的对象
  • relkind IN ('r','v'):指定搜索的对象为表和视图,相关的定义如下:r = ordinary table, i = index, S = sequence, t = TOAST table, v = view, m = materialized view, c = composite type, f = foreign table, p = partitioned table, I = partitioned index[1]。

3.最后执行拼接出来的语句,然后再删除用户即可。

当然我们也可以使用 \d 命令来进行筛选,这样的方式在表很多的方式下并不优雅。

rudonx1=# \d
            List of relations
 Schema |   Name    |   Type   |  Owner  
--------+-----------+----------+---------
 public | t1        | table    | rudonx
 public | t11       | table    | rudonx
 public | t1_id_seq | sequence | rudonx
 public | ttt       | table    | rudonx1
 public | ttt1      | table    | rudonx1
 public | ttt2      | table    | rudonx1
 public | ttt_view  | view     | rudonx1
(7 rows)

注意:建议在测试环境中充分测试,避免误操作带来业务中断的风险。

在 alter 表时语句阻塞问题

在前面我们已经拼接了 alter table 的语句,有一个问题是如果语句被阻塞,我们该如何排查?

1.使用 pg_stat_activity 查询等待事件为 Lock 的事务:

rudonx1=# SELECT * FROM pg_stat_activity WHERE wait_event_type ='Lock';
-[ RECORD 1 ]----+---------------------------------------------------
datid            | 16469
datname          | rudonx1
pid              | 2323750
usesysid         | 16468
usename          | rudonx
application_name | psql
client_addr      | 172.17.247.129
client_hostname  | 
client_port      | 58358
backend_start    | 2022-03-14 23:11:41.684693+08
xact_start       | 2022-03-14 23:11:44.201661+08
query_start      | 2022-03-14 23:11:44.201661+08
state_change     | 2022-03-14 23:11:44.201669+08
wait_event_type  | Lock
wait_event       | relation
state            | active
backend_xid      | 11678
backend_xmin     | 11676
query            | alter table "public"."rudonx11_t" owner to rudonx;
backend_type     | client backend

2.查看 阻塞的更信息信息

rudonx1=# SELECT blocked_locks.pid     AS blocked_pid,
       blocked_activity.usename  AS blocked_user,
       blocked_activity.client_addr as blocked_client_addr,
       blocked_activity.client_hostname as blocked_client_hostname,
       blocked_activity.client_port as blocked_client_port,
       blocked_activity.application_name as blocked_application_name,
       blocked_activity.wait_event_type as blocked_wait_event_type,
       blocked_activity.wait_event as blocked_wait_event,
       blocked_activity.query    AS blocked_statement,
       blocking_locks.pid     AS blocking_pid,
       blocking_activity.usename AS blocking_user,
       blocking_activity.client_addr as blocking_user_addr,
       blocking_activity.client_hostname as blocking_client_hostname,
       blocking_activity.client_port as blocking_client_port,
       blocking_activity.application_name as blocking_application_name,
       blocking_activity.wait_event_type as blocking_wait_event_type,
       blocking_activity.wait_event as blocking_wait_event,
       blocking_activity.query   AS current_statement_in_blocking_process
 FROM  pg_catalog.pg_locks         blocked_locks
    JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid
    JOIN pg_catalog.pg_locks         blocking_locks 
        ON blocking_locks.locktype = blocked_locks.locktype
        AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
        AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
        AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
        AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
        AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
        AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
        AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
        AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
        AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
        AND blocking_locks.pid != blocked_locks.pid
    JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
   WHERE NOT blocked_locks.granted ORDER BY blocked_activity.pid;
-[ RECORD 1 ]-------------------------+---------------------------------------------------
blocked_pid                           | 2323750
blocked_user                          | rudonx
blocked_client_addr                   | 172.17.247.129
blocked_client_hostname               | 
blocked_client_port                   | 58358
blocked_application_name              | psql
blocked_wait_event_type               | Lock
blocked_wait_event                    | relation
blocked_statement                     | alter table "public"."rudonx11_t" owner to rudonx;
blocking_pid                          | 2320785
blocking_user                         | rudonx11
blocking_user_addr                    | 172.20.165.1
blocking_client_hostname              | 
blocking_client_port                  | 34002
blocking_application_name             | psql
blocking_wait_event_type              | Client
blocking_wait_event                   | ClientRead
current_statement_in_blocking_process | insert into rudonx11_t values(1);

从上面可以看出,pid 为 2320785 阻塞了 2323750 的执行。在与业务方商量之后,如果可以的话,手动终止 pid 为 2320785 的事务。

3.终止事务

rudonx1=# SELECT pg_terminate_backend(2320785);
-[ RECORD 1 ]--------+--
pg_terminate_backend | t

终止完事务之后,我们会发现 drop table 的操作会顺利执行,然而生产环境更加复杂,建议找到业务上可维护的时间窗口,确保语句可以顺利执行。

参考文档

[1] https://www.postgresql.org/docs/11/catalog-pg-class.html

如果您有其他问题,欢迎您联系火山引擎技术支持服务

192
0
0
0
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论