【问题标题】:Why the SQL engine scan the whole table on a index column while using LIMIT?为什么 SQL 引擎在使用 LIMIT 时扫描索引列上的整个表?
【发布时间】:2018-08-01 00:54:51
【问题描述】:

我有一个类似的 SQL 查询:

select *
from customers
where customer_id > 0
order by customer_id asc
limit 500;

而customer_id 是customers 表的主键。当我执行此查询并检查执行计划时。我看到它扫描整个表:

SELECT (select)     500 23.0    0.0 Node Type = Limit;
Parallel Aware = false;
Startup Cost = 0.43;
Total Cost = 23.16;
Plan Rows = 500;
Plan Width = 237;

TRANSFORM (Limit)       500 23.0    0.0 Node Type = Limit;
Parallel Aware = false;
Startup Cost = 0.43;
Total Cost = 23.16;
Plan Rows = 500;
Plan Width = 237;

INDEX_SCAN (Index Scan)  table: customers; index: pk12; 3262339 148316.0    0.0 Node Type = Index Scan;
Parent Relationship = Outer;
Parallel Aware = false;
Scan Direction = Forward;
Index Name = pk12;
Relation Name = customers;
Alias = customers;
Startup Cost = 0.43;
Total Cost = 148316.36;
Plan Rows = 3222222;
Plan Width = 237;
Index Cond = (customer_id > '0'::numeric);

在我的直觉中,主键会创建索引,sql 引擎可以定位下限并从索引的叶节点(b+ 树)中获取 500 个项目。这是我能想到的最快的执行计划。为什么sql引擎会扫描整个DB表,先排序,只得到500条?

PS:PostgreSQL。

【问题讨论】:

  • 注意到索引扫描中的'0'::numeric,id 列是数字类型吗?这也可能是一个统计问题。是否禁用了 autovacuum?尝试ANALYZE <table_name>; 收集有关该特定表的统计信息。

标签: sql postgresql primary-key sql-execution-plan


【解决方案1】:

您看到的只是 完整 索引扫描的估计成本和行数,但正如您所见,PostgreSQL 知道它不必扫描完整索引,否则查询的估计总成本 (23.16) 不能低于索引扫描的估计成本 (148316.36)。

使用EXPLAIN (ANAYLZE) 查看实际发生了什么:

CREATE TABLE test (id) AS SELECT * FROM generate_series(1, 100000);
ALTER TABLE test ADD PRIMARY KEY (id);
VACUUM (ANALYZE) test;

我已经从执行计划中删除了不相关的行:

EXPLAIN (ANALYZE, FORMAT json)
   SELECT id FROM test WHERE id > 0 ORDER BY id LIMIT 500;

                  QUERY PLAN                   
-----------------------------------------------
 [                                            +
   {                                          +
     "Plan": {                                +
       "Node Type": "Limit",                  +
       "Startup Cost": 0.29,                  +
       "Total Cost": 14.56,                   +
       "Plan Rows": 500,                      +
       "Actual Rows": 500,                    +
       "Plans": [                             +
         {                                    +
           "Node Type": "Index Only Scan",    +
           "Index Name": "test_pkey",         +
           "Plan Rows": 100000,               +
           "Actual Rows": 500,                +
           "Index Cond": "(id > 0)",          +
         }                                    +
       ]                                      +
     },                                       +
   }                                          +
 ]
(1 row)

所以在找到前 500 行后停止索引扫描。

【讨论】:

    猜你喜欢
    • 2015-01-25
    • 1970-01-01
    • 2012-01-31
    • 2011-04-27
    • 1970-01-01
    • 2015-01-27
    • 2021-09-04
    • 1970-01-01
    • 2015-04-21
    相关资源
    最近更新 更多