【问题标题】:Need help understanding NpgSQL connection opening process需要帮助理解 NpgSQL 连接打开过程
【发布时间】:2018-08-17 13:30:15
【问题描述】:

我一直在尝试优化使用 NpgSQL 3.2.7 连接到 PostgreSQL 9.3 数据库的 Web 服务。今天我安装了 pgBouncer 并注意到在运行“select * from pg_stat_activity;”时我所有的 NpgSQL 连接都列出了这个查询:

SELECT ns.nspname, a.typname, a.oid, a.typrelid, a.typbasetype,
CASE WHEN pg_proc.proname='array_recv' THEN 'a' ELSE a.typtype END AS type,
CASE
  WHEN pg_proc.proname='array_recv' THEN a.typelem
  WHEN a.typtype='r' THEN rngsubtype
  ELSE 0
END AS elemoid,
CASE
  WHEN pg_proc.proname IN ('array_recv','oidvectorrecv') THEN 3    /* Arrays last */
  WHEN a.typtype='r' THEN 2                                        /* Ranges before */
  WHEN a.typtype='d' THEN 1                                        /* Domains before */
  ELSE 0                                                           /* Base types first */
END AS ord
FROM pg_type AS a
JOIN pg_namespace AS ns ON (ns.oid = a.typnamespace)
JOIN pg_proc ON pg_proc.oid = a.typreceive
LEFT OUTER JOIN pg_type AS b ON (b.oid = a.typelem)
LEFT OUTER JOIN pg_range ON (pg_range.rngtypid = a.oid) 
WHERE
  (
    a.typtype IN ('b', 'r', 'e', 'd') AND
    (b.typtype IS NULL OR b.typtype IN ('b', 'r', 'e', 'd'))  /* Either non-array or array of supported element type */
  ) 

当我在 pgAdmin 中运行此查询时,我第二次运行它需要 3 到 5 秒才能完成,此时所有内容都应该被缓存。当我以交互方式运行我的代码时,在 Web 服务调用中执行第一个打开命令需要 3 到 5 秒。

每次创建连接时都会运行吗?在我看来,这是获取一些相对静态数据的昂贵查询。如果每次创建连接时都必须运行它,是否有人对如何在 Web 服务中围绕它进行架构有任何建议?对于每次调用 Web 服务来说,3 到 5 秒的开销实在是太大了。使用池化对这个查询是否运行有影响吗?

添加时间:2018 年 3 月 14 日 这些是我在创建表以保存类型查询的结果后看到的日志条目。它成功运行它,然后由于某种原因找不到表。

2018-03-14 15:35:42 EDT LOG:持续时间:0.715 ms 解析:从“public”中选择 nspname,typname,oid,typrelid,typbasetype,type,elemoid,ord。“npgsqltypes”

2018-03-14 15:35:42 EDT LOG:持续时间:0.289 毫秒绑定:从“public”中选择 nspname、typname、oid、typrelid、typbasetype、type、elemoid、ord。“npgsqltypes”

2018-03-14 15:35:42 EDT LOG:执行:从“public”中选择 nspname,typname,oid,typrelid,typbasetype,type,elemoid,ord。“npgsqltypes”

2018-03-14 15:35:42 EDT LOG:持续时间:0.391 毫秒

2018-03-14 15:35:44 EDT 错误:关系“public.npgsqltypes”在字符 71 处不存在

2018-03-14 15:35:44 EDT 声明:从“public”中选择 nspname,typname,oid,typrelid,typbasetype,type,elemoid,ord。“npgsqltypes”

2018-03-14 15:35:44 EDT LOG:声明:DISCARD ALL

2018-03-14 15:35:44 EDT LOG:持续时间:0.073 毫秒

添加:2018 年 3 月 15 日

解释类型查询的输出:

Sort  (cost=3015139.78..3018795.67 rows=1462356 width=213)
  Sort Key: (CASE WHEN (pg_proc.proname = ANY ('{array_recv,oidvectorrecv}'::name[])) THEN 3 WHEN (a.typtype = 'r'::"char") THEN 2 WHEN (a.typtype = 'd'::"char") THEN 1 ELSE 0 END)
  ->  Hash Left Join  (cost=920418.37..2779709.53 rows=1462356 width=213)
        Hash Cond: (a.oid = pg_range.rngtypid)
        ->  Hash Join  (cost=920417.24..2752289.21 rows=1462356 width=209)
              Hash Cond: ((a.typreceive)::oid = pg_proc.oid)
              ->  Hash Join  (cost=919817.78..2724270.58 rows=1462356 width=149)
                    Hash Cond: (a.typnamespace = ns.oid)
                    ->  Hash Left Join  (cost=919305.50..2687199.40 rows=1462356 width=89)
                          Hash Cond: (a.typelem = b.oid)
                          Filter: (((a.typtype = ANY ('{b,r,e,d}'::"char"[])) AND ((b.typtype IS NULL) OR (b.typtype = ANY ('{b,r,e,d}'::"char"[])))) OR ((a.typname = ANY ('{record,void}'::name[])) AND (a.typtype = 'p'::"char")))
                          ->  Seq Scan on pg_type a  (cost=0.00..694015.89 rows=13731889 width=89)
                          ->  Hash  (cost=694015.89..694015.89 rows=13731889 width=5)
                                ->  Seq Scan on pg_type b  (cost=0.00..694015.89 rows=13731889 width=5)
                    ->  Hash  (cost=388.79..388.79 rows=9879 width=68)
                          ->  Seq Scan on pg_namespace ns  (cost=0.00..388.79 rows=9879 width=68)
              ->  Hash  (cost=465.87..465.87 rows=10687 width=68)
                    ->  Seq Scan on pg_proc  (cost=0.00..465.87 rows=10687 width=68)
        ->  Hash  (cost=1.06..1.06 rows=6 width=8)
              ->  Seq Scan on pg_range  (cost=0.00..1.06 rows=6 width=8)

【问题讨论】:

    标签: postgresql npgsql


    【解决方案1】:

    你说得对,这个查询是由 Npgsql 发出来从 PostgreSQL 后端加载所有类型的——不同的数据库可以有不同的数据类型(由于扩展、用户定义的类型等)。

    但是,此查询仅在与特定数据库的第一个物理连接上发送,由其连接字符串标识。换句话说,如果你连接到同一个数据库 X 次——连接到同一个连接字符串——你应该只会看到这个查询被发送一次。 Npgsql 在内部缓存这些信息。我刚刚验证了这是 3.2.7 中的行为,你看到别的了吗?

    【讨论】:

    • 感谢谢伊提供的信息。我认为问题在于,在负载测试中,所有连接都是一次创建的,因此每个连接都是全新的。我在大批量之前添加了创建一个连接,并且查询仅在创建第一个连接时运行。基于此,我是否可以假设,如果在创建我的 Web 服务时,我创建了一个与应用程序将使用的连接字符串相同的连接,然后我关闭了该连接,之后建立的任何连接都不会再次运行查询?
    • 没错。您可以在实际处理任何用户请求之前将其作为应用程序“热身”阶段的一部分来执行,以确保类型加载已经发生并且响应速度很快。确实,如果您同时从多个线程连接到同一个 new 数据库,则可能会多次加载类型(仅仅是因为它们会相互竞争)。除了一次性放缓之外,这没有负面影响。
    • Shay,我仍然有一些与此相关的问题。我们有许多使用 npgsql 的不同服务,启动查询确实会减慢速度。我尝试运行查询并将结果保存在表中,然后我在 npgsql 中更改查询以查询该表并重新编译。它工作了一段时间,但后来我开始收到消息,说我保存数据的表找不到。我正在使用这个查询: Select nspname,typname,oid,typrelid,typbasetype,type,elemoid,ord from public.npgsqltypes 表是否必须存储在不同的架构中?
    • 我不确定我是否理解... Npgsql 执行一个内置查询以从 pg_type 和其他表加载 PostgreSQL 类型。你的意思是你改变了 Npgsql 的代码从另一个表加载类型,认为它会比 pg_type 更有效?如果是这样的话,我会感到惊讶(无论如何,为此分叉 Npgsql 并不是一个好主意)。
    • 您能解释一下为什么一个一次性的类型加载查询会导致您出现这么多问题吗?不就是一开始就发生一次(最好是作为应用程序启动的一部分,在开始处理请求之前),然后就消失了吗?
    猜你喜欢
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多