在Postgres的一张巨大的桌子上,Kaminari的COUNT(*)很慢

时间:2014-02-17 21:10:27

标签: sql ruby-on-rails postgresql will-paginate kaminari

我使用Kaminari gem在大表(~1.5MM行)上对查询进行分页。虽然获取实际结果页面非常快(约20毫秒),但kaminari添加SELECT COUNT(*) WHERE ....的速度极慢,并且在执行时间上增加了几秒钟。

有没有办法估算结果的数量?

1 个答案:

答案 0 :(得分:4)

全表的快速估算

快速估算整个表格:

您的示例提示地址。假设我们在架构adr中有一个名为public的表:

SELECT reltuples FROM pg_class WHERE oid = 'public.adr'::regclass;

此相关答案的更多细节:
How do I speed up counting rows in a PostgreSQL table?

计算条件

对于有条件的计数,Postgres可以使用索引来加快速度。 Postgres 9.2中的“覆盖索引”对此进行了改进,但必须满足某些要求才能从中获益。更多内容见Postgres Wiki about Index-only scans

对于citystate条件的查询,如果条件是 selective ,则此多列索引会有很大帮助(只有一小部分行符合条件):

CREATE INDEX adr_foo_idx ON adr (city, state);

如果你有一小组典型条件,你甚至可以使用partial indexes

CREATE INDEX adr_ny_ny_idx ON adr(adr_id)
WHERE  city = 'New York'
AND    state = 'NY';

...每组(state, city)

一个

或两者兼而有之:

CREATE INDEX adr_ny_idx ON adr (city)
WHERE  state = 'NY';

...每state

一个

规格化

当然,让你的大表(和索引)更小的一切都有帮助。城市和城市的查找表将大大减少冗余存储。这里的关键词是normalization

而不是:

CREATE TABLE adr (
  adr_id serial PRIMARY KEY
 ,state text
 ,city text
 ...
 );

SELECT count(*)
FROM   adr
WHERE  city = 'New York'
AND    state = 'NY';

规范化您的数据库设计并使用正确的索引:

CREATE TABLE state (
  state_id serial PRIMARY KEY
 ,state text UNIQUE
 );

CREATE TABLE city (
  city_id serial PRIMARY KEY
 ,state_id int REFERENCES state
 ,city text
 ,UNIQUE (state_id, city)
 );

CREATE TABLE adr (
  adr_id serial PRIMARY KEY
  city_id int REFERENCES city
  ...
 );

CREATE INDEX adr_city_idx ON adr (city_id);

SELECT count(*)
FROM   state s
JOIN   city  c USING (state_id)
JOIN   adr   a USING (city_id)
WHERE  s.state = 'NY'
AND    c.city  = 'New York'

表和索引变小。整数处理比文本更快。一切都变得更快。

Materialized view

最重要的是,如果性能至关重要,并且由于您不需要完全计数,您可以使用具有相关条件计数的物化视图。在您选择的事件或时间刷新视图以使数字保持最新。有关详细信息,请参阅手册链接。需要Postgres 9.3,但您可以在任何版本中手动轻松实现它。