我使用 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且页面= 1158960 ,即使根本没有过滤,只需13秒即可选择最后一页的10个最后记录。
根据我在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 。但那个地狱很慢。
EXPLAIN(ANALYZE):
"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"
我能做些什么吗?
答案 0 :(得分:1)
好的,我找到了解决这个问题的方法。
当我使用 SET enable_seqscan = off; 执行完全相同的查询时,它会强制PostgreSQL使用索引扫描而不是序列扫描,并且更多更快强>!
SET enable_seqscan=off;
SELECT my_table.id, my_table.code, my_table.name
FROM my_table ORDER BY my_table.code ASC
LIMIT 10 OFFSET 1158960
**EXPLAIN (ANALYZE):**
"Limit (cost=1814764.86..1814777.39 rows=8 width=131) (actual time=616.543..616.545 rows=8 loops=1)"
" -> Index Scan using ix_my_table_code on my_table (cost=0.43..1814777.39 rows=1158968 width=131) (actual time=0.065..590.898 rows=1158968 loops=1)"
"Total runtime: 616.568 ms"
所以现在重点是 - 如何设置PostgreSQL配置以使其使用索引扫描而不强制它?我想答案是 - " 规划师成本常数"。关于他们的任何建议?
更新13.04.2016:
我终于弄清楚了情况并找到了解决方案。就我而言,通过在 postgresql.conf 中设置规划师成本常量来解决所有问题:
seq_page_cost = 1.0
random_page_cost = 1.0
cpu_tuple_cost = 0.01
cpu_index_tuple_cost = 0.0001
cpu_operator_cost = 0.0025
effective_cache_size = 1024MB
此外,还有许多人建议将effective_cache_size设置为服务器整个RAM的3/4。无论如何,使用这些设置,Planner始终在巨大的表上使用索引扫描。所以时间现在是200-300毫秒。
解决了问题。