单计数查询花了太多时间postgresql

时间:2016-03-23 05:34:08

标签: ruby-on-rails ruby-on-rails-3 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 个答案:

答案 0 :(得分:1)

这在很大程度上取决于您的架构和使用模式,但有几点需要尝试。

  1. 修剪您的电话号码,因为它们已经输入到数据库中,而不是在您进行此计数时。在计数期间使用TRIM时,数据库必须在它正在评估的每一行上运行它,这很浪费。如果您保证您的号码始终被修剪,您可以进行更简单的telephone = '06868787878'检查。如果这很常见,请确保telephone上有索引。
  2. 如果您无法或不想在查询之前修剪telephone数据,create an index on the expression TRIM(telephone)。这有效地预先计算了TRIM正在进行的所有工作,但显然只是查找TRIM(telephone) = '123',而不是telephone = '123'。{/ li>
  3. 将索引放在最具体的列上。例如。如果check_id只有2行中的41,Postgres可以先使用该索引缩小集合范围,然后只需要很少的工作来验证其余的条件。如果check_id通常是41,但telephone很少06868787878,则同样适用,您的索引应该在telephone。如果两者都是相当均匀分布的并且这是一种常见的查询模式,那么您可能希望在(check_id, telephone)上使用multicolumn index。遵循与单列索引相同的推理,将更具体的列放在第一位,或者如果您还需要仅对其中一列进行过滤,请先将其放在第一列。例如。 (check_id, telephone)索引可让您有效地查询check_id前缀。

    3B。创建涵盖所有条件的多列索引可能非常有效,因为Postgres只能使用索引来执行计数,而无需执行辅助查找来获取主记录并检查索引未涵盖的条件。