单次计数查询花费太多时间postgresql

Single count query taking too much time postgresql

explain analyze  SELECT COUNT(*) FROM "customers" WHERE (TRIM(telephone) = 
'06868787878' AND check_id = 41); 

                          QUERY PLAN                                                                      
------------------------------------------------------------------------------  
------------------------------------------------------------------- 
Aggregate  (cost=12982.58..12982.59 rows=1 width=0) (actual 
time=200.452..200.453 rows=1 loops=1) 
->  Bitmap Heap Scan on customers  (cost=544.59..12982.21 rows=147 width=0) 
(actual time=14.555..200.447 rows=1 loops=1) 
     Recheck Cond: (check_id = 41) 
     Filter: (btrim((telephone)::text) = '06868787878'::text) 
     Rows Removed by Filter: 29394 
     ->  Bitmap Index Scan on idx_customers_check_id  (cost=0.00..544.55 
   rows=29350 width=0) (actual time=9.669..9.669 rows=29395 loops=1) 
           Index Cond: (check_id = 41) 
  Total runtime: 200.750 ms 
 (8 rows) 

有时也需要 (293.6ms), (1956.3ms),有什么办法可以避免这种情况吗?

这在很大程度上取决于您的架构和使用模式,但有一些事情可以尝试。

  1. Trim 您的电话号码是在输入数据库时​​计算的,而不是在您执行此操作时计算的。当您在计数期间使用 TRIM 时,数据库必须在它正在评估的每一行上 运行 它,这是一种浪费。如果你保证你的数字总是 trimmed,你可以做一个更简单的 telephone = '06868787878' 检查。如果这很常见,请确保 telephone.
  2. 上有一个索引
  3. 如果您不能或不想在查询前trim您的telephone数据,create an index on the expressionTRIM(telephone)。这有效地预先计算了 TRIM 正在做的所有工作,但显然只有助于查找 TRIM(telephone) = '123',而不是 telephone = '123' 本身。
  4. 尽可能将索引放在最具体的列上。例如。如果 check_id 在 2 行中只有 41,Postgres 可以首先使用该索引来缩小集合的范围,然后验证其余条件的工作就很少了。如果 check_id 经常是 41,但 telephone 很少 06868787878,同样适用,你应该在 telephone 上建立索引。如果两者分布相当均匀并且这是一种常见的查询模式,也许您需要 (check_id, telephone) 上的 multicolumn index。按照与单列索引相同的推理,将更具体的列放在第一位,或者如果您还需要仅对其中一列进行过滤,则将其放在第一位。例如。 (check_id, telephone) 索引也可以让您有效地查询 check_id 前缀。

    3b。创建一个涵盖所有条件的多列索引可能非常有效,因为 Postgres 可以仅使用索引来执行计数,而无需执行二次查找来获取主要记录并检查索引未涵盖的条件。