【问题标题】:flask-sqlalchemy slow paginate countflask-sqlalchemy 慢分页计数
【发布时间】:2020-08-20 09:13:50
【问题描述】:

我的 Flask 应用程序中有一个 Postgres 10 数据库。我正在尝试对表上数百万行的过滤结果进行分页。问题是,paginate 方法对查询结果总数的统计完全无效。

这是带有虚拟过滤器的示例:

paginate = Buildings.query.filter(height>10).paginate(1,10)

如果执行 2 个查询,则在后台:

SELECT * FROM buildings where height > 10
SELECT count(*) FROM (
  SELECT * FROM buildings where height > 10
)
--------
count returns 200,000 rows

问题在于不带子查询的原始选择的计数非常快~30ms,但分页方法将其包装到需要 ~30s 的子查询中。

冷库查询计划:

是否可以选择以高性能方式使用来自 flask-sqlalchemy 的默认分页方法?

编辑: 为了更好地理解我的问题,这里使用了我的案例中使用的真实过滤器操作,但使用了虚拟字段名称:

paginate = Buildings.query.filter_by(owner_id=None).filter(Buildings.address.like('%A%')).paginate(1,10)

所以 ORM 产生的 SQL 是:

SELECT count(*) AS count_1 
FROM (SELECT foo_column, [...]
FROM buildings
WHERE buildings.owner_id IS NULL AND buildings.address LIKE '%A%' ) AS anon_1

该查询已通过以下索引进行了优化:

CREATE INDEX ix_trgm_buildings_address ON public.buildings USING gin (address gin_trgm_ops);
CREATE INDEX ix_buildings_owner_id ON public.buildings USING btree (owner_id)

问题就在于这个count函数,太慢了。

【问题讨论】:

  • 您是否在height 上添加了索引?
  • 子查询没有理由变慢。也许它只需要从磁盘读取数据,因为它还没有在内存中。尝试EXPLAIN (ANALYZE, BUFFERS) 的查询。
  • 高度的索引是存在的。
  • @jjanes 没有子查询的查询在测试时必须已经在缓存中,这就是为什么这两者之间存在差异的原因。问题是,在缓存中没有数据的情况下是否可以加快分页速度?

标签: database postgresql flask sqlalchemy flask-sqlalchemy


【解决方案1】:

所以它看起来像一个磁盘读取问题。解决方案将是获得更快的磁盘,获得更多的 RAM 是否都可以缓存,或者如果您有足够的 RAM 而不是使用 pg_prewarm 将所有数据提前放入缓存中。或者尝试增加 Effective_io_concurrency,以便位图堆扫描一次可以有多个 IO 请求未完成。

基于Filter: 条目和Row Removed by Index Recheck: 条目以及缺少有损块,您的实际查询似乎比您显示的更复杂。可能还有其他一些事情可以尝试,但我们需要查看真正的查询和索引定义(这显然只是“高度”上的普通 btree 索引)。

【讨论】:

  • 我编辑了我的问题,并添加了在我的场景中使用的真实过滤器(提供虚拟表名和列以更好地理解问题。
  • pg_trgm 实际上并不能很好地支持 '%A%'。它必须扫描整个索引,然后扫描整个表,因为 %A% 包含零三元组。但这并没有改变我的其余答案,问题不在于查询的制定方式,而在于数据不在缓存中。
猜你喜欢
  • 2017-08-23
  • 2013-08-30
  • 2016-07-29
  • 2019-04-23
  • 1970-01-01
  • 2017-02-07
  • 2018-08-30
  • 2020-06-12
相关资源
最近更新 更多