nzi*_*nab 10 sql postgresql query-optimization sql-execution-plan
我有这个查询(在postgresql中):
SELECT "table_1".* FROM "table_1"
INNER JOIN "join_table"
ON "table_1"."id" = "join_table"."table_1_id"
WHERE "join_table"."table_2_id" = 650727
ORDER BY table_1.created_at DESC
LIMIT 1
Run Code Online (Sandbox Code Playgroud)
返回1个结果,但执行时间约为250-300毫秒
还有btree索引table_1.created_at,以及join_table.table_1_id和join_table.table_2_id
当我只LIMIT 1从查询中删除时,执行时间下降到~13ms.此特定查询当前仅返回一个结果(没有LIMIT),但是在WHERE中还有其他值可能返回更多(这就是LIMIT是必要的原因).
为什么在一个只返回单个结果的查询中添加一个LIMIT,这会导致执行时间过多?
这是解释计划LIMIT 1(这些对我来说很难完全理解......):http://explain.depesz.com/s/rOy
这是没有LIMIT 1的解释计划:http://explain.depesz.com/s/q3d7
此外,如果我保留LIMIT 1,但将顺序更改为ASC,则查询也会降至13毫秒.如果我更改LIMIT为LIMIT 20(但保持ORDER BY DESC)它只需要22毫秒... wtf!?
所以它与组合有关ORDER BY DESC,并且LIMIT 1(确切地说是1)
好的,这是一个非常经典的案例.
无论何时使用LIMIT(或类似FETCH FIRST ... ROWS ONLY),优化器都会尝试优化查询,以便尽可能快地仅获取第一行.这意味着优化器优先选择执行计划,其中第一个成本值较低,而不是执行计划中显示的第二个成本值.请记住:PostgreSQL显示的两个成本值(例如,cost=48.150..6,416.240设置成本(48.150)和总执行成本(6,416.240).
这里的"问题"是你有一个支持你的ORDER BY子句的索引.因此,PostgreSQL认为它可以通过这个索引(由于DESC查询中的修饰符而以相反的顺序),并检查另一个表中的每一行是否满足其他WHERE子句.问题是优化器无法知道它是最早的行之一还是最后一行(根据ORDER BY).优化器进行任意猜测,认为匹配行将更多地朝向开始而不是结束.然后使用这种乐观估计来计算成本值,结果证明过于乐观,以便PostgreSQL最终解决了糟糕的执行计划.
当您更改ORDER BY ... DESC到ORDER BY ... ASC优化器时,会执行相同的任意但乐观的估计,在这种情况下会更正确,因此您可以获得更好的执行时间.
但是,从优化角度来看,根本原因是优化程序估计2,491行将匹配该WHERE子句tango = 650727.当优化器正确估计这只会击中几行时,问题可能不会发生.
该WHERE条款足够微不足道,一个好的估计应该没有问题.所以,主要的问题是:那张桌子上的统计数据怎么样?
有几种方法可以解决这个问题:
ANALYZE),看看是否有帮助.ALTER TABLE ... SET STATISTICS).这也增加了用于收集统计数据的样本大小,这意味着ANALYZE需要更长时间,但会产生更准确的结果.从理论上讲,这应该足以解决这个问题.但是,其他选择是:
created_at由于其他原因(如其他查询)不需要索引,请删除它.ORDER BY子句使用与子句相同的表,那将是很好的WHERE:如果您很幸运,您可能有一个列join_table具有相同的顺序,table_1.created_at因此它没有任何区别您订购的.但是,要小心,这很容易出错(例如,序列填充的序号可能有大纲).小智 0
尽管您仅添加限制 1,但对查询的任何更改都会影响其执行计划和使用的索引。
要解决您的问题,因为您说当 order 为 ASC 时,您的查询性能很好:
看来在 table_1.created_at 上创建的索引是 ASC。我知道在 db2 中您可以在创建索引时指定为双向 ASC/DESC。我想在postgresql中你应该有相同的,如果没有你可以在同一个字段1上使用sort DESC创建2个索引,另一个使用sort ASC创建2个索引
| 归档时间: |
|
| 查看次数: |
1176 次 |
| 最近记录: |