如何强制PostgreSQL使用我的索引?

如何强制PostgreSQL使用我的索引?,postgresql,indexing,postgresql-performance,Postgresql,Indexing,Postgresql Performance,事实: CREATE TABLE product ( product_id SERIAL, factory_key VARCHAR(60), relevant BOOLEAN ) Indexes: "product_factory_key_key" btree (factory_key); "product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = fal

事实:

CREATE TABLE product (
  product_id     SERIAL,
  factory_key    VARCHAR(60),
  relevant       BOOLEAN
)
Indexes:
"product_factory_key_key" btree (factory_key);
"product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
"product_relevant_key" btree (relevant);
  • 我们在
    产品
    表中有大约1亿条记录
  • 有少数工厂。例如,一家工厂可能有500万种产品
  • 工厂里有数百万把钥匙
  • 只有少数行与每个工厂无关。例如,有一家工厂有500万种产品,大约有100种不相关的产品
  • 但是,有数百万行不相关的行。例如,最常见的情况是一个工厂密钥,5行产品,可能有2行不相关的产品
  • 这是问题查询:

    CREATE TABLE product (
      product_id     SERIAL,
      factory_key    VARCHAR(60),
      relevant       BOOLEAN
    )
    Indexes:
    "product_factory_key_key" btree (factory_key);
    "product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
    "product_relevant_key" btree (relevant);
    
    解释和分析:

    SELECT * FROM product WHERE factory_key='some_product_key' AND relevant=false LIMIT 10;
    
    问题:

    CREATE TABLE product (
      product_id     SERIAL,
      factory_key    VARCHAR(60),
      relevant       BOOLEAN
    )
    Indexes:
    "product_factory_key_key" btree (factory_key);
    "product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
    "product_relevant_key" btree (relevant);
    
    这是有问题的,因为:

  • 我相信规划师选择使用seq scan是因为有太多的行与此工厂匹配。(约320万行与该工厂匹配,约为3%)

  • 但是,因为只有极少数行是不相关的。我正在寻找不相关的。序列扫描结果非常昂贵

  • 我已经创建了一个综合索引
    产品(工厂)键(相关)(键)
    ),但它没有利用该索引

    编辑:

    CREATE TABLE product (
      product_id     SERIAL,
      factory_key    VARCHAR(60),
      relevant       BOOLEAN
    )
    Indexes:
    "product_factory_key_key" btree (factory_key);
    "product_factory_key_relevant_key" btree (factory_key, relevant) WHERE relevant = false;
    "product_relevant_key" btree (relevant);
    
    我试图强迫postgres使用复合键:
    product\u-factory\u-key\u-related\u-key

    SET enable_seqscan=off

    尽管如此,它现在使用的是索引扫描。它实际上仍然比seqscan慢。(因此,我猜规划者进行顺序扫描是正确的)

    压倒一切的成本参数 您不能强迫PostgreSQL使用特定索引,也不能完全阻止它进行seqscan

    但是,如果可能的话,您可以通过将相关的
    enable\uu
    参数设置为
    off
    来告诉它避免执行某些扫描类型。它实际上是一个只用于调试的特性

    要进行测试,请尝试:

                                                                           QUERY PLAN
    --------------------------------------------------------------------------------------------------------------------------------------------------------
     Limit  (cost=0.57..34.03 rows=10 width=188) (actual time=8.088..469974.692 rows=10 loops=1)
       ->  Index Scan using product_factory_key_relevant_key on product  (cost=0.57..10689307.49 rows=3194776 width=188) (actual time=8.083..469974.655 rows=10 loops=1)
             Index Cond: (relevant = false)
             Filter: ((NOT relevant) AND ((product_key)::text = 'some_product_key'::text))
             Rows Removed by Filter: 2205295
     Total runtime: 469974.820 ms
    (6 rows)
    
    如果Pg可能会使用索引扫描(或其他方式),它会

    您可能还需要考虑:

    SET enable_seqscan = off;
    
    i、 e.告诉PostgreSQL,随机i/O只比顺序i/O稍微贵一点。在使用SSD的系统上,或者在大多数数据库缓存在RAM中的系统上,这通常是正确的。在这种情况下,更可能选择索引

    当然,如果系统的随机I/O实际上更昂贵,那么使用索引可能会更慢

    选择性,部分指数 你真正应该做的是听从别人给你的建议。按照选择性的顺序创建索引-如果
    相关的
    不太常见,则使用该索引。您甚至可以进一步创建部分索引:

    此索引仅包含
    relevant='f'
    的值。它只能用于计划者知道相关信息为假的查询。另一方面,它将是一个更小、更快的指数

    统计 您可能还有不准确的统计数据,这会导致PostgreSQL认为值频率与表的实际频率不同<代码>解释分析将有助于说明这一点

    你也可以
    分析我的表格
    ,以防统计数据过时;如果是这样,请增加自动真空运行的频率,因为它无法跟上

    如果统计数据是最新的,但规划人员仍在进行基于统计数据的错误估计,那么增加表的统计数据目标(请参阅手册)并重新分析可能会有帮助,如果这实际上是一个统计数据错误估计问题

    版本 较旧的PostgreSQL版本在成本估算、查询优化、统计、查询执行方法以及几乎所有其他方面都不太聪明

    如果您不在最新版本上,请升级

    例如,9.2的仅索引扫描将允许您创建部分索引

    (产品id、工厂密钥),其中(不相关)

    然后运行查询:

    CREATE INDEX idx_name_blah ON tbl_name_blah (factory_key) WHERE (NOT relevant);
    

    这应该只读取索引,根本没有堆访问权。

    Imho,查询中的问题是
    $1
    。我想这意味着你在使用事先准备好的声明,因为你在某个地方读到了这是最佳实践

    这实际上是不好的,因为PG无法预先知道您使用的标准的基数是高还是低。因此,它需要选择一个适合大多数情况的计划。如果有些值到处都是,以至于btree索引最左边的部分没有用处,那么就有很好的理由进行seq扫描

    相反,如果您运行查询而不首先准备查询,它将根据您传递的值进行计划,并为该特定值选择最佳计划。因此,考虑在不准备它的情况下运行同一个查询,如果它开始使用你现有的索引,你就处在这个病态的用例中。
    如果不是,克雷格说的话。。。特别是部分索引。

    好吧,试试看;您确实应该按照降序选择性对列进行排序,因此您应该做的第一件事是创建索引。如果你不能在几个小时内阻止写操作,你可能想同时创建索引。@CraigRinger,但这毫无意义。我已经创建了一个特定的复合索引,它专门匹配条件。为什么计划者仍然不使用我的索引。希望你能理解我的挫败感Hahaindex不是魔法。特别是,索引访问需要大量的随机I/O,因此如果随机I/O成本很高且索引不排除大多数行,那么它可能比seqscan慢得多。因此,PostgreSQL可能认为使用索引比seqscan更昂贵(即更慢)。如果这是错误的,可能是因为你的成本参数没有反映现实,尽管规划师肯定会错误估计成本。这也可能是由于不准确的统计数据-显示
    explain-analyze
    。顺便说一句,您显示的计划只是
    explain
    。您需要
    explain analyze
    来查看统计数据的问题。看见