【发布时间】:2016-07-29 21:30:36
【问题描述】:
我使用 Python=2.7.3、PostgreSQL=9.3、Flask=0.10.0、SQLAlchemy =1.10.12,Flask-SQLAlchemy=2.1 和 psycopg2=2.5.4
我在 PostgreSQL 中有一个 10 亿条记录的表,我必须对其进行分页并在其中提供搜索:
class MyTable(db.Model):
""" My Table """
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(100), index=True, unique=True)
name = db.Column(db.String(512), index=True)
__tablename__ = 'my_table'
所以我在请求数据的代码中做了以下操作:
records = MyTable.query.filter(**filter_list).\
order_by(asc('code')).paginate(page, per_page, False)
关键是如果 per_page=10 和 page=1158960 即使根本没有过滤,只需为最后一页选择 10 条最后记录需要大约 13 秒。
从我在 flask-sqlalchemy 源中找到的,.paginate 是这样的:
.order_by(asc('code')).limit(per_page).offset((page - 1) * per_page)
生成的 SQL 查询如下所示:
SELECT my_table.id, my_table.code, my_table.name
FROM my_table ORDER BY my_table.code ASC
LIMIT 10 OFFSET 1158960
当我在服务器控制台上触发它时,我意识到问题出在 ORDER BY 子句中。不知何故,它必须首先使用 ORDER BY 对整个表进行排序,然后才使用 LIMIT 和 OFFSET。但这太慢了。
解释(分析):
"Limit (cost=470520.26..470520.26 rows=1 width=178) (actual time=12460.060..12460.061 rows=8 loops=1)"
" -> Sort (cost=467626.96..470520.26 rows=1157320 width=178) (actual time=11343.220..12424.686 rows=1158968 loops=1)"
" Sort Key: code"
" Sort Method: external merge Disk: 218312kB"
" -> Seq Scan on my_table (cost=0.00..42518.20 rows=1157320 width=178) (actual time=0.026..378.637 rows=1158968 loops=1)"
"Total runtime: 12475.160 ms"
如果您只是从该 SQL 请求中删除 ORDER BY,它会在 270 毫秒内完成!
"Limit (cost=42518.20..42518.20 rows=1 width=178) (actual time=269.940..269.942 rows=8 loops=1)"
" -> Seq Scan on my_table (cost=0.00..42518.20 rows=1157320 width=178) (actual time=0.030..246.200 rows=1158968 loops=1)"
"Total runtime: 269.992 ms"
我能做些什么吗?
【问题讨论】:
-
你有哪些指标?您要应用哪些过滤器?
-
Seq Scan on my_table可能意味着索引ix_my_table_code未用于ORDER BY操作。我会测试reindex index ix_my_table_code看看Index Scan using ix_my_table_code on my_table是否出现在explain analyze ...中 -
@univerio 关于索引,它们是由 flask-sqlalchemy 创建的默认索引: CREATE UNIQUE INDEX ix_my_table_code ON my_table USING btree (code COLLATE pg_catalog."default");
-
@J.J.Hakala 我按照您的建议进行了重新索引,但没有任何改变。我也不太明白 - 如果为具有默认 ASC 顺序的字段构建索引 - 为什么该字段的 ORDER_BY ... ASC 会强制执行我们在 EXPLAIN 中看到的排序操作?记录不是已经在该索引中排序了吗?
-
@Tosh 我用 postgresql 9.4 进行了测试,我不知道在这种情况下它的行为是否与 9.3 版本不同。
标签: postgresql flask pagination sqlalchemy sql-order-by