【问题标题】:Bitmap Index Scan always Followed by Bitmap Heap Scan for JSON field query位图索引扫描始终后跟位图堆扫描以进行 JSON 字段查询
【发布时间】:2018-07-18 19:13:42
【问题描述】:

我有以下索引:

CREATE INDEX index_c_profiles_on_city_state_name_domain ON 
c_profiles ((data->>'state'), (data->>'city'), name, domain);

我正在使用以下查询:

SELECT mm.name, mm.domain, mm.data ->> 'city' as city, mm.data ->> 
'state' as state 
FROM c_profiles as mm
WHERE ((mm.data ->> 'state') = 'AZ')

但是当我使用 EXPLAIN ANALYZE 进行测试时,它总是进行位图索引扫描(良好且快速),然后进行非常慢的位图堆扫描(通常比单独的索引扫描慢 100 倍)。

我也试过只索引 WHERE 条件,结果是一样的,它使用索引后仍然在做非常慢的 Bitmap Heap Scan。

为什么 Postgres 这样做?我怎样才能让它只做索引扫描以使这个查询快速?

这是一个示例 EXPLAIN ANALYZE 结果:

[
  {
    "Execution Time": 53.655,
    "Planning Time": 0.081,
    "Plan": {
      "Exact Heap Blocks": 1338,
      "Node Type": "Bitmap Heap Scan",
      "Actual Total Time": 53.031,
      "Shared Hit Blocks": 727,
      "Schema": "public",
      "Plans": [
        {
          "Node Type": "Bitmap Index Scan",
          "Actual Total Time": 0.455,
          "Shared Hit Blocks": 2,
          "Shared Read Blocks": 13,
          "Temp Written Blocks": 0,
          "Local Dirtied Blocks": 0,
          "Local Hit Blocks": 0,
          "Plan Width": 0,
          "Actual Loops": 1,
          "Actual Startup Time": 0.455,
          "Temp Read Blocks": 0,
          "Local Read Blocks": 0,
          "Index Name": "index_mattermark_profiles_on_city_state_name_domain",
          "Startup Cost": 0,
          "Shared Dirtied Blocks": 0,
          "Shared Written Blocks": 0,
          "Local Written Blocks": 0,
          "Plan Rows": 788,
          "Index Cond": "((mm.data ->> 'state'::text) = 'AZ'::text)",
          "Actual Rows": 1417,
          "Parent Relationship": "Outer",
          "Total Cost": 34.33
        }
      ],
      "Shared Read Blocks": 650,
      "Relation Name": "mattermark_profiles",
      "Local Hit Blocks": 0,
      "Local Dirtied Blocks": 0,
      "Temp Written Blocks": 0,
      "Plan Width": 1010,
      "Actual Loops": 1,
      "Rows Removed by Index Recheck": 0,
      "Lossy Heap Blocks": 0,
      "Alias": "mm",
      "Recheck Cond": "((mm.data ->> 'state'::text) = 'AZ'::text)",
      "Temp Read Blocks": 0,
      "Output": [
        "name",
        "domain",
        "(data ->> 'city'::text)",
        "(data ->> 'state'::text)"
      ],
      "Actual Startup Time": 0.703,
      "Local Read Blocks": 0,
      "Startup Cost": 34.53,
      "Shared Dirtied Blocks": 0,
      "Shared Written Blocks": 0,
      "Local Written Blocks": 0,
      "Plan Rows": 788,
      "Actual Rows": 1417,
      "Total Cost": 2894.17
    },
    "Triggers": []
  }
]

【问题讨论】:

    标签: json postgresql jsonb postgresql-9.5


    【解决方案1】:

    当PostgreSQL认为它会更快时,它会选择位图索引扫描而不是普通的索引扫描。

    这通常是估计结果行数很高的情况。

    正常的索引扫描必须为找到的每个索引条目访问表,这会导致表上出现大量随机 I/O,并且可能需要多次处理同一块。

    位图索引扫描的工作原理是首先查找所有索引条目,按照它们在表中的物理位置顺序对它们进行排序,然后从表中扫描所需的块。这样效率更高,因为它会按顺序扫描表块。

    第二步,位图堆扫描,在EXPLAIN 输出中显示为它自己的节点,通常是更昂贵的一步。

    所以一切看起来都井井有条。

    您可以尝试将enable_bitmapscan 设置为off,看看PostgreSQL是否正确,由此产生的计划会更昂贵。

    【讨论】:

    • 我确实尝试了 enable_bitmapscan = false 并且你是正确的 postgres 只是进行了仅索引扫描并且没有改变结果(花费了大约相同的时间,可能稍微多一点)。令我困惑的是,该表只有约 150k 记录,运行此查询需要约 10 秒才能找到较大状态的记录,例如“NY”(约 20k 记录匹配)。这比我们对这种大小的表的任何其他查询都要长得多,这让我想知道如何进一步优化它......
    • @BryanP:嗯,您需要检索所有 20k 行还是只检索前几行?然后一个小的LIMIT 应该会有所帮助。
    • @BryanP 将持续时间与执行顺序扫描的SELECT * FROM mattermark_profiles 的持续时间进行比较。它可能会更大一些。为了减少这种情况,您需要更快的 I/O 子系统或更多内存(以及缓存表)。
    • 我已经打开了另一个问题,我想更深入地探讨一个相关主题:stackoverflow.com/questions/51432884/…
    • @ErwinBrandstetter 不幸的是,我需要完整的应用程序来满足我的要求
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 2017-06-23
    • 2020-02-14
    • 2018-05-05
    • 2011-06-28
    • 2011-09-29
    • 1970-01-01
    相关资源
    最近更新 更多