如何针对矩形区域优化此SQL查询?

时间:2010-04-12 22:04:12

标签: sql postgresql indexing

我正在尝试优化以下查询,但我不清楚哪些索引或索引最好。我将瓷砖存储在二维平面中并查询该平面的矩形区域。出于本问题的目的,该表包含以下列:

  • id:主键整数
  • world_id:一个整数外键,用作tile子集的命名空间
  • tileY:Y坐标整数
  • tileX:X坐标整数
  • value:此tile的内容,varchar是否重要。

我有以下索引:

  • “ywot_tile_pkey”PRIMARY KEY,btree(id)
  • “ywot_tile_world_id_key”UNIQUE,btree(world_id,“tileY”,“tileX”)
  • “ywot_tile_world_id”btree(world_id)

这是我正在尝试优化的查询:

ywot=> EXPLAIN ANALYZE SELECT * FROM "ywot_tile" WHERE ("world_id" = 27685  AND "tileY" <= 6  AND "tileX" <= 9  AND "tileX" >= -2  AND "tileY" >= -1 );                                                                    QUERY PLAN                                                                 -------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on ywot_tile  (cost=11384.13..149421.27 rows=65989 width=168) (actual time=79.646..80.075 rows=96 loops=1)
   Recheck Cond: ((world_id = 27685) AND ("tileY" <= 6) AND ("tileY" >= (-1)) AND ("tileX" <= 9) AND ("tileX" >= (-2)))
   ->  Bitmap Index Scan on ywot_tile_world_id_key  (cost=0.00..11367.63 rows=65989 width=0) (actual time=79.615..79.615 rows=125 loops=1)
         Index Cond: ((world_id = 27685) AND ("tileY" <= 6) AND ("tileY" >= (-1)) AND ("tileX" <= 9) AND ("tileX" >= (-2)))
 Total runtime: 80.194 ms

所以世界是固定的,我们正在寻找一个矩形的瓷砖区域。可能相关的更多信息:

  • 查询区域的所有图块可能存在也可能不存在
  • 查询矩形的高度和宽度通常约为10x10-20x20
  • 对于任何给定(世界,X)或(世界,Y)对,可能存在无限数量的匹配瓦片,但最坏的情况目前约为10,000,通常会少得多。
  • 创建新图块的频率远远低于现有图块的更新频率(更改“值”),而且本身的频率远不如上面的查询那样频繁。

    我唯一能想到的就是索引(世界,X)和(世界,Y)。我的猜测是,数据库可以采用这两组并将它们相交。问题是,对于其中任何一个,都存在潜在的无限数量的匹配。是否有其他类型的指数更合适?

  • 3 个答案:

    答案 0 :(得分:2)

    将表格集中在“ywot_tile_world_id_key”上,主键似乎只是一个人工id。如果您有更多唯一的垂直值,而不是水平值,则可能需要反转顺序(world-id,y,x)。同时删除world-id上的唯一索引,它由复合索引复制。

    答案 1 :(得分:1)

    X,Y的GIST与PostGIS的GIST非常相似。事实上,您甚至可以使用Postgresql的PostGIS扩展程序并获得相当大的收益

    答案 2 :(得分:0)

    这就是我最终做的事情。查询现在大约是20ms而不是80,这是一个不错的改进,虽然并不令人惊讶。

    1. 加载了btree_gist contrib模块
    2. 创建了以下索引:
      CREATE INDEX CONCURRENTLY ywot_tile_boxes ON ywot_tile USING gist (world_id, box(point("tileX","tileY"),point("tileX","tileY")));
    3. 将查询切换为如下所示:
      SELECT * FROM "ywot_tile" WHERE world_id = 27685 AND box(point("tileX","tileY"),point("tileX","tileY")) && box(point(-2,-1),point(9,6));
    4. 非常感谢任何进一步的建议。