opengauss 系统表缺少索引,导致opengauss登录很慢

我是用 npgsql 连接 opengauss ,监控发现,登录 opengauss过程中,npgsql 会执行以下脚本从数据库服务器获取信息:

explain ANALYZE
SELECT ns.nspname, t.oid, t.typname, t.typtype, t.typnotnull, t.elemtypoid
FROM (
– Arrays have typtype=b - this subquery identifies them by their typreceive and converts their typtype to a
– We first do this for the type (innerest-most subquery), and then for its element type
– This also returns the array element, range subtype and domain base type as elemtypoid
SELECT
typ.oid, typ.typnamespace, typ.typname, typ.typtype, typ.typrelid, typ.typnotnull, typ.relkind,
elemtyp.oid AS elemtypoid, elemtyp.typname AS elemtypname, elemcls.relkind AS elemrelkind,
CASE WHEN elemproc.proname=‘array_recv’ THEN ‘a’ ELSE elemtyp.typtype END AS elemtyptype
FROM (
SELECT typ.oid, typnamespace, typname, typrelid, typnotnull, relkind, typelem AS elemoid,
CASE WHEN proc.proname=‘array_recv’ THEN ‘a’ ELSE typ.typtype END AS typtype,
CASE
WHEN proc.proname=‘array_recv’ THEN typ.typelem
WHEN typ.typtype=‘r’ THEN rngsubtype

                WHEN typ.typtype='d' THEN typ.typbasetype
            END AS elemtypoid
        FROM pg_type AS typ
        LEFT JOIN pg_class AS cls ON (cls.oid = typ.typrelid)
        LEFT JOIN pg_proc AS proc ON proc.oid = typ.typreceive
        LEFT JOIN pg_range ON (pg_range.rngtypid = typ.oid)
    ) AS typ
    LEFT JOIN pg_type AS elemtyp ON elemtyp.oid = elemtypoid
    LEFT JOIN pg_class AS elemcls ON (elemcls.oid = elemtyp.typrelid)
    LEFT JOIN pg_proc AS elemproc ON elemproc.oid = elemtyp.typreceive
) AS t
JOIN pg_namespace AS ns ON (ns.oid = typnamespace)
WHERE
    typtype IN ('b', 'r', 'm', 'e', 'd') OR -- Base, range, multirange, enum, domain
    (typtype = 'c' AND relkind='c') OR -- User-defined free-standing composites (not table composites) by default
    (typtype = 'p' AND typname IN ('record', 'void', 'unknown')) OR -- Some special supported pseudo-types
    (typtype = 'a' AND (  -- Array of...
        elemtyptype IN ('b', 'r', 'm', 'e', 'd') OR -- Array of base, range, multirange, enum, domain
        (elemtyptype = 'p' AND elemtypname IN ('record', 'void')) OR -- Arrays of special supported pseudo-types
        (elemtyptype = 'c' AND elemrelkind='c') -- Array of user-defined free-standing composites (not table composites) by default
    ))
ORDER BY CASE
       WHEN typtype IN ('b', 'e', 'p') THEN 0           -- First base types, enums, pseudo-types
       WHEN typtype = 'c' THEN 1                        -- Composites after (fields loaded later in 2nd pass)
       WHEN typtype = 'r' THEN 2                        -- Ranges after
       WHEN typtype = 'm' THEN 3                        -- Multiranges after
       WHEN typtype = 'd' AND elemtyptype <> 'a' THEN 4 -- Domains over non-arrays after
       WHEN typtype = 'a' THEN 5                        -- Arrays after
       WHEN typtype = 'd' AND elemtyptype = 'a' THEN 6  -- Domains over arrays last
END;

连接数据库需要好长时间,以下是执行计划,如下表:

Sort (cost=59.21..59.22 rows=1 width=267) (actual time=76727.251..76727.263 rows=180 loops=1)

Sort Key: (CASE WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ANY (‘{b,e,p}’::“char”)) THEN 0 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘c’::“char”) THEN 1 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘r’::“char”) THEN 2 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘m’::“char”) THEN 3 WHEN ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘d’::“char”) AND (CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END <> ‘a’::“char”)) THEN 4 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘a’::“char”) THEN 5 WHEN ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘d’::“char”) AND (CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ‘a’::“char”)) THEN 6 ELSE NULL::integer END)

Sort Method: quicksort Memory: 75kB

→ Nested Loop (cost=9.41..59.20 rows=1 width=267) (actual time=38051.889..76727.059 rows=180 loops=1)

→ Nested Loop Left Join (cost=9.41..50.87 rows=1 width=207) (actual time=38050.809..76725.241 rows=180 loops=1)

Filter: ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ANY (‘{b,r,m,e,d}’::“char”)) OR ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘c’::“char”) AND (cls.relkind = ‘c’::“char”)) OR ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘p’::“char”) AND (typ.typname = ANY (‘{record,void,unknown}’::name))) OR ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘a’::“char”) AND ((CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ANY (‘{b,r,m,e,d}’::“char”)) OR ((CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ‘p’::“char”) AND (elemtyp.typname = ANY (‘{record,void}’::name))) OR ((CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ‘c’::“char”) AND (elemcls.relkind = ‘c’::“char”)))))

Rows Removed by Filter: 785

→ Nested Loop Left Join (cost=9.41..42.54 rows=1 width=213) (actual time=38050.793..76722.621 rows=965 loops=1)

→ Hash Left Join (cost=9.41..34.26 rows=1 width=216) (actual time=38050.786..76721.247 rows=965 loops=1)

Hash Cond: (CASE WHEN (proc.proname = ‘array_recv’::name) THEN typ.typelem WHEN (typ.typtype = ‘r’::“char”) THEN pg_range.rngsubtype WHEN (typ.typtype = ‘d’::“char”) THEN typ.typbasetype ELSE NULL::oid END = elemtyp.oid)

→ Hash Left Join (cost=1.14..25.97 rows=1 width=151) (actual time=1.678..38671.524 rows=965 loops=1)

Hash Cond: (typ.oid = pg_range.rngtypid)

→ Nested Loop Left Join (cost=0.00..24.82 rows=1 width=147) (actual time=1.276..38670.722 rows=965 loops=1)

→ Nested Loop Left Join (cost=0.00..16.54 rows=1 width=87) (actual time=0.673..38649.806 rows=965 loops=1)

→ Index Scan using pg_type_oid_index on pg_type typ (cost=0.00..8.26 rows=1 width=90) (actual time=0.657..38625.535 rows=965 loops=1)

→ Index Scan using pg_class_oid_index on pg_class cls (cost=0.00..8.27 rows=1 width=5) (actual time=18.795..23.771 rows=737 loops=965)

Index Cond: (oid = typ.typrelid)

→ Index Scan using pg_proc_oid_index on pg_proc proc (cost=0.00..8.27 rows=1 width=68) (actual time=20.193..20.408 rows=946 loops=965)

Index Cond: (oid = (typ.typreceive)::oid)

→ Hash (cost=1.06..1.06 rows=6 width=8) (actual time=0.291..0.291 rows=6 loops=1)

Buckets: 32768 Batches: 1 Memory Usage: 257kB

→ Seq Scan on pg_range (cost=0.00..1.06 rows=6 width=8) (actual time=0.284..0.286 rows=6 loops=1)

→ Hash (cost=8.26..8.26 rows=1 width=77) (actual time=38048.996..38048.996 rows=965 loops=1)

Buckets: 32768 Batches: 1 Memory Usage: 362kB

→ Index Scan using pg_type_oid_index on pg_type elemtyp (cost=0.00..8.26 rows=1 width=77) (actual time=0.015..38048.707 rows=965 loops=1)

→ Index Scan using pg_class_oid_index on pg_class elemcls (cost=0.00..8.27 rows=1 width=5) (actual time=0.783..0.940 rows=33 loops=965)

Index Cond: (oid = elemtyp.typrelid)

→ Index Scan using pg_proc_oid_index on pg_proc elemproc (cost=0.00..8.27 rows=1 width=68) (actual time=1.474..1.499 rows=123 loops=965)

Index Cond: (oid = (elemtyp.typreceive)::oid)

→ Index Scan using pg_namespace_oid_index on pg_namespace ns (cost=0.00..8.27 rows=1 width=68) (actual time=1.426..1.479 rows=180 loops=180)

Index Cond: (oid = typ.typnamespace)

Total runtime: 76727.883 ms

从执行计划看,执行这个脚本需要 很长时间,表中所有查询都是基于系统表查询的,目前数据库连接需要5分钟以上,不知道开发团队有没有办法提供优化建议?

你好,我使用如下版本的opengauss测试了下您使用的语句,执行很快

版本:gaussdb --version
gaussdb (openGauss 7.0.0-RC2 build e5fe3f9f) compiled at 2025-09-26 16:52:54 commit 0 last mr 8457

执行耗时:

Total runtime: 11.472 ms

我使用的也是npsql(最新的版本)

根据你反馈的执行计划,可以看到最后的基表扫描都走的是index_scan,说明系统表是有索引的,问题并不是出在基表,仔细分析可以发现,主要的耗时在hash连接的操作中Hash (cost=8.26..8.26 rows=1 width=77) (actual time=38048.996..38048.996 rows=965 loops=1),而不是表的扫描

建议你看下你配置的如下参数的大小,请适当调大

shared_buffers = 1GB
temp_buffers = 8MB
work_mem = 64MB
maintenance_work_mem = 128MB

另外,你的计划中大量走了nest loop join,从我执行的情况来看,这个查询全部走的是hash join,如下

Sort (cost=887.40..887.45 rows=20 width=267) (actual time=16.568..16.583 rows=207 loops=1)
Sort Key: (CASE WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ANY (‘{b,e,p}’::“char”)) THEN 0 WHEN (CASE WHEN (proc.proname = ‘arra
y_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘c’::“char”) THEN 1 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘r’::“char”) TH
EN 2 WHEN (CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘m’::“char”) THEN 3 WHEN ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“c
har” ELSE typ.typtype END = ‘d’::“char”) AND (CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END <> ‘a’::“char”)) THEN 4 WHEN (CASE WHEN (pro
c.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘a’::“char”) THEN 5 WHEN ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END
= ‘d’::“char”) AND (CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ‘a’::“char”)) THEN 6 ELSE NULL::integer END)
Sort Method: quicksort Memory: 83kB
→ Nested Loop (cost=748.27..886.97 rows=20 width=267) (actual time=11.990..16.413 rows=207 loops=1)
→ Hash Left Join (cost=748.27..880.09 rows=20 width=207) (actual time=11.958..15.285 rows=207 loops=1)
Hash Cond: ((elemtyp.typreceive)::oid = elemproc.oid)
Filter: ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ANY (‘{b,r,m,e,d}’::“char”)) OR ((CASE WHEN (proc.proname = ‘array_re
cv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘c’::“char”) AND (cls.relkind = ‘c’::“char”)) OR ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype
END = ‘p’::“char”) AND (typ.typname = ANY (‘{record,void,unknown}’::name))) OR ((CASE WHEN (proc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE typ.typtype END = ‘a’::“char”)
AND ((CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::“char” ELSE elemtyp.typtype END = ANY (‘{b,r,m,e,d}’::“char”)) OR ((CASE WHEN (elemproc.proname = ‘array_recv’::na
me) THEN ‘a’::“char” ELSE elemtyp.typtype END = ‘p’::“char”) AND (elemtyp.typname = ANY (‘{record,void}’::name))) OR ((CASE WHEN (elemproc.proname = ‘array_recv’::name) THEN ‘a’::"
char" ELSE elemtyp.typtype END = ‘c’::“char”) AND (elemcls.relkind = ‘c’::“char”)))))
Rows Removed by Filter: 584
→ Hash Left Join (cost=474.00..552.42 rows=791 width=213) (actual time=8.817..11.216 rows=791 loops=1)
Hash Cond: (elemtyp.typrelid = elemcls.oid)
→ Hash Left Join (cost=394.60..462.15 rows=791 width=216) (actual time=8.030..10.153 rows=791 loops=1)
Hash Cond: (CASE WHEN (proc.proname = ‘array_recv’::name) THEN typ.typelem WHEN (typ.typtype = ‘r’::“char”) THEN pg_range.rngsubtype WHEN (typ.typtype = 'd
'::“char”) THEN typ.typbasetype ELSE NULL::oid END = elemtyp.oid)
→ Hash Left Join (cost=354.81..408.51 rows=791 width=151) (actual time=7.060..8.654 rows=791 loops=1)
Hash Cond: (typ.oid = pg_range.rngtypid)
→ Hash Left Join (cost=353.67..405.33 rows=791 width=147) (actual time=6.776..7.952 rows=791 loops=1)
Hash Cond: ((typ.typreceive)::oid = proc.oid)
→ Hash Left Join (cost=79.40..120.18 rows=791 width=87) (actual time=0.976..1.718 rows=791 loops=1)
Hash Cond: (typ.typrelid = cls.oid)
→ Seq Scan on pg_type typ (cost=0.00..29.91 rows=791 width=90) (actual time=0.010..0.362 rows=791 loops=1)
→ Hash (cost=68.62..68.62 rows=862 width=5) (actual time=0.841..0.841 rows=862 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 288kB
→ Seq Scan on pg_class cls (cost=0.00..68.62 rows=862 width=5) (actual time=0.005..0.568 rows=862 loops=1)
→ Hash (cost=216.90..216.90 rows=4590 width=68) (actual time=5.668..5.668 rows=4590 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 705kB
→ Seq Scan on pg_proc proc (cost=0.00..216.90 rows=4590 width=68) (actual time=0.174..3.992 rows=4590 loops=1)
→ Hash (cost=1.06..1.06 rows=6 width=8) (actual time=0.159..0.159 rows=6 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 257kB
→ Seq Scan on pg_range (cost=0.00..1.06 rows=6 width=8) (actual time=0.151..0.153 rows=6 loops=1)
→ Hash (cost=29.91..29.91 rows=791 width=77) (actual time=0.845..0.845 rows=791 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 343kB
→ Seq Scan on pg_type elemtyp (cost=0.00..29.91 rows=791 width=77) (actual time=0.005..0.560 rows=791 loops=1)
→ Hash (cost=68.62..68.62 rows=862 width=5) (actual time=0.669..0.669 rows=862 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 288kB
→ Seq Scan on pg_class elemcls (cost=0.00..68.62 rows=862 width=5) (actual time=0.005..0.401 rows=862 loops=1)
→ Hash (cost=216.90..216.90 rows=4590 width=68) (actual time=3.007..3.007 rows=4590 loops=1)
Buckets: 32768 Batches: 1 Memory Usage: 705kB
→ Seq Scan on pg_proc elemproc (cost=0.00..216.90 rows=4590 width=68) (actual time=0.003..1.317 rows=4590 loops=1)
→ Index Scan using pg_namespace_oid_index on pg_namespace ns (cost=0.00..0.29 rows=1 width=68) (actual time=0.451..0.547 rows=207 loops=207)
Index Cond: (oid = typ.typnamespace)
Total runtime: 17.204 ms
(40 rows)

你可以调整上述参数后,再更新下当前postgres库的统计信息(连接到postgres库执行下vacuum),然后再尝试下,谢谢。如果还是很慢,可以看下你的数据库是否部署的磁盘的IO性能如何。谢谢