我遇到了这个Postgres问题,如果我在查询字符串上使用参数vs硬编码它的相同查询需要很长时间才能执行。列名是'media_type',它是VARCHAR(20)。我正在使用Symfony2和Doctrine2 ORM从PHP运行这些查询,并且该表有大约1.000.000条记录。
我的查询有问题吗?它可能是Postgres配置问题吗?
1 - media_type的硬编码值
duration: 5.365 ms parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
duration: 0.142 ms bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0'
duration: 8.667 ms execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
parameters: $1 = '1', $2 = '1', $3 = '100', $4 = '0'
执行计划:
duration: 8.640 ms plan:
Query Text: SELECT id,site_id FROM item where media_type = 'Collection' AND enabled = 'true' AND site_id = $1 AND user_id = $2 ORDER BY id DESC LIMIT $3 OFFSET $4
Limit (cost=8.38..8.38 rows=1 width=12) (actual time=8.516..8.595 rows=24 loops=1)
Buffers: shared hit=10 read=15
-> Sort (cost=8.38..8.38 rows=1 width=12) (actual time=8.505..8.530 rows=24 loops=1)
Sort Key: id
Sort Method: quicksort Memory: 26kB
Buffers: shared hit=10 read=15
-> Index Scan using item_media_type_index on item (cost=0.00..8.37 rows=1 width=12) (actual time=7.955..8.397 rows=24 loops=1)
Index Cond: ((media_type)::text = 'Collection'::text)
Filter: (enabled AND (site_id = $1) AND (user_id = $2))
Buffers: shared hit=8 read=15
2 - 使用media_type(SLOWER)的参数
duration: 5.557 ms parse pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
duration: 1.322 ms bind pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0'
duration: 71564.998 ms execute pdo_stmt_00000001: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
parameters: $1 = 'Collection', $2 = '1', $3 = '1', $4 = '100', $5 = '0'
执行计划:
duration: 71564.922 ms plan:
Query Text: SELECT id,site_id FROM item where media_type = $1 AND enabled = 'true' AND site_id = $2 AND user_id = $3 ORDER BY id DESC LIMIT $4 OFFSET $5
Limit (cost=90663.16..181326.31 rows=17184 width=12) (actual time=3.667..71564.864 rows=24 loops=1)
Buffers: shared hit=183786 read=96585
-> Index Scan Backward using item_pkey on item (cost=0.00..906610.46 rows=171836 width=12) (actual time=3.655..71564.798 rows=24 loops=1)
Filter: (enabled AND ((media_type)::text = $1) AND (site_id = $2) AND (user_id = $3))
Buffers: shared hit=183786 read=96585
提前致谢。
答案 0 :(得分:9)
这在PostgreSQL中是一个长期存在的疣,历史上需要一些有趣的规划师调整才能解决。它已经在PostgreSQL 9.2中修复(现在测试版),但是像往常一样感谢Tom Lane。
E.1.3.1.3。优化
提高计划员选择参数化计划的能力(Tom 泳道)
现在解析,分析和重写预准备语句,但不是 必须有计划。准备好的计划执行时 参数,计划者可能会为每个常数重新计算它,或者它 如果其成本接近于a的成本,则可以执行通用计划 具体的计划。
请参阅9.2 beta release notes和quick note I wrote about this on lwn.net。关于处理mailing lists上比正常运行速度慢的准备/参数化语句的信息很多。
答案 1 :(得分:2)
当我绑定到一个SMALLINT字段并传入一个Postgres隐式从INTEGER转换为SMALLINT的值时,我遇到了一个非常类似的问题。我通过使演员明确来修复它。由于media_type是VARCHAR(20)类型,Postgres正在从TEXT类型进行隐式转换。试试这个:
where media_type = $1::VARCHAR(20)
答案 2 :(得分:1)
在静态,快速查询中,正在使用item_media_type_index。 在绑定的较慢查询中,未使用item_media_type_index。
“item”表中“media_type”列的选择性是什么?如果你做了:
SELECT media_type, COUNT(*)
FROM item
GROUP BY media_type
ORDER BY 2 desc
media_types是否均衡均衡,或者与其他媒体类型相比,'Collection'media_types相对较少?如果“收集”项目相对较少,那么我会冒这个猜测:使用静态查询,解析器知道您正在查询“收集”并且可以确定“收集”具有较低的计数并且索引可能值得使用。但是在绑定变量的情况下,解析器不知道您正在使用哪个media_type。其他一些media_type值可能占表中记录的很大比例(比如说20%)。在这种情况下,进行扫描比使用索引更麻烦。解析器需要做出决定,并且恰好决定不使用索引(对于您的情况是错误的,但对于其他media_type可能是正确的)。这只是基于其他一些rdbms如何工作的猜测。
在这种情况下,如果您知道选择性属性非常偏差,则使用硬代码,使用动态sql强制延迟解析,或者如果您认为正确,则强制使用索引。