【问题标题】:Ways to improve this SQL query改进此 SQL 查询的方法
【发布时间】:2014-03-04 17:19:27
【问题描述】:

我有这个问题

SELECT "items".*
FROM
    "items"
    INNER JOIN
    item_mods ON item_mods.item_id = items.id AND item_mods.mod_id = 15
WHERE (items.league_id = 1) AND (items.item_type_id = 11) AND (num_sockets >= 2)
ORDER BY item_mods.total_value DESC
LIMIT 25

这是解释http://explain.depesz.com/s/dbf

"Limit  (cost=55739.84..55739.90 rows=25 width=554) (actual time=18065.470..18065.478 rows=25 loops=1)"
"  ->  Sort  (cost=55739.84..55741.90 rows=824 width=554) (actual time=18065.468..18065.471 rows=25 loops=1)"
"        Sort Key: item_mods.total_value"
"        Sort Method: top-N heapsort  Memory: 37kB"
"        ->  Nested Loop  (cost=5871.95..55716.59 rows=824 width=554) (actual time=285.806..18055.589 rows=610 loops=1)"
"              ->  Bitmap Heap Scan on items  (cost=5871.52..20356.70 rows=4339 width=550) (actual time=201.543..10028.684 rows=9945 loops=1)"
"                    Recheck Cond: ((item_type_id = 11) AND (num_sockets >= 2))"
"                    Rows Removed by Index Recheck: 4120"
"                    Filter: (league_id = 1)"
"                    Rows Removed by Filter: 1125"
"                    ->  BitmapAnd  (cost=5871.52..5871.52 rows=4808 width=0) (actual time=199.322..199.322 rows=0 loops=1)"
"                          ->  Bitmap Index Scan on index_items_on_item_type_id  (cost=0.00..289.61 rows=15625 width=0) (actual time=38.699..38.699 rows=16018 loops=1)"
"                                Index Cond: (item_type_id = 11)"
"                          ->  Bitmap Index Scan on index_items_on_num_sockets  (cost=0.00..5579.49 rows=301742 width=0) (actual time=158.441..158.441 rows=301342 loops=1)"
"                                Index Cond: (num_sockets >= 2)"
"              ->  Index Scan using index_item_mods_on_item_id on item_mods  (cost=0.43..8.14 rows=1 width=8) (actual time=0.803..0.803 rows=0 loops=9945)"
"                    Index Cond: (item_id = items.id)"
"                    Filter: (mod_id = 15)"
"                    Rows Removed by Filter: 9"
"Total runtime: 18065.773 ms"

如何提高此查询的速度?我注意到索引扫描中有一个循环 > 9000 次

【问题讨论】:

  • num_sockets 列属于哪个表?
  • 你能显示两个表的 DDL,包括任何索引吗?接下来,您真的需要 所有 [items] 中的字段吗?
  • 您的哪些 WHERE 条件是稳定的?可能的部分索引必须涵盖的值范围是多少?这些表是如何关联的? 1:n,n:m,n:1,...?每个项目都有 item_mod(s) 吗?
  • @DipenduPaul:从解释输出中推断(index_items_on_num_sockets),应该是index.num_sockets

标签: sql postgresql indexing postgresql-performance sql-limit


【解决方案1】:

您的查询速度慢的原因是因为您想要返回数据的方式没有索引。

注意“位图索引扫描”,它说,我知道你有一个索引,但我必须查看整个表才能找到我需要的行(因此总行扫描高达 301742!)。这可能是由于您要求的其他列和您应用的约束的组合,即 item_mods.mod_id = 15

试试:

  1. "items".* - 只选择您需要的列,而不是全部。

  2. 在以下位置创建索引:item_mods.item_id AND item_mods.mod_id

  3. 在以下位置创建索引:items.league_id AND items.item_type_id AND num_sockets(假设 num_sockets 在同一张桌子上)

任何性能差异?

【讨论】:

  • 在这种情况下,创建 2 个索引有很大帮助
【解决方案2】:

这只会缩短和清理语法,但不会实质性地改变任何东西:

SELECT i.*
FROM   items     i
JOIN   item_mods m ON m.item_id = i.id
WHERE  i.league_id = 1
AND    i.item_type_id = 11
AND    i.num_sockets >= 2
AND    m.mod_id = 15
ORDER  BY m.total_value DESC
LIMIT  25;

这样的查询很难优化。 Postgres 不能只从索引顶部读取。由于您是按item_mods 中的列进行排序,但最具选择性的条件是在items 上,因此也很难定制一个更有帮助的索引。

当然,您可以优化任一表的索引。但是如果没有额外的信息来提供给查询,它就不会便宜。 在 Postgres 知道获胜者之前,必须读取所有符合条件的行。

我们在 dba.SE 上针对这个相关问题开发了解决方案。复杂的东西:
Can spatial index help a “range - order by - limit” query

【讨论】:

  • 是的,我发现唯一的方法是添加非常具体的索引。就我而言,读取速度远比插入速度重要。所以我计划添加更多的索引。例如,league_id 几乎会出现在每个查询中,所以我计划为 [league_id,item_type_id] 和 [league_id, num_sockets] 添加索引
  • @NickBarrett:确保只保留那些实际使用的索引。这个查询只需要为每个表创建一个多列索引 - 除非您使用部分索引进行操作。
  • 欧文,我对索引的了解有限。如果我为 [league_id, item_type_id, num_sockets] 创建一个索引,则比 2 个索引 [league_id,item_type_id] 和 [league_id, num_sockets] 好。谢谢
  • @NickBarrett:嗯,是的。但是“更好”是由所有要求和条件的总和定义的。部分索引可能会更快——但只适用于一组狭义的查询。显示 1 个查询并不能提供完整的图片。我不知道要覆盖的可能查询范围、表定义、基数、数据分布。索引有很多技巧......
【解决方案3】:

下面的查询应该会提供更好的性能,因为过滤是在加入之前完成的。

   SELECT t.* 
   FROM
   (
     SELECT items.* FROM items
     WHERE  (items.league_id = 1) AND (items.item_type_id = 11) 
   ) t 
   INNER JOIN 
   (
      SELECT item_mods.*
      FROM item_mods
      WHERE item_mods.mod_id = 15 
   ) s
   ON s.item_id = t.id 
   WHERE (num_sockets >= 2)
   ORDER BY item_mods.total_value DESC
   LIMIT 25

num_sockets>=2 如果知道它属于哪个表,也可以包含在一些内部查询中。

请让我知道它是否表现更好。

【讨论】:

  • 这很可能不会有帮助。 Postgres 足够聪明,可以以任何一种方式以优化的顺序应用条件。这个查询的问题是主要的。
  • Erwin 是对的,优化器对这个查询的解释完全相同
猜你喜欢
  • 2011-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多