单次计数查询花费太多时间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),有什么办法可以避免这种情况吗?
这在很大程度上取决于您的架构和使用模式,但有一些事情可以尝试。
- Trim 您的电话号码是在输入数据库时计算的,而不是在您执行此操作时计算的。当您在计数期间使用
TRIM
时,数据库必须在它正在评估的每一行上 运行 它,这是一种浪费。如果你保证你的数字总是 trimmed,你可以做一个更简单的 telephone = '06868787878'
检查。如果这很常见,请确保 telephone
. 上有一个索引
- 如果您不能或不想在查询前trim您的
telephone
数据,create an index on the expressionTRIM(telephone)
。这有效地预先计算了 TRIM
正在做的所有工作,但显然只有助于查找 TRIM(telephone) = '123'
,而不是 telephone = '123'
本身。
尽可能将索引放在最具体的列上。例如。如果 check_id
在 2 行中只有 41
,Postgres 可以首先使用该索引来缩小集合的范围,然后验证其余条件的工作就很少了。如果 check_id
经常是 41
,但 telephone
很少 06868787878
,同样适用,你应该在 telephone
上建立索引。如果两者分布相当均匀并且这是一种常见的查询模式,也许您需要 (check_id, telephone)
上的 multicolumn index。按照与单列索引相同的推理,将更具体的列放在第一位,或者如果您还需要仅对其中一列进行过滤,则将其放在第一位。例如。 (check_id, telephone)
索引也可以让您有效地查询 check_id
前缀。
3b。创建一个涵盖所有条件的多列索引可能非常有效,因为 Postgres 可以仅使用索引来执行计数,而无需执行二次查找来获取主要记录并检查索引未涵盖的条件。
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),有什么办法可以避免这种情况吗?
这在很大程度上取决于您的架构和使用模式,但有一些事情可以尝试。
- Trim 您的电话号码是在输入数据库时计算的,而不是在您执行此操作时计算的。当您在计数期间使用
TRIM
时,数据库必须在它正在评估的每一行上 运行 它,这是一种浪费。如果你保证你的数字总是 trimmed,你可以做一个更简单的telephone = '06868787878'
检查。如果这很常见,请确保telephone
. 上有一个索引
- 如果您不能或不想在查询前trim您的
telephone
数据,create an index on the expressionTRIM(telephone)
。这有效地预先计算了TRIM
正在做的所有工作,但显然只有助于查找TRIM(telephone) = '123'
,而不是telephone = '123'
本身。 尽可能将索引放在最具体的列上。例如。如果
check_id
在 2 行中只有41
,Postgres 可以首先使用该索引来缩小集合的范围,然后验证其余条件的工作就很少了。如果check_id
经常是41
,但telephone
很少06868787878
,同样适用,你应该在telephone
上建立索引。如果两者分布相当均匀并且这是一种常见的查询模式,也许您需要(check_id, telephone)
上的 multicolumn index。按照与单列索引相同的推理,将更具体的列放在第一位,或者如果您还需要仅对其中一列进行过滤,则将其放在第一位。例如。(check_id, telephone)
索引也可以让您有效地查询check_id
前缀。3b。创建一个涵盖所有条件的多列索引可能非常有效,因为 Postgres 可以仅使用索引来执行计数,而无需执行二次查找来获取主要记录并检查索引未涵盖的条件。